自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

安全無憂!在 Spring Boot 3.3 中輕松實(shí)現(xiàn) TOTP 雙因素認(rèn)證

開發(fā) 架構(gòu)
本 TOTP 注冊系統(tǒng)通過結(jié)合現(xiàn)代前端技術(shù)與穩(wěn)健的后端架構(gòu),成功實(shí)現(xiàn)了高效、安全的用戶注冊流程。系統(tǒng)的設(shè)計(jì)充分考慮了安全性與用戶體驗(yàn),確保用戶在注冊過程中能夠快速獲取所需信息,而不影響安全標(biāo)準(zhǔn)。

隨著互聯(lián)網(wǎng)的快速發(fā)展,網(wǎng)絡(luò)安全問題日益嚴(yán)峻。傳統(tǒng)的用戶名和密碼認(rèn)證方式已經(jīng)無法滿足現(xiàn)代應(yīng)用對(duì)安全性的要求,因此雙因素認(rèn)證(2FA)成為了提升安全性的有效手段。雙因素認(rèn)證不僅要求用戶輸入密碼,還需通過第二種方式進(jìn)行身份驗(yàn)證,例如手機(jī)生成的動(dòng)態(tài)驗(yàn)證碼。

時(shí)間同步一次性密碼(TOTP)是一種基于時(shí)間的雙因素認(rèn)證方式,它通過算法生成短期有效的驗(yàn)證碼。用戶在登錄時(shí),需要輸入從手機(jī)應(yīng)用(如 Google Authenticator)獲取的 TOTP 代碼。由于 TOTP 代碼每 30 秒更新一次,即使攻擊者獲取了用戶的密碼,沒有有效的 TOTP 代碼,也無法登錄賬戶。

本文將詳細(xì)介紹如何在 Spring Boot 3.3 中實(shí)現(xiàn)基于 TOTP 的雙因素認(rèn)證,涵蓋從依賴配置、服務(wù)實(shí)現(xiàn)到前端展示的完整過程。

什么是 TOTP?

TOTP(Time-based One-Time Password)是一種用于雙因素認(rèn)證的算法,它基于當(dāng)前時(shí)間和用戶的共享秘密(密鑰)生成一次性密碼。TOTP 主要遵循以下步驟:

  1. 密鑰生成:在用戶賬戶創(chuàng)建時(shí)生成一個(gè)共享密鑰,并與用戶的身份綁定。該密鑰通常以 Base32 編碼格式存儲(chǔ)。
  2. 時(shí)間戳使用:TOTP 使用當(dāng)前時(shí)間戳,將時(shí)間分成固定的時(shí)間段(例如,30 秒)。每個(gè)時(shí)間段生成一個(gè)唯一的 TOTP 密碼。
  3. 動(dòng)態(tài)密碼生成:通過將共享密鑰和當(dāng)前時(shí)間戳作為輸入,使用 HMAC-SHA1 或類似算法生成一次性密碼。
  4. 驗(yàn)證過程:在用戶登錄時(shí),服務(wù)器端也使用相同的共享密鑰和當(dāng)前時(shí)間戳生成 TOTP 密碼,并與用戶輸入的密碼進(jìn)行比對(duì)。

這種機(jī)制保證了每次登錄時(shí)生成的密碼都是唯一且短暫的,極大地提升了賬戶的安全性。

運(yùn)行效果:

圖片

若想獲取項(xiàng)目完整代碼以及其他文章的項(xiàng)目源碼,且在代碼編寫時(shí)遇到問題需要咨詢交流,歡迎加入下方的知識(shí)星球。

項(xiàng)目依賴配置

首先,在 pom.xml 中添加所需的依賴:

<?xml versinotallow="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.4</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.icoderoad</groupId>
	<artifactId>totp-authentication</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>totp-authentication</name>
	<description>Demo project for Spring Boot</description>
	
	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>
        <dependency>
            <groupId>dev.samstevens.totp</groupId>
            <artifactId>totp</artifactId>
            <version>1.7.1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

配置文件

接下來,我們在 application.yml 中配置所需的屬性:

server:
  port: 8080
  
totp:
  time-step: 30
  length: 6

生成和配置密鑰

生成密鑰服務(wù)類

package com.icoderoad.totp.service;

import org.springframework.stereotype.Service;

import dev.samstevens.totp.secret.DefaultSecretGenerator;
import dev.samstevens.totp.secret.SecretGenerator;

