SpringSecurity 手机号登录

一、工作流程 

1.向手机发送验证码,第三方短信发送平台,如阿里云短信。
2.手机获取验证码后,在表单中输入验证码。
3.使用自定义过滤器​SmsCodeValidateFilter​。
4.短信校验通过后,使用自定义手机认证过滤器​SmsCodeAuthenticationFilter校验手机号码是否存在​。
5.自定义​SmsCodeAuthenticationToken​提供给​SmsCodeAuthenticationFilter​。
6.自定义​SmsCodeAuthenticationProvider​提供给​AuthenticationManager​。
7.创建针对手机号查询用户信息的​SmsCodeUserDetailsService​,提交给​。SmsCodeAuthenticationProvider​。
8.自定义​SmsCodeSecurityConfig​配置类将上面组件连接起来。
9.将​SmsCodeSecurityConfig​添加到​LearnSrpingSecurity​安全配置的过滤器链上。

二、实现 

2.1、验证码生成、发送

/*** 创建验证码生成器*/
@Component
public class SmsCodeGenerator {public String generate() {return RandomStringUtils.randomNumeric(4);}
}/*** 验证码发送器*/
@Component
public class SmsCodeSender {public void send(String mobile, String code) {System.out.println("向手机" + mobile + "发送短信验证码" + code);}
}/*** 发送短信接口*/
@RestController
public class ValidateCodeController {@Autowiredprivate SmsCodeGenerator smsCodeGenerator;@Resourceprivate SmsCodeSender smsCodeSender;@Resourceprivate RedisTemplate redisTemplate;@GetMapping("/code/sms")public String createSmsCode(@RequestParam String mobile) throws IOException {//获取验证码String smsCode = smsCodeGenerator.generate();//把验证码设置到redisredisTemplate.boundValueOps(SecurityConstants.getValidCodeKey(mobile)).set(smsCode, 300, TimeUnit.SECONDS);smsCodeSender.send("18360903475", "登录验证码为:" + smsCode + ",五分钟过期");return "验证码是 : " + smsCode;}
}

2.2、手机号码认证 Token

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;import java.util.Collection;/*** 手机号码认证 Token*/
public class PhoneNumAuthenticationToken extends AbstractAuthenticationToken {private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;/*** principal的作用有两个, 在未登录之前是用户名,那么在登录之后是用户的信息。*/private final Object principal;/*** 构造* @param principal 手机号码*/public PhoneNumAuthenticationToken(Object principal) {super(null);this.principal = principal;// 用于指示AbstractSecurityInterceptor是否应向AuthenticationManager提供身份验证令牌setAuthenticated(false);}/*** 构造* @param principal 用户信息* @param authorities 用户权限列表*/public PhoneNumAuthenticationToken(Object principal,Collection<? extends GrantedAuthority> authorities) {super(authorities);this.principal = principal;// 用于指示AbstractSecurityInterceptor是否应向AuthenticationManager提供身份验证令牌setAuthenticated(true);}/*** 正常这个是返回密码,但手机登录没有密码,不用管*/@Overridepublic Object getCredentials() {return null;}/*** 获取手机号或用户信息*/@Overridepublic Object getPrincipal() {return this.principal;}
}

2.3、拦截请求、获取手机号码 

/*** 手机号码拦截器, 获取手机号码*/
public class PhoneNumAuthenticationFilter extends AbstractAuthenticationProcessingFilter {public PhoneNumAuthenticationFilter() {super(new AntPathRequestMatcher("/phoneLogin", "POST"));}@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {if (!Objects.equals(request.getMethod(),"POST")) {throw new AuthenticationServiceException("身份验证方法需为:'POST'请求");}// 获取手机号String phoneNum = Optional.ofNullable(request.getParameter(Constants.PHONE_NUM_PARAMETER)).map(String::trim).orElse("");// new 手机号码验证TokenPhoneNumAuthenticationToken authRequest = new PhoneNumAuthenticationToken(phoneNum);// 身份验证详细信息authRequest.setDetails(super.authenticationDetailsSource.buildDetails(request));return this.getAuthenticationManager().authenticate(authRequest);}
}

2.4、短信验证码验证过滤器

/*** 短信验证码验证过滤器*/
@Component
public class SmsCodeFilter extends OncePerRequestFilter {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Resourceprivate CustomizeAuthencationFailureHandler customizeAuthencationFailureHandler;@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain filterChain) throws ServletException, IOException {/*** uri = /phoneLogin 即手机号码登录才拦截*/if (Objects.equals(Constants.SMS_LOGIN_URI,request.getRequestURI())) {try{// 验证手机验证码validateProcess(request);}catch (AuthenticationException ex) {customizeAuthencationFailureHandler.onAuthenticationFailure(request, response, ex);return;}}filterChain.doFilter(request, response);}/*** 验证手机验证码*/private void validateProcess(HttpServletRequest request){// 获取手机号String msgCode = stringRedisTemplate.opsForValue().get(Constants.SMS_CODE_SESSION_KEY);String code = request.getParameter(Constants.MSG_CODE);if(Strings.isBlank(code)) {throw new InternalAuthenticationServiceException("短信验证码不能为空.");}if(null == msgCode) {throw new InternalAuthenticationServiceException("短信验证码已失效.");}if(!code.equals(msgCode)) {throw new InternalAuthenticationServiceException("短信验证码错误.");}}
}

2.5、继承 WebSecurityConfigurerAdapter 配置 HttpSecurity 

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {/*** 数据源*/@Resourceprivate DataSource dataSource;/*** 用户信息服务*/@Resourceprivate UserAuthentication userAuthentication;/*** 成功处理*/@Resourceprivate CustomizeAuthencationSuccessHandler customizeAuthencationSuccessHandler;/*** 失败处理*/@Resourceprivate CustomizeAuthencationFailureHandler customizeAuthencationFailureHandler;/*** 用户登出处理*/@Resourceprivate UserLogoutSuccessHandler userLogoutSuccessHandler;/*** 多用户登录处理*/@Resourceprivate MutilpleSessionHandler mutilpleSessionHandler;/*** 密码编码器*/@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}/*** 手机号码登录验证处理*/@Resourceprivate DaoPhoneNumAuthenticationProvider daoPhoneNumAuthenticationProvider;/*** 信息验证码过滤器*/@Resourceprivate SmsCodeFilter smsCodeFilter;/*** 把AuthenticationManager公开*/@Override@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}/*** 配置自定义验证查询/加密工具*/@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userAuthentication).passwordEncoder(passwordEncoder());}/*** 手机号码登录拦截器*/@Beanpublic PhoneNumAuthenticationFilter phoneNumAuthenticationFilter() throws Exception {// 手机号码拦截器, 获取手机号码PhoneNumAuthenticationFilter phoneNumAuthenticationFilter = new PhoneNumAuthenticationFilter();phoneNumAuthenticationFilter.setAuthenticationManager(authenticationManagerBean());//使用手机号登录失败了如何处理phoneNumAuthenticationFilter.setAuthenticationFailureHandler(customizeAuthencationFailureHandler);// 使用手机号登录成功了如何处理phoneNumAuthenticationFilter.setAuthenticationSuccessHandler(customizeAuthencationSuccessHandler);return phoneNumAuthenticationFilter;}@Overrideprotected void configure(HttpSecurity http) throws Exception {http// 加入短信验证码过滤器.addFilterBefore(smsCodeFilter, UsernamePasswordAuthenticationFilter.class)// 加入手机号码登录过滤器.addFilterAfter(phoneNumAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)// 加入手机号码登录验证提供者.authenticationProvider(daoPhoneNumAuthenticationProvider)// 表单登录.formLogin()// 未登录跳转登录页面.loginPage("/login.html")// 指定登录路径.loginProcessingUrl("/login")// 用户登录成功的处理.successHandler(customizeAuthencationSuccessHandler)// 用户登录失败的处理.failureHandler(customizeAuthencationFailureHandler)// 因为用户传入过来的token, 需要再次进行校验.userDetailsService(userAuthentication).tokenValiditySeconds(3600)// .alwaysRemember(true)// 认证配置.and().authorizeRequests()//不拦截的Url.antMatchers("/login.html", "/image/code", "/smsCode", "/css/**", "/js/**", "/phoneLogin").permitAll().anyRequest()  //所有的请求.authenticated()   //认证之后就可以访问// 多端登录限制,限制一个账号同时只能一个人登录.and().sessionManagement().maximumSessions(1).expiredSessionStrategy(mutilpleSessionHandler).and()// 登出配置.and().logout().logoutUrl("/logout")// 登出成功处理.logoutSuccessHandler(userLogoutSuccessHandler).and().csrf().disable();}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/231004.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

UE4/UE5 日志插件(基于spdlog)

1 解决问题 对于高频日志序列化到本地的需求&#xff0c;spdlog肯定完美满足。 源码地址&#xff1a;https://github.com/gabime/spdlog 博主下载的版本为 spdlog-1.12.0&#xff0c;各位大佬可以根绝自己爱好选择。 2 过程介绍 大概目录&#xff1a; SpdlogLibC目录下是对…

Qt/C++音视频开发60-坐标拾取/按下鼠标获取矩形区域/转换到视频源真实坐标

一、前言 通过在通道画面上拾取鼠标按下的坐标&#xff0c;然后鼠标移动&#xff0c;直到松开&#xff0c;根据松开的坐标和按下的坐标&#xff0c;绘制一个矩形区域&#xff0c;作为热点或者需要电子放大的区域&#xff0c;拿到这个坐标区域&#xff0c;用途非常多&#xff0…

C语言之文件操作(下)

C语言之文件操作&#xff08;下&#xff09; 文章目录 C语言之文件操作&#xff08;下&#xff09;1. 文件的顺序读写1.1 文件的顺序读写函数1.1.1 字符输入/输出函数&#xff08;fgetc/fputc&#xff09;1.1.2 ⽂本⾏输⼊/输出函数&#xff08;fgets/fputs&#xff09;1.1.3 格…

工业应用新典范,飞凌嵌入式FET-D9360-C核心板发布!

来源&#xff1a;飞凌嵌入式官网 当前新一轮科技革命和产业变革突飞猛进&#xff0c;工业领域对高性能、高可靠性、高稳定性的计算需求也在日益增长。为了更好地满足这一需求&#xff0c;飞凌嵌入式与芯驰科技&#xff08;SemiDrive&#xff09;强强联合&#xff0c;基于芯驰D9…

SI24R03国产自主可控RISC-V架构MCU低功耗2.4GHz收发芯片SoC

目录 RISC-V架构的优势SI24R03/04特性射频收发器模块特征MCU 模块特征 其他特征 RISC-V架构的优势 相对于目前主流的英特尔X86架构及ARM等架构来说&#xff0c;RISC-V架构具有指令精简、模块化、可扩展、开源、免费等优点。RISC-V的基础指令集只有40多条&#xff0c;加上其他基…

Kafka--从Zookeeper数据理解Kafka集群工作机制

从Zookeeper数据理解Kafka集群工作机制 这一部分主要是理解Kafka的服务端重要原理。但是Kafka为了保证高吞吐&#xff0c;高性能&#xff0c;高可扩展的三高架构&#xff0c;很多具体设计都是相当复杂的。如果直接跳进去学习研究&#xff0c;很快就会晕头转向。所以&#xff0c…

Echarts相关配置

title&#xff1a;标题组件 tooltip:提示框组件 legend:图例组件 toolbox:工具栏 grid&#xff1a;直角坐标系内绘图网格 xAxis:直角坐标系grid中的x轴 yAxis&#xff1a;直角坐标系grid中的y轴 series:系列列表。每个系列通过type决定自己的图表类型 color&#xff1a;调色…

如何用 Cargo 管理 Rust 工程系列 戊

以下内容为本人的学习笔记&#xff0c;如需要转载&#xff0c;请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq.com/s/-OiWtUCUc3FmKIGMBEYfHQ 单元和集成测试 Rust 为单元测试提供了非常好的原生支持。 创建库工程时&#xff0c;cargo 生成的源码文件 lib.rs 自带…

【C语言】自定义类型——枚举、联合体

引言 对枚举、联合体进行介绍&#xff0c;包括枚举的声明、枚举的优点&#xff0c;联合体的声明、联合体的大小。 ✨ 猪巴戒&#xff1a;个人主页✨ 所属专栏&#xff1a;《C语言进阶》 &#x1f388;跟着猪巴戒&#xff0c;一起学习C语言&#x1f388; 目录 引言 枚举 枚举…

06. Python模块

目录 1、前言 2、什么是模块 3、Python标准库模块 3.1、os模块 3.2、datetime 模块 3.3、random模块 4、自定义模块 4.1、创建和使用 4.2、模块命名空间 4.3、作用域 5、安装第三方依赖 5.1、使用 pip 安装单个依赖 5.2、从 requirements.txt 安装依赖 5.3、安装指…

还在为学MyBatis发愁?史上最全,一篇文章带你学习MyBatis

文章目录 前言一、&#x1f4d6;MyBatis简介1.Mybatis历史2.MyBatis特性3.对比&#xff08;其他持久化层技术&#xff09; 二、&#x1f4e3;搭建MyBatis1.开发环境2.创建maven工程3.创建MyBatis核心配置文件4.创建mapper接口5.创建MyBatis的映射文件6.通过junit测试功能7.加入…

OpenCV4工业缺陷检测的六种方法

机器视觉 机器视觉是使用各种工业相机&#xff0c;结合传感器跟电气信号实现替代传统人工&#xff0c;完成对象识别、计数、测量、缺陷检测、引导定位与抓取等任务。其中工业品的缺陷检测极大的依赖人工完成&#xff0c;特别是传统的3C制造环节&#xff0c;产品缺陷检测依赖于人…

python+torch线性回归模型机器学习

程序示例精选 pythontorch线性回归模型机器学习 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《pythontorch线性回归模型机器学习》编写代码&#xff0c;代码整洁&#xff0c;规则&#xf…

USB2.0 Spec

USB System Description A USB system is described by three definitional areas: • USB interconnect • USB devices • USB host USB interconnect The USB interconnect is the manner in which USB devices are connected to and communicate with the host. USB Ho…

docker基本命令

1.docker命令图解 2. 从仓库拉取镜像 #下载最新版 docker pull nginx # 镜像名:版本名&#xff08;标签&#xff09; docker pull nginx:1.20.1docker rmi 镜像名:版本号/镜像id3. 容器启动及停止 docker run [OPTIONS] IMAGE [COMMAND] [ARG...] docker run [设置项] 镜…

R语言【rgbif】——occ_search对待字符长度大于1500的WKT的特殊处理真的有必要吗?

一句话结论&#xff1a;只要有网有流量&#xff0c;直接用长WKT传递给参数【geometry】、参数【limit】配合参数【start】获取所有记录。 当我在阅读 【rgbif】 给出的用户手册时&#xff0c;注意到 【occ_search】 强调了 参数 【geometry】使用的wkt格式字符串长度。 文中如…

使用数组模拟栈的相关操作【栈1.1】

public class ArrayStackDemo {public static void main(String[] args) {ArrayStack arrayStack new ArrayStack(4);Scanner sc new Scanner(System.in);boolean loop true;char key ;while (loop) {System.out.println("栈操作菜单项");System.out.println(&q…

范仲淹仅存五首词作,篇篇经典

范仲淹&#xff0c;北宋时期杰出的政治家&#xff0c;文学家&#xff0c;提起他首先想到初中那篇《岳阳楼记》&#xff0c;难倒无数背诵困难户&#xff0c;它虽然难背&#xff0c;但却是一篇文字排列极美&#xff0c;意境极佳的美文。 “不以物喜&#xff0c;不以己悲&#xf…

Android hilt使用

一&#xff0c;添加依赖库 添加依赖库app build.gradle.kts implementation("com.google.dagger:hilt-android:2.49")annotationProcessor("com.google.dagger:hilt-android:2.49")annotationProcessor("com.google.dagger:hilt-compiler:2.49"…

程序人生,由“小作文”事件想到的

时势造英雄。自媒体时代&#xff0c;火出圈是靠大众的审美和爱好&#xff0c;自己能做的关键&#xff0c;其实是做好自己&#xff0c;选择向上生长&#xff0c;持续不断的读书、学习。同时保持一份好奇心&#xff0c;培养一个兴趣爱好并自得其乐。 展示自我 回想起我小时候&am…