在当今的数字环境中,提供安全且用户友好的身份验证方法至关重要。一种越来越流行的方法是一次性令牌 (OTT) 身份验证,通常以通过电子邮件发送的“魔术链接”的形式实现。 Spring Security 6.4.0 为 OTT 身份验证提供强大的内置支持,包括即用型实现。在这份综合指南中,我们将探讨如何使用内置解决方案和自定义实现来实现安全的 OTT 身份验证。
了解一次性令牌与一次性密码
在深入实施之前,了解一次性令牌 (OTT) 与一次性密码 (OTP) 的不同非常重要。虽然 OTP 系统通常需要初始设置并依赖外部工具来生成密码,但从用户角度来看,OTT 系统更简单 - 它们会收到可用于身份验证的唯一令牌(通常通过电子邮件)。
主要区别包括:
- OTT 不需要初始用户设置
- 令牌由您的应用程序生成和交付
- 每个令牌通常只能使用一次,并在设定时间后过期
可用的内置实现
Spring Security 提供了 OneTimeTokenService 的两种实现:
-
InMemoryOneTimeTokenService:
- 将令牌存储在内存中
- 非常适合开发和测试
- 不适合生产或集群环境
- 应用程序重新启动时令牌会丢失
-
JdbcOneTimeTokenService:
- 将令牌存储在数据库中
- 适合生产使用
- 在集群环境中工作
- 具有自动清理功能的持久存储
使用 InMemoryOneTimeTokenService
以下是如何实现更简单的内存解决方案:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .requestMatchers("/login/**", "/ott/**").permitAll() .anyRequest().authenticated() ) .formLogin(Customizer.withDefaults()) .oneTimeTokenLogin(Customizer.withDefaults()); // Uses InMemoryOneTimeTokenService by default return http.build(); } }
使用 JdbcOneTimeTokenService
对于生产环境,使用 JDBC 实现:
@Configuration @EnableWebSecurity public class SecurityConfig { @Autowired JdbcTemplate jdbcTemplate; @Bean public OneTimeTokenService oneTimeTokenService() { return new JdbcOneTimeTokenService(jdbcTemplate); } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .requestMatchers("/login/**", "/ott/**").permitAll() .anyRequest().authenticated() ) .formLogin(Customizer.withDefaults()) .oneTimeTokenLogin(Customizer.withDefaults()); return http.build(); } }
JdbcOneTimeTokenService 所需的表结构:
CREATE TABLE one_time_tokens ( token_value VARCHAR(255) PRIMARY KEY, username VARCHAR(255) NOT NULL, issued_at TIMESTAMP NOT NULL, expires_at TIMESTAMP NOT NULL, used BOOLEAN NOT NULL );
定制实施
为了更好地控制令牌生成和验证过程,您可以创建自定义实现:
1. Token实体和存储库
@Entity @Table(name = "one_time_tokens") public class OneTimeToken { @Id @GeneratedValue private Long id; private String tokenValue; private String username; private LocalDateTime createdAt; private LocalDateTime expiresAt; private boolean used; // Getters and setters omitted for brevity } @Repository public interface OneTimeTokenRepository extends JpaRepository<onetimetoken long> { Optional<onetimetoken> findByTokenValueAndUsedFalse(String tokenValue); void deleteByExpiresAtBefore(LocalDateTime dateTime); } </onetimetoken></onetimetoken>
2. 自定义令牌服务
@Service @Transactional public class PersistentOneTimeTokenService implements OneTimeTokenService { private static final int TOKEN_VALIDITY_MINUTES = 15; @Autowired private OneTimeTokenRepository tokenRepository; @Override public OneTimeToken generate(GenerateOneTimeTokenRequest request) { String tokenValue = UUID.randomUUID().toString(); LocalDateTime now = LocalDateTime.now(); OneTimeToken token = new OneTimeToken(); token.setTokenValue(tokenValue); token.setUsername(request.getUsername()); token.setCreatedAt(now); token.setExpiresAt(now.plusMinutes(TOKEN_VALIDITY_MINUTES)); token.setUsed(false); return return new DefaultOneTimeToken(token.getTokenValue(),token.getUsername(), Instant.now()); } @Override public Authentication consume(ConsumeOneTimeTokenRequest request) { OneTimeToken token = tokenRepository.findByTokenValueAndUsedFalse(request.getTokenValue()) .orElseThrow(() -> new BadCredentialsException("Invalid or expired token")); if (token.getExpiresAt().isBefore(LocalDateTime.now())) { throw new BadCredentialsException("Token has expired"); } token.setUsed(true); tokenRepository.save(token); UserDetails userDetails = loadUserByUsername(token.getUsername()); return new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); } }
实施令牌交付
Spring Security 不处理令牌传递,因此您需要实现它:
@Component public class EmailMagicLinkHandler implements OneTimeTokenGenerationSuccessHandler { @Autowired private JavaMailSender mailSender; private final OneTimeTokenGenerationSuccessHandler redirectHandler = new RedirectOneTimeTokenGenerationSuccessHandler("/ott/check-email"); @Override public void handle(HttpServletRequest request, HttpServletResponse response, OneTimeToken token) throws IOException, ServletException { String magicLink = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request)) .replacePath(request.getContextPath()) .replaceQuery(null) .fragment(null) .path("/login/ott") .queryParam("token", token.getTokenValue()) .toUriString(); SimpleMailMessage message = new SimpleMailMessage(); message.setTo(getUserEmail(token.getUsername())); message.setSubject("Your Sign-in Link"); message.setText("Click here to sign in: " + magicLink); mailSender.send(message); redirectHandler.handle(request, response, token); } }
自定义 URL 和页面
Spring Security 提供了多种自定义选项:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .requestMatchers("/login/**", "/ott/**").permitAll() .anyRequest().authenticated() ) .formLogin(Customizer.withDefaults()) .oneTimeTokenLogin(Customizer.withDefaults()); // Uses InMemoryOneTimeTokenService by default return http.build(); } }
生产注意事项
在生产中部署 OTT 身份验证时:
-
选择正确的实施
- 使用 JdbcOneTimeTokenService 或自定义实现进行生产
- InMemoryOneTimeTokenService 只能用于开发/测试
配置电子邮件传送
@Configuration @EnableWebSecurity public class SecurityConfig { @Autowired JdbcTemplate jdbcTemplate; @Bean public OneTimeTokenService oneTimeTokenService() { return new JdbcOneTimeTokenService(jdbcTemplate); } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .requestMatchers("/login/**", "/ott/**").permitAll() .anyRequest().authenticated() ) .formLogin(Customizer.withDefaults()) .oneTimeTokenLogin(Customizer.withDefaults()); return http.build(); } }
-
安全最佳实践
- 设置适当的令牌过期时间(建议 15 分钟)
- 对代币生成实施速率限制
- 对所有端点使用 HTTPS
- 监控失败的身份验证尝试
- 确保代币是一次性的,使用后立即失效
- 实现过期令牌的自动清理
- 使用安全随机令牌生成来防止猜测
它是如何运作的
- 用户通过提交电子邮件地址请求令牌
- 系统生成安全令牌并通过电子邮件发送魔法链接
- 用户点击链接并被重定向到令牌提交页面
- 系统验证令牌并验证用户(如果有效)
结论
Spring Security 的 OTT 支持为实现安全、用户友好的身份验证提供了坚实的基础。无论您选择内置实现还是创建自定义解决方案,您都可以为用户提供无密码登录选项,同时保持高安全标准。
实施 OTT 身份验证时,请记住:
- 选择适合您环境的实施
- 实施安全令牌交付
- 配置正确的令牌过期时间
- 遵循安全最佳实践
- 创建用户友好的错误处理和重定向
- 实施适当的电子邮件模板以获得专业外观
通过遵循本指南,您可以实现安全且用户友好的 OTT 身份验证系统,满足您的应用程序的需求,同时利用 Spring Security 强大的安全功能。
参考:https://docs.spring.io/spring-security/reference/servlet/authentication/onetimetoken.html
以上是使用 Spring Security 实现一次性令牌身份验证的详细内容。更多信息请关注PHP中文网其他相关文章!

