告别密码!Spring Boot 3.3 带你玩转 TOTP 双重认证,安全感 UP!UP!
在这个信息时代,网络安全就像咱们的贴身保镖,一刻都不能放松警惕。传统的“账号密码”组合,早就成了黑客蜀黍眼中的小甜点,一不小心就被“一锅端”了。为了守护咱们的数字资产,双因素认证(2FA)必须安排上!它就像给账号加了双重保险,除了密码,还得验证手机上的动态验证码,安全系数直接翻倍!
时间同步一次性密码(TOTP)就是 2FA 阵营里的明星选手。它能根据时间和密钥,像变魔术一样生成只有 30 秒有效期的验证码。就算黑客拿到了你的密码,没有这串“新鲜出炉”的 TOTP 码,也只能望洋兴叹,进不了你的账户!
今天,咱们就来手把手教你如何在 Spring Boot 3.3 项目里,搭建一套基于 TOTP 的双因素认证系统。从环境配置到代码实现,再到前端页面展示,保证让你学得明白,用得溜!
啥是 TOTP?听起来很高大上!
TOTP (Time-based One-Time Password),说白了就是一种“限时秒杀”型密码。它基于时间和用户的“暗号”(共享密钥)来生成,核心步骤如下:
-
暗号生成:用户注册的时候,系统会生成一个独一无二的“暗号”,这个“暗号”通常会用 Base32 编码一下,藏起来。
-
时间戳登场:TOTP 会把时间切成 N 多个小段(比如 30 秒一段),每个时间段都对应一个唯一的密码。
-
密码变变变:把“暗号”和当前时间戳扔进一个神秘的算法(比如 HMAC-SHA1),就能生成一个一次性密码。
-
验证身份:用户登录时,服务器也用同样的“暗号”和时间戳,生成一个密码,然后和用户输入的密码比对,“暗号”对上了,时间也对上了,才能放行!
这种机制保证了每次登录的密码都是新鲜的、唯一的,就像给你的账户上了 N 把锁,安全感直接拉满!
效果展示:
想要获取完整的项目代码,以及更多干货满满的文章源码,欢迎加入我们的知识星球,一起学习,共同进步! 如果你在代码实战中遇到了难题,也可以来星球提问,我们一起帮你解决!
项目依赖,安排!
首先,在你的 pom.xml
文件里,添加以下依赖:
<?xml version="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
里,配置一些 TOTP 相关的属性:
server:
port: 8080
totp:
time-step: 30 # 验证码的有效期,单位:秒
length: 6 # 验证码的长度
生成和配置密钥,安排!
密钥生成服务类
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() {
// 生成安全的随机 base32 编码字符串
return secretGenerator.generate();
}
}
这个服务类就像一个“暗号制造机”,专门用来生成安全的随机 Base32 编码字符串,也就是咱们的“暗号”。
属性配置类
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; // 默认值为 30 秒
private int length = 6; // 默认值为 6 位
}
这个配置类,负责读取 application.yml
里的 totp
属性,比如验证码的有效期和长度。
配置 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(); // 使用系统时间提供者
}
@Bean
public int getTotpLength() {
return totpProperties.getLength();
}
public int getTimeStepInSeconds() {
return totpProperties.getTimeStep();
}
}
这个配置类,主要用来配置 TimeProvider
(时间提供者)和获取验证码长度、有效期等属性。
TOTP 生成和验证,安排!
TOTP 生成服务
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(); // 使用默认构造函数
}
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 验证码。
TOTP 验证服务
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()); // 从配置文件中读取或设置
this.codeVerifier.setAllowedTimePeriodDiscrepancy( this.totpProperties.getLength() ); // 可配置的时间误差
}
public boolean verifyTotp(String secret, String code) {
return codeVerifier.isValidCode(secret, code);
}
}
这个服务类,负责验证用户输入的 TOTP 验证码是否正确。
用户注册与 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 模拟用户存储(可以替换为数据库实现)
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);
}
/**
* 根据用户名获取 TOTP 秘密
*
* @param username 用户名
* @return TOTP 秘密
*/
public String findSecretByUsername(String username) {
return userSecrets.get(username);
}
// 可以添加更多与用户相关的方法,如验证用户、获取用户信息等
}
这个服务类,负责存储用户的“暗号”,这里用 HashMap
模拟,实际项目中要换成数据库哦!
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 {
// 创建 QR 数据
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 代码转换为 Base64 URL,便于在 HTML 中显示
return "data:image/png;base64," + java.util.Base64.getEncoder().encodeToString(qrCodeBytes);
}
}
这个类,负责生成包含“暗号”的二维码,方便用户用 Google Authenticator 之类的 APP 扫描。
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;
}
}
这个类,用来封装注册接口的返回值,包含“暗号”和二维码的 URL。
注册控制器
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 {
private final SecretService secretService;
private final UserService userService;
private final QRCodeGenerator qrCodeGenerator;
@Autowired
public RegistrationController(SecretService secretService, UserService userService, QRCodeGenerator qrCodeGenerator) {
this.secretService = secretService;
this.userService = userService;
this.qrCodeGenerator = qrCodeGenerator;
}
@PostMapping("/register")
public ResponseEntity<RegistrationResponse> registerUser(@RequestBody UserDto user) {
String secret = secretService.generateSecret();
// 将密钥安全存储在用户账户下
userService.saveUserSecret(user.getUsername(), secret);
// 生成二维码供用户扫描
String qrCodeUrl = generateQrCodeUrl(secret, user.getUsername());
return ResponseEntity.ok(new RegistrationResponse(secret, qrCodeUrl));
}
private String generateQrCodeUrl(String secret, String username) {
try {
return qrCodeGenerator.generateQrCodeUrl(secret, username, "YourIssuer", 6, 30);
} catch (QrGenerationException e) {
// 处理 QR 代码生成异常
throw new RuntimeException("Failed to generate QR code.", e);
}
}
}
这个控制器,负责处理用户注册的请求,生成“暗号”和二维码,并返回给前端。
前端页面实现,安排!
登录页面 (index.html)
首先,在 src/main/resources/templates/
目录下创建一个名为 index.html
的文件。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TOTP 注册</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<h1 class="mt-5">注册 TOTP</h1>
<form id="registrationForm">
<div class="form-group">
<label for="username">用户名</label>
<input type="text" class="form-control" id="username" placeholder="输入用户名" required>
</div>
<button type="submit" class="btn btn-primary">注册</button>
</form>
<div id="result" class="mt-4" style="display: none;">
<h2>注册成功!</h2>
<p>您的密钥: <span id="secret"></span></p>
<h3>扫描二维码:</h3>
<img id="qrCode" alt="二维码" />
</div>
</div>
<script>
document.getElementById('registrationForm').addEventListener('submit', function (event) {
event.preventDefault(); // 阻止表单提交
const username = document.getElementById('username').value;
fetch('/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username }),
})
.then(response => {
if (!response.ok) {
throw new Error('网络错误');
}
return response.json();
})
.then(data => {
// 更新页面内容
document.getElementById('secret').textContent = data.secret;
document.getElementById('qrCode').src = data.qrCodeUrl;
document.getElementById('result').style.display = 'block'; // 显示结果
})
.catch(error => {
console.error('发生错误:', error);
alert('注册失败,请重试!');
});
});
</script>
</body>
</html>
这个页面,主要用来让用户输入用户名,然后调用注册接口,展示“暗号”和二维码。
总结
这套 TOTP 注册系统,结合了现代前端技术和稳健的后端架构,实现了高效、安全的用户注册流程。在设计的时候,充分考虑了安全性和用户体验,确保用户在注册过程中能够快速获取所需信息,又不影响安全标准。 总的来说,这套系统不仅提高了用户账户的安全性,还通过友好的操作流程,增强了用户的信任感,为未来的扩展和优化打下了坚实的基础!
```
黑客/网络安全学习包
资料目录
-
成长路线图&学习规划
-
配套视频教程
-
SRC&黑客文籍
-
护网行动资料
-
黑客必读书单
-
面试题合集
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
*************************************优快云大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享*************************************
1.成长路线图&学习规划
要学习一门新的技术,作为新手一定要先学习成长路线图,方向不对,努力白费。
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图&学习规划。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
*************************************优快云大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享*************************************
2.视频教程
很多朋友都不喜欢晦涩的文字,我也为大家准备了视频教程,其中一共有21个章节,每个章节都是当前板块的精华浓缩。
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
*************************************优快云大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享*************************************
3.SRC&黑客文籍
大家最喜欢也是最关心的SRC技术文籍&黑客技术也有收录
SRC技术文籍:
黑客资料由于是敏感资源,这里不能直接展示哦!
4.护网行动资料
其中关于HW护网行动,也准备了对应的资料,这些内容可相当于比赛的金手指!
5.黑客必读书单
**
**
6.面试题合集
当你自学到这里,你就要开始思考找工作的事情了,而工作绕不开的就是真题和面试题。
更多内容为防止和谐,可以扫描获取~
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
*************************************优快云大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享*********************************