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,一经查实,立即删除!

相关文章

【并发编程】线程基础

目录 1、线程基础 1.1基本概念 1.1.1 进程与线程 1.1.1.2 什么是线程 1.1.1.3 两者间的联系和区别 1.1.2 多线程 1.1.2.1 什么是多线程 1.1.2.2 多线程的局限 1.1.3串行,并行,并发 1.1.3.1 什么是串行 1.1.3.2 什么是并行 1.1.3.3 什么是并发 1.1.3.4 区别和联系…

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…

vue3.0基础

1. setup函数 vue单页面使用到的变量和方法都定义在setup函数中,return后才能被页面引用 export default {setup(){const name 张三const person {name,age:30}function goWork(){consle.log(工作)}return {name,person,goWork}} } 注意&#xff1a;直接定义的变量修改不会…

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; 目录 引言 枚举 枚举…

网络连接和协议

网络连接是通过一系列协议来实现的&#xff0c;其中TCP/IP协议和HTTP协议是其中两个关键的协议。 1. **TCP/IP协议&#xff1a;** - TCP/IP&#xff08;Transmission Control Protocol/Internet Protocol&#xff09;是一组用于在互联网上传输数据的协议。它是一个层次化的…

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、安装指…

【matlab】MATLAB 中的标量运算及实例

MATLAB 中的标量运算及实例 引言 在 MATLAB 中&#xff0c;标量是指只包含单个数值的变量或常量。尽管标量运算可能看似简单&#xff0c;但它在数值计算、数据处理和算法设计中扮演着重要的角色。本文将深入探讨 MATLAB 中的标量运算&#xff0c;介绍其基本操作和一些实例应用…

还在为学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…

Pyhton之深入理解类

深入理解Python中的类 简介 在Python中&#xff0c;类是实现面向对象编程&#xff08;OOP&#xff09;的主要工具。它们允许我们创建自定义的数据结构&#xff0c;它包含了数据和对数据操作的方法。本文将帮助你理解Python中类的工作原理&#xff0c;以及如何有效地使用它们。…

element input 选中 但值未及时回显

这可能是因为 Vue 的渲染更新机制。Vue 使用异步更新队列&#xff0c;当你观察到数据变化时&#xff0c;Vue 将开启一个队列&#xff0c;并缓冲在同一事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发&#xff0c;只会被推入到队列中一次。这种在缓冲时去除重复数…

C语言连接zookeeper客户端(不能完全参考官网教程)

准备过程 1.通过VStudio 远程连接linux的开发环境&#xff1b; 2.g环境&#xff0c;通过MingW安装&#xff1b; 3.必须要安装好pthread.h的环境&#xff0c;不管是windows端&#xff08;linux 可视化端开发就不管这个&#xff09;还是linux端&#xff1b; 4.需要准备zookeeper…