Spring Security自定义AuthenticationManager实现手机号/密码双认证 📅 发布时间:2026/7/4 0:51:15 👁️ 浏览次数: 01整体思路 3 步走1.自定义认证提供者CustomAuthenticationProvider识别登录方式分发给对应UserDetailsService。2.双 Service•UserDetailsService验证账号密码•PhoneNumberUserService验证手机号验证码3.配置注入把自定义提供者塞进 Spring Security让它乖乖听话。02自定义认证提供者publicclassCustomAuthenticationProviderimplementsAuthenticationProvider{privatefinalUserDetailsServiceuserDetailsService;// 账号密码验证privatefinalPasswordEncoderpasswordEncoder;// 密码加密器privatefinalPhoneNumberUserServicephoneNumberUserService;// 手机号验证publicCustomAuthenticationProvider(UserDetailsServiceuserDetailsService,PasswordEncoderpasswordEncoder,PhoneNumberUserServicephoneNumberUserService){this.userDetailsServiceuserDetailsService;this.passwordEncoderpasswordEncoder;this.phoneNumberUserServicephoneNumberUserService;}OverridepublicAuthenticationauthenticate(Authenticationauthentication)throwsAuthenticationException{Stringprincipal(String)authentication.getPrincipal();// username:xxx 或 phone:xxxStringcredentials(String)authentication.getCredentials();// 密码或验证码UserDetailsuserDetails;if(principal.startsWith(username:)){// 账号密码登录Stringusernameprincipal.substring(username:.length());userDetailsuserDetailsService.loadUserByUsername(username);if(!passwordEncoder.matches(credentials,userDetails.getPassword())){thrownewBadCredentialsException(密码错误);}}elseif(principal.startsWith(phone:)){// 手机号登录StringphoneNumberprincipal.substring(phone:.length());userDetailsphoneNumberUserService.loadUserByPhoneNumber(phoneNumber);// 这里验证码校验可放在 service 内也可前置过滤器else{thrownewBadCredentialsException(登录方式不支持);}// 生成已认证令牌UsernamePasswordAuthenticationTokenresultnewUsernamePasswordAuthenticationToken(userDetails,credentials,userDetails.getAuthorities());result.setDetails(authentication.getDetails());returnresult;}Overridepublicbooleansupports(Class?authentication){returnUsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);}}}注解1.前缀识别用username:和phone:做路由避免写两套接口。2.职责分离验证码校验交给PhoneNumberUserService保持单一职责。3.线程安全所有依赖通过构造器注入无共享可变状态天然并发友好。03双 Service 实现UserDetailsService账号密码版ServiceRequiredArgsConstructorpublicclassUserDetailsServiceImplimplementsUserDetailsService{privatefinalUserMapperuserMapper;privatefinalMenuMappermenuMapper;OverridepublicUserDetailsloadUserByUsername(Stringusername)throwsUsernameNotFoundException{UseruseruserMapper.selectOne(newLambdaQueryWrapperUser().eq(User::getUserName,username));if(usernull)thrownewUsernameNotFoundException(用户不存在);ListStringpermsmenuMapper.selectPermsByUserId(user.getId());perms.add(user.getRoles());// 合并角色returnnewLoginUser(user,perms);}}PhoneNumberUserService手机号验证码版ServiceRequiredArgsConstructorpublicclassPhoneNumberUserService{privatefinalUserMapperuserMapper;privatefinalMenuMappermenuMapper;privatefinalRedisTemplateString,StringredisTemplate;// 缓存验证码publicUserDetailsloadUserByPhoneNumber(StringphoneNumber){// 1️ 查库UseruseruserMapper.selectOne(newLambdaQueryWrapperUser().eq(User::getPhonenumber,phoneNumber));if(usernull)thrownewRuntimeException(手机号未注册);// 2️ 查权限ListStringpermsmenuMapper.selectPermsByUserId(user.getId());perms.add(user.getRoles());// 3️验证码校验示例可前置过滤器//String codeInRedis redisTemplate.opsForValue().get(SMS: phoneNumber);returnnewLoginUser(user,perms);}}注解1.LambdaQueryWrapperMyBatis-Plus 写法链式清爽。2.角色权限合并把角色当权限塞到同一集合后续授权更丝滑。3.验证码解耦校验逻辑可放在 Service也可前置过滤器灵活插拔。04SecurityConfig把自定义提供者塞进去ConfigurationEnableWebSecurityRequiredArgsConstructorpublicclassSecurityConfig{privatefinalAuthenticationConfigurationauthenticationConfiguration;//密码加密器BeanpublicPasswordEncoderpasswordEncoder(){returnnewBCryptPasswordEncoder();}BeanpublicUserDetailsServiceuserDetailsService(){returnnewUserDetailsServiceImpl();}BeanpublicPhoneNumberUserServicephoneNumberUserService(){returnnewPhoneNumberUserService();}BeanpublicCustomAuthenticationProvidercustomAuthenticationProvider(){returnnewCustomAuthenticationProvider(userDetailsService(),passwordEncoder(),phoneNumberUserService());}BeanpublicAuthenticationManagerauthenticationManager()throwsException{// 替换默认 AuthenticationManagerreturnnewProviderManager(customAuthenticationProvider());}}注解1.ProviderManagerSpring Security 的核心调度器塞入我们的 Provider 就能接管认证。2.构造器注入Spring 推荐写法避免循环依赖。3.无 Autowired全部显式 Bean方便单测 Mock。05登录接口一行代码双通道RestControllerRequestMapping(/auth)RequiredArgsConstructorpublicclassAuthController{privatefinalAuthenticationManagerauthenticationManager;privatefinalRedisTemplateString,ObjectredisTemplate;PostMapping(/login)publicResultlogin(RequestBodyLoginDTOdto){Stringprincipaldto.getLoginType()1?username:dto.getUsername():phone:dto.getPhone();UsernamePasswordAuthenticationTokentokennewUsernamePasswordAuthenticationToken(principal,dto.getCredential());AuthenticationauthenticateauthenticationManager.authenticate(token);LoginUserloginUser(LoginUser)authenticate.getPrincipal();StringjwtJwtUtil.createJWT(loginUser.getUser().getId().toString());redisTemplate.opsForValue().set(login:loginUser.getUser().getId(),loginUser);returnResult.OK(登录成功,Map.of(token,jwt));}}注解1.DTO 统一前端传loginType1账号密码2手机号验证码后端零 if-else。2.JWT Redis无状态 Token 在线用户信息缓存分布式登录稳稳的。3.异常透传认证失败直接抛异常被全局异常处理器统一包装前端拿到统一格式。测试登录方式请求体返回账号密码{loginType:1,username:yuqn,credential:123456}{msg:登录成功,token:eyJ...}手机验证码{loginType:2,phone:13800138000,credential:8888}同上
600×600路面缺陷检测数据集:含纵向/横向裂纹、坑洞、不规则裂缝四类VOC标注,已分训练测试集 本文还有配套的精品资源,点击获取 简介:这个数据集专为路面缺陷目标检测任务设计,所有图像统一调整为600600像素RGB格式,覆盖四类常见病害:纵向裂纹、横向裂纹、坑洞、不规则裂缝。标注采用标准VOC格式,… 2026/6/3 1:49:56
Ubuntu重启后NVIDIA-SMI报错?别慌,三步修复驱动通信失败(附DKMS安装命令) Ubuntu重启后NVIDIA驱动失效的深度修复指南每次系统更新后,NVIDIA显卡驱动突然"失联"是许多开发者和运维人员都遇到过的棘手问题。当你满怀期待地重启服务器准备继续深度学习训练时,终端却冷冰冰地抛出一行"NVIDIA-SMI has failed becaus… 2026/6/3 1:49:25
告别卡顿!用Debian 10打造你的轻量级Linux开发环境(VMware最小化安装指南) 在VMware中构建极致轻量的Debian 10开发环境:从零配置到高效编码当你的开发机开始频繁卡顿,而预算又不允许立即升级硬件时,一个精简高效的Linux环境往往能带来意想不到的性能提升。作为最稳定的发行版之一,Debian 10(B… 2026/6/3 1:49:25
PCF8591与MKV46F256VLH16的信号转换系统设计与优化 1. PCF8591与MKV46F256VLH16的信号转换系统概述在嵌入式系统开发中,模拟信号与数字信号的相互转换是连接物理世界与数字世界的桥梁。PCF8591作为一款经典的8位ADC/DAC转换芯片,配合MKV46F256VLH16这款基于ARM Cortex-M4内核的微控制器,可以构… 2026/7/4 0:48:47
STM32L4R9AI与PCF8591的硬件协同设计与优化 1. PCF8591与STM32L4R9AI的硬件协同设计PCF8591是一款集成了4通道8位ADC和1通道8位DAC的I2C接口芯片,其典型工作电压范围为2.5V-6V。与STM32L4R9AI搭配使用时,需要注意几个关键硬件设计要点:1.1 电源系统设计PCF8591的供电设计直接影响ADC/DA… 2026/7/4 0:48:47
STM32F334R8驱动WS2812B LED灯带的完整指南 1. 项目概述:WS2812与STM32F334R8的完美组合 WS2812智能LED灯珠与STM32F334R8微控制器的组合,为嵌入式灯光控制领域带来了全新的可能性。WS2812作为一款集成了控制电路和RGB三色LED的智能灯珠,仅需单线通信即可实现全彩控制,而STM… 2026/7/4 0:46:46
Bifrost:三星固件下载的终极跨平台解决方案 Bifrost:三星固件下载的终极跨平台解决方案 【免费下载链接】Bifrost Cross-platform tool for downloading Samsung mobile device firmware. 项目地址: https://gitcode.com/gh_mirrors/sa/Bifrost 在三星手机用户和开发者的世界里,寻找官方固件… 2026/7/4 0:42:44
终极指南:如何用OBS插件一键同步直播到8个平台 终极指南:如何用OBS插件一键同步直播到8个平台 【免费下载链接】obs-multi-rtmp OBS複数サイト同時配信プラグイン 项目地址: https://gitcode.com/gh_mirrors/ob/obs-multi-rtmp 你是否曾为每个直播平台单独配置OBS而感到疲惫?是否希望扩大观众覆… 2026/7/4 0:38:43
浅谈异常与恋爱 在java的异常结构中有一个顶级父类叫做Throwable,这个父类具有两个子类,分别是: Error Exception 为了便于初学者能更好的理解异常机制,我姑且先列举一些不恰当的例子: 可以这样理解Error 1.你某天很舒服的坐在沙发… 2026/7/4 0:36:42
STM32F745VG与MC6470 IMU的高性能姿态控制系统设计 1. MC6470与STM32F745VG的黄金组合解析在工业自动化和机器人控制领域,传感器与微控制器的协同工作能力直接决定了系统的响应速度和定位精度。MC6470作为一款6自由度惯性测量单元(6DOF IMU),与STM32F745VG这款基于ARM Cortex-M7内核的高性能微控制器组合&… 2026/7/4 0:00:28
Playwright自动化测试实战:从零搭建现代Web测试框架 1. 项目概述:为什么是 Playwright?如果你正在为现代 Web 应用的自动化测试头疼,尤其是面对那些充斥着动态加载、复杂交互的单页应用(SPA),那么 Playwright 的出现,很可能就是你的解药。我接触过… 2026/7/4 0:00:28
终极指南:如何将JSXBIN二进制文件转换为可读JSX源代码 终极指南:如何将JSXBIN二进制文件转换为可读JSX源代码 【免费下载链接】jsxbin-to-jsx-converter JSXBin to JSX Converter written in C# 项目地址: https://gitcode.com/gh_mirrors/js/jsxbin-to-jsx-converter 你是否曾经面对过Adobe产品的JSXBIN文件感到… 2026/7/4 0:02:28