Spring Security手机验证码登录实现与优化
1. Spring Security与手机验证码登录的核心原理在传统的Spring Security体系中用户名密码认证是最基础的认证方式。但现代应用往往需要更灵活的认证方案手机验证码登录就是其中典型代表。要实现这种认证方式我们需要理解Spring Security的扩展机制。Spring Security的认证流程本质上是一个过滤器链其中UsernamePasswordAuthenticationFilter负责处理表单登录。当我们要添加手机验证码登录时实际上是在这个链条中插入一个新的自定义过滤器。这个过滤器需要完成以下工作从请求中提取手机号和验证码验证验证码的有效性创建对应的认证令牌(Authentication Token)调用认证管理器(AuthenticationManager)完成认证关键点在于Spring Security的认证体系是围绕AuthenticationProvider展开的。我们需要实现一个自定义的AuthenticationProvider来处理我们的手机验证码令牌。public class SmsCodeAuthenticationProvider implements AuthenticationProvider { private UserDetailsService userDetailsService; Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { String mobile (String) authentication.getPrincipal(); // 这里应该添加验证码校验逻辑 UserDetails userDetails userDetailsService.loadUserByUsername(mobile); if(userDetails null) { throw new UsernameNotFoundException(手机号未注册); } return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); } Override public boolean supports(Class? authentication) { return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication); } }2. 五分钟快速集成方案2.1 基础环境准备首先确保你的项目已经包含Spring Security依赖。如果是Spring Boot项目添加以下依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency2.2 验证码生成与校验验证码的生成和校验是手机登录的核心。我们可以创建一个简单的验证码服务Service public class SmsCodeService { // 使用ConcurrentHashMap存储验证码实际项目中应该使用Redis private MapString, String codeMap new ConcurrentHashMap(); public String generateCode(String mobile) { String code RandomStringUtils.randomNumeric(6); codeMap.put(mobile, code); // 实际项目中这里应该调用短信服务发送验证码 return code; } public boolean verifyCode(String mobile, String code) { String savedCode codeMap.get(mobile); return code ! null code.equals(savedCode); } }2.3 自定义认证过滤器创建处理手机验证码登录的过滤器public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter { private final SmsCodeService smsCodeService; public SmsCodeAuthenticationFilter(SmsCodeService smsCodeService) { super(new AntPathRequestMatcher(/login/mobile, POST)); this.smsCodeService smsCodeService; } Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { String mobile request.getParameter(mobile); String code request.getParameter(code); if(!smsCodeService.verifyCode(mobile, code)) { throw new BadCredentialsException(验证码错误); } SmsCodeAuthenticationToken authRequest new SmsCodeAuthenticationToken(mobile); return this.getAuthenticationManager().authenticate(authRequest); } }2.4 安全配置整合最后在安全配置中将所有组件整合起来Configuration EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { Autowired private UserDetailsService userDetailsService; Autowired private SmsCodeService smsCodeService; Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers(/code/sms).permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage(/login) .permitAll() .and() .addFilterBefore(smsCodeAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); } Bean public SmsCodeAuthenticationFilter smsCodeAuthenticationFilter() throws Exception { SmsCodeAuthenticationFilter filter new SmsCodeAuthenticationFilter(smsCodeService); filter.setAuthenticationManager(authenticationManagerBean()); return filter; } Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(smsCodeAuthenticationProvider()); } Bean public SmsCodeAuthenticationProvider smsCodeAuthenticationProvider() { SmsCodeAuthenticationProvider provider new SmsCodeAuthenticationProvider(); provider.setUserDetailsService(userDetailsService); return provider; } }3. 生产环境注意事项3.1 验证码安全增强上述示例为了简洁使用了内存存储验证码实际项目中应该使用Redis等分布式缓存存储验证码设置合理的过期时间通常5分钟添加验证码防刷机制限制同一IP/手机号的发送频率验证码应该包含一定的随机性和复杂度避免使用简单数字组合// 改进的验证码生成逻辑 public String generateSecureCode(String mobile) { // 添加频率限制 if(redisTemplate.opsForValue().get(SMS_LIMIT: mobile) ! null) { throw new RuntimeException(发送过于频繁); } // 生成更复杂的验证码 String code RandomStringUtils.randomAlphanumeric(8).toUpperCase(); // 存储验证码设置5分钟过期 redisTemplate.opsForValue().set(SMS_CODE: mobile, code, 5, TimeUnit.MINUTES); // 设置发送间隔限制 redisTemplate.opsForValue().set(SMS_LIMIT: mobile, 1, 1, TimeUnit.MINUTES); return code; }3.2 用户注册与绑定流程手机验证码登录通常涉及以下场景处理新用户首次使用手机号登录时自动注册已注册用户绑定新手机号手机号更换处理建议实现一个统一的用户身份管理系统将手机号作为用户的一个属性而非唯一标识。3.3 性能优化建议验证码校验应该快速失败避免不必要的数据库查询考虑使用缓存存储用户信息减少认证过程中的数据库压力对于高并发场景可以采用异步方式处理短信发送4. 常见问题排查4.1 过滤器不生效问题如果自定义的SMS认证过滤器没有生效检查以下方面过滤器是否被正确添加到过滤器链中顺序很重要请求的URL和HTTP方法是否匹配过滤器的配置是否被其他安全规则拦截4.2 验证码校验失败验证码校验失败的常见原因包括手机号存储键不一致注意大小写、空格等问题验证码过期时间设置过短分布式环境下的缓存一致性问题4.3 用户查找问题当出现用户查找失败时检查UserDetailsService的实现是否正确手机号在数据库中的存储格式用户是否被禁用或锁定5. 进阶扩展思路5.1 多因素认证可以将手机验证码作为第二因素实现多因素认证先进行用户名密码认证通过后要求输入手机验证码全部验证通过后发放访问令牌5.2 无密码登录完全依赖手机验证码的无密码登录系统用户只需输入手机号获取验证码验证通过后直接登录适合对安全性要求不高的场景5.3 国际号码支持对于有国际用户的应用需要考虑手机号格式的国际标准化处理国际短信服务的集成时区相关的验证码过期策略在实际项目中集成手机验证码登录时我发现最容易被忽视的是验证码的生命周期管理。很多开发者只关注验证码的生成和校验却忘记了及时清理已使用的验证码这可能导致安全风险。建议在验证通过后立即使验证码失效而不是等待其自然过期。另一个实用技巧是在验证码中加入场景标识比如LOGIN:123456和RESET_PWD:123456可以是不同的验证码这样可以防止验证码被跨场景滥用。这种细粒度的控制能显著提升系统的安全性。

相关新闻

Windows资源管理器崩溃原因分析与系统级修复方案

Windows资源管理器崩溃原因分析与系统级修复方案

1. Windows文件资源管理器崩溃问题概述Windows文件资源管理器(Explorer.exe)作为系统核心组件,负责管理桌面、任务栏、开始菜单和文件系统界面。当它频繁崩溃时,用户会遇到桌面闪退、任务栏消失、窗口突然关闭等问题,严…

2026/10/9 9:24:14 阅读更多 →
AI如何识别服装的印花、褶皱、反光与污渍?

AI如何识别服装的印花、褶皱、反光与污渍?

当一件衣服同时出现印花、褶皱、反光和污渍时,人眼可以轻松分辨,但对于人工智能(AI)来说,这却是一个需要综合运用多种“感官”和“知识”的复杂任务。AI并非简单地“看”图片,而是通过一系列算法步骤&#…

2026/10/9 12:26:12 阅读更多 →
Redis 缓存学习笔记(一):缓存基础与底层数据结构

Redis 缓存学习笔记(一):缓存基础与底层数据结构

缓存是什么 缓存的本质,是用“可接受的不一致”和“额外存储空间”,换取更低的访问成本。 什么场景适合缓存 缓存本质是一次权衡:我愿意引入数据的不一致性和额外的存储空间这两项成本,来换取更低的访问成本。 要不要缓存&…

2026/10/9 21:00:08 阅读更多 →

最新新闻

如何用 SearXNG 给本地 Qwen3 搭一个能联网搜索的 MCP?TaoToken 统一 Key 接入实践

如何用 SearXNG 给本地 Qwen3 搭一个能联网搜索的 MCP?TaoToken 统一 Key 接入实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 6:14:10 阅读更多 →
从接需求到上线:Trae 的“原生中文 Agent”模式,是否真的比 Cursor Composer 更懂中国程序员?TaoToken 统一 Key 通道实测

从接需求到上线:Trae 的“原生中文 Agent”模式,是否真的比 Cursor Composer 更懂中国程序员?TaoToken 统一 Key 通道实测

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 6:14:10 阅读更多 →
撕开 DeepSeek MLA 源码:现代 C++ / TensorRT-LLM 底层算子设计与优化全景

撕开 DeepSeek MLA 源码:现代 C++ / TensorRT-LLM 底层算子设计与优化全景

在 64K 长上下文推理中,以 128 头、单头 128 维的注意力层配置为例,传统多头注意力(MHA)每生成一个 Token 就需要从显存读取 64 KB 的 KV 缓存;DeepSeek 提出的多头潜在注意力(Multi-Head Latent Attention, MLA)通过 512 维潜在向量与解耦 RoPE 机制,将单 Token 缓存体…

2026/10/11 6:14:10 阅读更多 →
900KB请求就能让Next.js服务器瘫痪?CVE-2026-23870高危漏洞来袭,React 19用户必须立刻升级

900KB请求就能让Next.js服务器瘫痪?CVE-2026-23870高危漏洞来袭,React 19用户必须立刻升级

一个不足1MB的POST请求,就能让整台运行Next.js的生产服务器瞬间“冻僵”——这不是危言耸听,而是编号为CVE-2026-23870的高危拒绝服务漏洞正在上演的真实剧本。如果你团队的项目正跑在React 19.x之上,这条消息值得你放下手头的工作&#xff0…

2026/10/11 6:13:10 阅读更多 →
律所案件管理系统实战:SpringBoot+Vue3+MyBatis开发详解

律所案件管理系统实战:SpringBoot+Vue3+MyBatis开发详解

一套律师事务所案件管理系统,说高级点是数字化管理,说白了就是帮律师把案子、客户、日程、文书从“Excel 表格 微信聊天 脑子记”的混乱状态里解放出来。前端 Vue3 做交互界面,后端 SpringBoot 提供接口,MyBatis 管数据库操作&a…

2026/10/11 6:13:10 阅读更多 →
MobileNetV2实战:PyTorch训练到TensorRT部署全流程解析

MobileNetV2实战:PyTorch训练到TensorRT部署全流程解析

简介:面向深度学习初学者和有模型部署需求的开发者,提供一套基于PyTorch mobilenetv2的图像分类完整实践资源,覆盖从数据准备、模型训练到ONNX与TensorRT推理的端到端流程。资源包共2000个文件,以大量png样本图片、py脚本及pyc编译…

2026/10/11 6:13:09 阅读更多 →

日新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 10:38:42 阅读更多 →