本文分析了2025年的前四个JavaScript框架(React,Angular,Vue,Susve),比较了它们的性能,可伸缩性和未来前景。 尽管由于强大的社区和生态系统,所有这些都保持占主导地位,但它们的相对人口

本文讨论了使用咖啡因和Guava缓存在Java中实施多层缓存以提高应用程序性能。它涵盖设置,集成和绩效优势,以及配置和驱逐政策管理最佳PRA

Node.js 20通过V8发动机改进可显着提高性能,特别是更快的垃圾收集和I/O。 新功能包括更好的WebSembly支持和精制的调试工具,提高开发人员的生产率和应用速度。

Java的类上载涉及使用带有引导,扩展程序和应用程序类负载器的分层系统加载,链接和初始化类。父代授权模型确保首先加载核心类别,从而影响自定义类LOA

本文介绍了SnakeyAml中的CVE-2022-1471漏洞,这是一个允许远程代码执行的关键缺陷。 它详细介绍了如何升级春季启动应用程序到Snakeyaml 1.33或更高版本的降低风险,强调了依赖性更新

本文使用lambda表达式,流API,方法参考和可选探索将功能编程集成到Java中。 它突出显示了通过简洁性和不变性改善代码可读性和可维护性等好处

本文讨论了使用Maven和Gradle进行Java项目管理,构建自动化和依赖性解决方案,以比较其方法和优化策略。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

WebStorm Mac版
好用的JavaScript开发工具

SublimeText3 Linux新版
SublimeText3 Linux最新版

ZendStudio 13.5.1 Mac
功能强大的PHP集成开发环境

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

SublimeText3 英文版
推荐:为Win版本,支持代码提示!