@Service
public class SecretService {
    private final SecretGenerator secretGenerator = new DefaultSecretGenerator();

    public String generateSecret() {
        // 生成安全的隨機(jī) base32 編碼字符串
        return secretGenerator.generate();
    }
}

屬性配置類

package com.icoderoad.totp.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import lombok.Data;

@Data
@Component
@ConfigurationProperties(prefix = "totp")
public class TotpProperties {
    private int timeStep = 30; // 默認(rèn)值為 30 秒
    private int length = 6;     // 默認(rèn)值為 6 位
}

配置 TOTP 生成器

package com.icoderoad.totp.service;

import com.icoderoad.totp.config.TotpProperties;
import dev.samstevens.totp.time.TimeProvider;
import dev.samstevens.totp.time.SystemTimeProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class TotpConfiguration {

    private final TotpProperties totpProperties;

    public TotpConfiguration(TotpProperties totpProperties) {
        this.totpProperties = totpProperties;
    }

    @Bean
    public TimeProvider timeProvider() {
        return new SystemTimeProvider(); // 使用系統(tǒng)時(shí)間提供者
    }

    @Bean
    public int getTotpLength() {
        return totpProperties.getLength();
    }

    public int getTimeStepInSeconds() {
        return totpProperties.getTimeStep();
    }
}

TOTP 生成和驗(yàn)證

TOTP 生成服務(wù)

package com.icoderoad.totp.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.icoderoad.totp.config.TotpProperties;

import dev.samstevens.totp.code.CodeGenerator;
import dev.samstevens.totp.code.DefaultCodeGenerator;
import dev.samstevens.totp.exceptions.CodeGenerationException;
import dev.samstevens.totp.time.SystemTimeProvider;
import dev.samstevens.totp.time.TimeProvider;

@Service
public class TotpGeneratorService {
	
	@Autowired
	private TotpProperties totpProperties;
	
    private final CodeGenerator codeGenerator;
    private final TimeProvider timeProvider;

    @Autowired
    public TotpGeneratorService(TimeProvider timeProvider) {
        this.timeProvider = timeProvider != null ? timeProvider : new SystemTimeProvider();
        this.codeGenerator = new DefaultCodeGenerator(); // 使用默認(rèn)構(gòu)造函數(shù)
    }

    public String generateTotp(String secret) {
        long counter = getCounter();
        try {
			return codeGenerator.generate(secret, counter);
		} catch (CodeGenerationException e) {
			return "";
		}
    }

    private long getCounter() {
        long timeStep = totpProperties.getTimeStep();
        return timeProvider.getTime() / timeStep;
    }
}

TOTP 驗(yàn)證服務(wù)

package com.icoderoad.totp.service;

import dev.samstevens.totp.code.CodeVerifier;
import dev.samstevens.totp.code.DefaultCodeVerifier;
import dev.samstevens.totp.code.DefaultCodeGenerator;
import dev.samstevens.totp.exceptions.CodeGenerationException;
import dev.samstevens.totp.time.TimeProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.icoderoad.totp.config.TotpProperties;

@Service
public class TotpVerificationService {
    private final DefaultCodeVerifier codeVerifier;
    private final TimeProvider timeProvider;
    
	private final TotpProperties totpProperties;

    @Autowired
    public TotpVerificationService(TimeProvider timeProvider, TotpProperties totpProperties) {
    	this.totpProperties = totpProperties;
        this.timeProvider = timeProvider;
        this.codeVerifier = new DefaultCodeVerifier(new DefaultCodeGenerator(), timeProvider);
        this.codeVerifier.setTimePeriod(this.totpProperties.getTimeStep()); // 從配置文件中讀取或設(shè)置
        this.codeVerifier.setAllowedTimePeriodDiscrepancy( this.totpProperties.getLength() ); // 可配置的時(shí)間誤差
    }

    public boolean verifyTotp(String secret, String code) {
        return codeVerifier.isValidCode(secret, code);
    }
}

用戶注冊與 TOTP 集成

UserService 類

package com.icoderoad.totp.service;

