无密码时代来临!Spring Security 6.4魔法链接登录全解析

Spring Security 6.4 一次性令牌登录详解

作者:lengleng
发布日期:2025-01-31

1. 概述

Spring Security 6.4 引入了一项新的安全特性 —— 一次性令牌登录(One-Time Token Login)。这种登录方式允许用户通过邮件接收一个魔法链接(Magic Link)来完成身份验证,无需传统的用户名和密码组合。这种方式不仅提升了用户体验,还增强了系统的安全性。

比如笔者上篇文章提到的 mintlify 文档工具,默认提供的就是这种方式。
mintlify 邮件登录

1.1 登录流程图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sequenceDiagram
participant User
participant System
participant EmailService

User->>System: 提交邮箱
System->>EmailService: 生成并发送魔法链接
EmailService->>User: 发送含链接邮件
User->>System: 点击魔法链接
alt 验证成功
System->>User: 登录成功
else 验证失败
System->>User: 显示错误提示
end

1.2 技术架构图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
graph TB
A[用户界面] --> B[Spring Security过滤器链]
B --> C[OneTimeTokenAuthenticationFilter]
C --> D[OneTimeTokenRepository]
C --> E[UserDetailsService]
D --> F[(令牌存储)]
E --> G[(用户数据库)]

style A fill:#f9f,stroke:#333,stroke-width:2px
style B fill:#bbf,stroke:#333,stroke-width:2px
style C fill:#dfd,stroke:#333,stroke-width:2px
style D fill:#dfd,stroke:#333,stroke-width:2px
style E fill:#dfd,stroke:#333,stroke-width:2px
style F fill:#fdd,stroke:#333,stroke-width:2px
style G fill:#fdd,stroke:#333,stroke-width:2px

2. 核心概念

2.1 一次性令牌

  • 一次性令牌是一个临时的、只能使用一次的认证凭证
  • 通常以 URL 参数或者令牌字符串的形式发送给用户
  • 有效期通常为 5-15 分钟

2.2 魔法链接

  • 包含一次性令牌的 URL 链接
  • 通过邮件发送给用户
  • 点击即可完成身份验证

3. 实现原理

3.1 认证流程

  1. 令牌生成:采用密码学安全随机数生成器生成 128 位随机令牌
  2. 令牌存储:支持多种存储方式(内存、Redis、数据库)
  3. 邮件发送:异步发送包含令牌的魔法链接
  4. 令牌验证:过滤器链中校验令牌有效性
  5. 会话建立:成功验证后创建安全上下文

3.2 核心组件

组件名称 职责描述
OneTimeTokenFilter 拦截令牌验证请求
OneTimeTokenManager 管理令牌生命周期
TokenExpirationStrategy 定义令牌过期策略
TokenVerificationHandler 处理令牌验证逻辑

4. 实现步骤

4.1 添加依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

4.2 配置一次性令牌服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/login/**").permitAll()
.anyRequest().authenticated()
)
.oneTimeTokenLogin(oneTime -> oneTime
.tokenRepository(tokenRepository())
.tokenValidityDuration(Duration.ofMinutes(5))
);
return http.build();
}

@Bean
public OneTimeTokenRepository tokenRepository() {
return new InMemoryOneTimeTokenRepository();
}
}

4.3 实现令牌生成和发送

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Service
public class OneTimeTokenService {

@Autowired
private OneTimeTokenRepository tokenRepository;

@Autowired
private EmailService emailService;

public void sendLoginToken(String email) {
String token = generateToken();
tokenRepository.save(new OneTimeToken(token, email));

String loginLink = "https://your-domain.com/login/verify?token=" + token;
emailService.sendLoginLink(email, loginLink);
}

private String generateToken() {
return UUID.randomUUID().toString();
}
}

5. 总结

Spring Security 6.4 的一次性令牌登录功能为应用提供了一种现代、安全的身份验证方式。通过合理的配置和实现,可以在保证安全性的同时提供良好的用户体验。在实际应用中,需要根据具体场景调整配置参数,并结合其他安全措施,构建完整的安全解决方案。