import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class UserService {
    // 使用 HashMap 模擬用戶存儲(chǔ)(可以替換為數(shù)據(jù)庫實(shí)現(xiàn))
    private final Map<String, String> userSecrets = new HashMap<>();

    /**
     * 保存用戶的 TOTP 秘密
     *
     * @param username 用戶名
     * @param secret   用戶的 TOTP 秘密
     */
    public void saveUserSecret(String username, String secret) {
        userSecrets.put(username, secret);
    }

    /**
     * 根據(jù)用戶名獲取 TOTP 秘密
     *
     * @param username 用戶名
     * @return TOTP 秘密
     */
    public String findSecretByUsername(String username) {
        return userSecrets.get(username);
    }

    // 可以添加更多與用戶相關(guān)的方法,如驗(yàn)證用戶、獲取用戶信息等
}

QRCodeGenerator類

package com.icoderoad.totp.generator;

import org.springframework.stereotype.Component;

import dev.samstevens.totp.exceptions.QrGenerationException;
import dev.samstevens.totp.qr.QrData;
import dev.samstevens.totp.qr.ZxingPngQrGenerator;

@Component
public class QRCodeGenerator {

    private final ZxingPngQrGenerator qrGenerator;

    public QRCodeGenerator() {
        this.qrGenerator = new ZxingPngQrGenerator();
    }

    public byte[] generate(String secret, String username, String issuer, int digits, int period) throws QrGenerationException {
        // 創(chuàng)建 QR 數(shù)據(jù)
        QrData qrData = new QrData.Builder()
                .label(username)
                .secret(secret)
                .issuer(issuer)
                .digits(digits)
                .period(period)
                .build();

        // 生成 QR 代碼
        return qrGenerator.generate(qrData);
    }

    public String generateQrCodeUrl(String secret, String username, String issuer, int digits, int period) throws QrGenerationException {
        byte[] qrCodeBytes = generate(secret, username, issuer, digits, period);
        
        // 將生成的 QR 代碼轉(zhuǎn)換為 Base64 URL,便于在 HTML 中顯示
        return "data:image/png;base64," + java.util.Base64.getEncoder().encodeToString(qrCodeBytes);
    }
}

RegistrationResponse類

package com.icoderoad.totp.controller;

public class RegistrationResponse {
    private final String secret;
    private final String qrCodeUrl;

    public RegistrationResponse(String secret, String qrCodeUrl) {
        this.secret = secret;
        this.qrCodeUrl = qrCodeUrl;
    }

    public String getSecret() {
        return secret;
    }

    public String getQrCodeUrl() {
        return qrCodeUrl;
    }
}

注冊控制器

package com.icoderoad.totp.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.icoderoad.totp.dto.UserDto;
import com.icoderoad.totp.generator.QRCodeGenerator;
import com.icoderoad.totp.service.SecretService;
import com.icoderoad.totp.service.UserService;

import dev.samstevens.totp.exceptions.QrGenerationException;

@RestController
public class RegistrationController {

總結(jié)

本 TOTP 注冊系統(tǒng)通過結(jié)合現(xiàn)代前端技術(shù)與穩(wěn)健的后端架構(gòu),成功實(shí)現(xiàn)了高效、安全的用戶注冊流程。系統(tǒng)的設(shè)計(jì)充分考慮了安全性與用戶體驗(yàn),確保用戶在注冊過程中能夠快速獲取所需信息,而不影響安全標(biāo)準(zhǔn)。總體而言,該系統(tǒng)不僅提升了用戶賬戶的安全性,也通過友好的操作流程增強(qiáng)了用戶的信任感,為未來的擴(kuò)展和優(yōu)化打下了堅(jiān)實(shí)基礎(chǔ)。

責(zé)任編輯:武曉燕 來源: 路條編程
相關(guān)推薦

2020-05-25 07:00:00

雙因素認(rèn)證身份認(rèn)證密碼

2022-08-01 00:08:03

雙因素認(rèn)證2FA

2024-09-05 09:35:58

CGLIBSpring動(dòng)態(tài)代理

2022-12-19 16:17:21

CDN

2022-11-10 19:49:29

2011-10-31 14:57:37

2013-06-19 11:26:39

2011-08-15 09:31:55

2021-12-28 11:13:05

安全認(rèn)證 Spring Boot

2013-05-23 18:03:25

2020-09-30 11:22:16

帳戶安全

2011-03-16 16:00:06

內(nèi)網(wǎng)安全

2021-06-10 10:26:37

網(wǎng)絡(luò)安全/智慧醫(yī)療

2023-04-25 10:59:56

2024-10-07 08:18:05

SpringBOM管理

2018-08-08 05:03:31

2020-12-24 17:12:29

賬戶安全雙因素認(rèn)證Facebook

2024-10-11 11:46:40

2012-07-11 17:33:47

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)