spring security 手机号 短信验证码认证、验证码认证 替换默认的用户名密码认证132

         spring security内置的有用户名密码认证规则,还可以调用第三方微信、qq登录接口实现登录认证,这里使用自定义的手机号和短信验证码实现登录认证。   

         要实现自定义的手机号和短信验证码认证需要了解用户名密码认证的逻辑,仿照该逻辑就可以写出任何自定义的登录认证:

                Filter用来过滤对应的登录请求

                Manager用来寻找具体能匹配上Token的Provider对象校验

                Provider调用Service,Service返回一个已经存储的用户信息封装为Token对象,

 Provider 拿去该Token对象和用户登录封装的Token值比较。如果匹配成功返回一个新的已认证的Token。

        一、熟悉security框架用户名密码校验逻辑,Filter、Provider、Authentication。  

0)UsernamePasswordAuthenticationToken

        UsernamePasswordAuthenticationToken就是Authentication的一个实现类

        



1)UsernamePasswordAuthenticationFilter:



2)AuthenticationManager

        security实现认证的实现类为ProviderManager。



3)DaoAuthenticationProvider

                ①AbstractUserDetailsAuthenticationProvider

                                       DaoAuthenticationProvider的authenticate()方法在父类                     AbstractUserDetailsAuthenticationProvider中;

             ②  DaoAuthenticationProvider的retrieveUser()方法

   



            4)UserDetilsService

                        调用实现类WebSecurityConfigurerAdapter完成创建一个包含用户名和密码的Authentication对象。

                        密码由内部类随机生成。

 二、自定义手机号验证码登录认证。

                实现该认证,只需要重写Token,Filter,Provider,UserDetilsService。这里验证码可以使用阿里云的免费短信测试,因为我的白嫖短信已经过期,所以这里事先写死验证码,最后调用阿里云的短信验证API。

                0)登录业务的两个url

                                用户点击发送短信调用的单元方法,使用直接响应

    @RequestMapping("/sendPhoneCode")@ResponseBodypublic String sendPhoneCode(String phone,HttpSession session) throws ExecutionException, InterruptedException {String code = RandomNumberCode.creatCode();SendSmsResponse sendSmsResponse = SmsUtils.sendCode(phone,code);session.setAttribute("smsCode",code);
//        if (sendSmsResponse.getStatusCode() == 200){
//            return AjaxResultVo.success(200);
//        }return AjaxResultVo.success(200);}

                              用户点击登录的拦截url,使用过滤器实现

        

           1)Token

                             模仿UsernamePasswordAuthenticationToken,把密码删除。验证码直接在controller层比较,如果验证码不对没必要比较手机号是否注册。

public class SmsAuthenticationToekn extends AbstractAuthenticationToken {private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;private final Object principal;public SmsAuthenticationToekn(Object principal) {super(null);this.principal = principal;setAuthenticated(false);}public SmsAuthenticationToekn(Object principal,Collection<? extends GrantedAuthority> authorities) {super(authorities);this.principal = principal;super.setAuthenticated(true); // must use super, as we override}@Overridepublic Object getCredentials() {return null;}@Overridepublic Object getPrincipal() {return this.principal;}@Overridepublic void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {Assert.isTrue(!isAuthenticated,"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");super.setAuthenticated(false);}@Overridepublic void eraseCredentials() {super.eraseCredentials();}
}


        2)Filter

                修改拦截路径,比较发送短信的验证码和用户的验证码是否一致,不一致直接异常。这里发送短信的验证码直接为1234

package com.xja.filter;import com.xja.domain.SmsAuthenticationToekn;
import org.springframework.lang.Nullable;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;/*** @author rk* @description: TODO* @date 2024/9/16 19:27*/
public class SmsAuthenticationFilter extends AbstractAuthenticationProcessingFilter {public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/phoneLogin","POST");private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;private boolean postOnly = true;public SmsAuthenticationFilter() {super(DEFAULT_ANT_PATH_REQUEST_MATCHER);}@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {System.out.println("过滤器生效");if (this.postOnly && !request.getMethod().equals("POST")) {throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());}HttpSession session = request.getSession();String smsCode = (String) session.getAttribute("smsCode");
//        if (smsCode.equals(request.getParameter("code"))){
//                throw  new UsernameNotFoundException("用户名或验证码错误");
//            }if (!"1234".equals(request.getParameter("code"))){throw  new UsernameNotFoundException("用户名或验证码错误");}String username = obtainUsername(request);username = (username != null) ? username : "";username = username.trim();SmsAuthenticationToekn authRequest = new SmsAuthenticationToekn(username);setDetails(request, authRequest);return this.getAuthenticationManager().authenticate(authRequest);}@Nullableprotected String obtainUsername(HttpServletRequest request) {return request.getParameter(this.usernameParameter);}protected void setDetails(HttpServletRequest request, SmsAuthenticationToekn authRequest) {authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));}
}


        3)Provider

                如果比较成功 返回一个新创建的Token对象存储用户信息。

public class SmsAuthenticationProvider implements AuthenticationProvider {@Autowiredprivate UserDetailsService userDetailsService;@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {System.out.println("校验器生效");SmsAuthenticationToekn smsAuthenticationToekn = (SmsAuthenticationToekn) authentication;UserDetails userDetails = userDetailsService.loadUserByUsername(smsAuthenticationToekn.getName());if (userDetails == null){throw  new UsernameNotFoundException("用户名或验证码错误");}System.out.println();return new SmsAuthenticationToekn(userDetails.getUsername(),userDetails.getAuthorities());}@Overridepublic boolean supports(Class<?> authentication) {return (SmsAuthenticationToekn.class.isAssignableFrom(authentication));}}


        4)UserDetilsService

                        从数据库中校验是否有该手机号,这里的业务是没有提示用户输入有误,

很多登录界面使用手机号验证码时不需要注册,那么在这里也可以直接调用mapper注册该手机号.

@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate UserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {System.out.println("service生效");User user = userMapper.SelectUserByUserName(name);System.out.println("user = " + user.toString());if (user == null){throw new UsernameNotFoundException("用户名或密码错误");}List<SimpleGrantedAuthority> powerNameList = new ArrayList<SimpleGrantedAuthority>();powerNameList.add(new SimpleGrantedAuthority("ROLE_"+"userManage"));powerNameList.add(new SimpleGrantedAuthority("system:user:add"));return new org.springframework.security.core.userdetails.User(user.getUname(),user.getUpassword(),powerNameList);}

        5)调用阿里云api发送手机验证码

        5.1)复制SDK,导入依赖

                     AccessKey需要手动创建

                     手机号和验证码在用户发送请求时获取

        5.2)创建AccessKey

        5.3)封装发送短信的工具类

package com.xja.util;import com.aliyun.auth.credentials.Credential;
import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
import com.aliyun.sdk.service.dysmsapi20170525.AsyncClient;
import com.aliyun.sdk.service.dysmsapi20170525.models.AddSmsSignRequest;
import com.aliyun.sdk.service.dysmsapi20170525.models.AddSmsSignResponse;
import com.aliyun.sdk.service.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.sdk.service.dysmsapi20170525.models.SendSmsResponse;
import com.google.gson.Gson;
import darabonba.core.client.ClientOverrideConfiguration;import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;/*** @author rk* @description: TODO* @date 2024/9/18 17:07*/
public class SmsUtils {public static SendSmsResponse sendCode(String phone,String code) throws ExecutionException, InterruptedException {// HttpClient Configuration/*HttpClient httpClient = new ApacheAsyncHttpClientBuilder().connectionTimeout(Duration.ofSeconds(10)) // Set the connection timeout time, the default is 10 seconds.responseTimeout(Duration.ofSeconds(10)) // Set the response timeout time, the default is 20 seconds.maxConnections(128) // Set the connection pool size.maxIdleTimeOut(Duration.ofSeconds(50)) // Set the connection pool timeout, the default is 30 seconds// Configure the proxy.proxy(new ProxyOptions(ProxyOptions.Type.HTTP, new InetSocketAddress("<your-proxy-hostname>", 9001)).setCredentials("<your-proxy-username>", "<your-proxy-password>"))// If it is an https connection, you need to configure the certificate, or ignore the certificate(.ignoreSSL(true)).x509TrustManagers(new X509TrustManager[]{}).keyManagers(new KeyManager[]{}).ignoreSSL(false).build();*/// Configure Credentials authentication information, including ak, secret, tokenStaticCredentialProvider provider = StaticCredentialProvider.create(Credential.builder()// Please ensure that the environment variables ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET are set..accessKeyId("xxx").accessKeySecret("xxx")//.securityToken(System.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")) // use STS token.build());// Configure the ClientAsyncClient client = AsyncClient.builder().region("cn-hangzhou") // Region ID//.httpClient(httpClient) // Use the configured HttpClient, otherwise use the default HttpClient (Apache HttpClient).credentialsProvider(provider)//.serviceConfiguration(Configuration.create()) // Service-level configuration// Client-level configuration rewrite, can set Endpoint, Http request parameters, etc..overrideConfiguration(ClientOverrideConfiguration.create()// Endpoint 请参考 https://api.aliyun.com/product/Dysmsapi.setEndpointOverride("dysmsapi.aliyuncs.com")//.setConnectTimeout(Duration.ofSeconds(30))).build();// Parameter settings for API requestSendSmsRequest sendSmsRequest = SendSmsRequest.builder().signName("阿里云短信测试").templateCode("SMS_134567").phoneNumbers(phone).templateParam("{\"code\":\""+code+"\"}")// Request-level configuration rewrite, can set Http request parameters, etc.// .requestConfiguration(RequestConfiguration.create().setHttpHeaders(new HttpHeaders())).build();// Asynchronously get the return value of the API requestCompletableFuture<SendSmsResponse> response = client.sendSms(sendSmsRequest);// Synchronously get the return value of the API requestSendSmsResponse resp = response.get();System.out.println(new Gson().toJson(resp));// Asynchronous processing of return values/*response.thenAccept(resp -> {System.out.println(new Gson().toJson(resp));}).exceptionally(throwable -> { // Handling exceptionsSystem.out.println(throwable.getMessage());return null;});*/// Finally, close the clientclient.close();return resp;}
}

 6)发送短信

        也算是成功了吧

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Java进阶之集合框架(Set)

【基本内容】 二、Set接口(接上一章) Set是Java集合框架中不允许有重复元素的无序集合&#xff0c;其典型的实现类是HashSet&#xff0c;它完全是遵循Set接口特性规范实现的&#xff0c;无序且不允许元素重复&#xff1b;而Set接口下的实现类还有LinkedHashSet和TreeSort&#…

记录生产环境,通过域名访问的图片展示不全,通过ip+端口的方式访问图片是完整的

原因&#xff1a;部署nginx的服务器硬盘满了 排查发现nginx日志文件占用了大量硬盘 解决方案&#xff1a; 删除该文件&#xff0c;重启nginx服务&#xff0c;问题解决。

AI修手有救了?在comfyui中使用Flux模型实现局部重绘案例

&#x1f431;‍&#x1f409;背景 局部重绘相关的话题我们已经讨论和测试过很多次了&#xff0c;比如说inpaint模型、brushnet模型、powerpaint模型等等&#xff0c;最近对于flux模型重绘画面的案例也越来越多了&#xff0c;那我们就结合flux模型的重绘来试试看效果。 &…

前端mock了所有……

目录 一、背景描述 二、开发流程 1.引入Mock 2.创建文件 3.需求描述 4.Mock实现 三、总结 一、背景描述 前提&#xff1a; 事情是这样的&#xff0c;老板想要我们写一个demo拿去路演/拉项目&#xff0c;有一些数据&#xff0c;希望前端接一下&#xff0c;写几个表格&a…

qt信号与槽(自定义)

自定义信号与槽 在qt里&#xff0c;我们可以自己去定义信号与槽。 这里举个栗子&#xff1a; 信号的定义 在我们类里边定义一个信号&#xff0c;我们需要用signals&#xff1a;来声明&#xff0c;不用再去cpp文件里边定义。而且返回值必须是void&#xff0c;可以有参数。 槽…

2024年最新测绘地理信息规范在线查看下载

随着科技的飞速发展&#xff0c;测绘地理信息行业也迎来了新的机遇与挑战。 为了确保测绘地理信息的准确性和规范性&#xff0c;每年都会出台了一系列最新的测绘地理信息规范。 本文将历年地形行业发布的相关标准规范&#xff0c;包括现行和一些已经弃用的标准&#xff0c;截…

数据结构与算法——详谈栈和队列

目录 一&#xff1a;栈 1.1&#xff1a;栈的概念结构与实现 1.1.1&#xff1a;栈的概念结构 1.1.2&#xff1a;栈的实现 1.2&#xff1a;栈的各个功能实现 1.2.1&#xff1a;对栈进行初始化 1.2.2&#xff1a;判空栈 1.2.3&#xff1a;入栈 1.2.4&#xff1a;出栈 1.…

一文读懂AI安全治理框架

随着AI的发展以及研究&#xff0c;我们总会提到AI带来的一些潜在威胁&#xff0c;但截止目前我还没有完全的梳理过AI到底有哪些潜在的风险&#xff0c;今天就来一一看一下&#xff01;陆续补齐。

自动化中验证码的操作笔记,懂的赶紧收藏!

在自动化测试的过程中&#xff0c;验证码一直被视为一个“拦路虎”。很多测试人员在做接口或UI自动化时都会遇到验证码的阻碍&#xff0c;导致测试无法继续进行。今天&#xff0c;我们就来讨论如何在自动化过程中破解验证码&#xff0c;快速绕过这道关卡&#xff0c;轻松完成自…

LVM硬盘挂载

LVM硬盘挂载 一、基础概念 sda/sdb/nvme0n1/nvme0n2&#xff1a; 硬盘的命名方式&#xff0c;中括号的字母为第三位按不同硬盘的加载顺序排序。sda1/sda2/sdb1&#xff1a; 第4位为分区号&#xff0c;数字为不同分区的依序命名lvm: LVM是一种逻辑卷管理器&#xff0c;允许管理…

黑马头条day1 环境搭建 SpringCloud微服务(注册发现,服务调用,网关)

Nacos 环境搭建 Vmvare打开已经安装好的虚拟机镜像环境 使用findshell作为链接工具 和MobaXterm差不多 初始工程搭建 项目导入到idea 里边 这个项目都是用的比较老的东西 jdk1.8 甚至把仓库也提供好了 主体机构 common 就是通用的配置 feign 是对外的接口 model …

css五种定位总结

在 CSS 中&#xff0c;定位&#xff08;Positioning&#xff09;主要有五种模式&#xff0c;每种模式的行为和特点不同&#xff0c;以下是 static、relative、absolute、fixed 和 sticky 五种定位方式的对比总结&#xff1a; 1. static&#xff08;默认定位&#xff09; 特性…

“中秋快乐”文字横幅的MATLAB代码生成

中秋快乐呀朋友们&#xff01;&#xff01;&#xff01; 给大家带来一个好玩的代码&#xff0c;能够生成“中秋快乐”的横幅文字&#xff0c;比较简单&#xff0c;当然你也可以根据自己的需求去更改文字和背景&#xff0c;废话不多说&#xff0c;直接展示。 文字会一直闪烁&…

计算机毕业设计 基于SpringBoot框架的网上蛋糕销售系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

基于Springboot+vue的音乐网站

随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了音乐网站的开发全过程。通过分析音乐网站管理的不足&#xff0c;创建了一个计算机管理音乐网站的方案。文章介绍了音乐网站的系统分析部分&#xff0c;包括可行性分析…

如何在Mac上安装多个Python环境

如何在Mac上安装多个Python环境 简介 在你的Mac上使用多个Python环境可以对项目管理很有帮助,特别是在同时处理不同Python版本或不同的包需求时。在这篇文章中,我们将向你展示如何在Mac上轻松地安装和管理多个Python环境。 一. 安装Conda Conda是一个包管理和环境管理系统…

深度学习 之 常见损失函数简介:名称、作用及用法

引言 在机器学习和深度学习中&#xff0c;损失函数&#xff08;Loss Function&#xff09;是模型训练过程中一个不可或缺的部分。它用来度量模型预测结果与真实值之间的差异&#xff0c;从而指导模型参数的优化。合理选择损失函数对于提高模型的准确性和泛化能力至关重要。本文…

代码随想录训练营第36天|二维背包

1049. 最后一块石头的重量 II class Solution { public:int lastStoneWeightII(vector<int>& stones) {int sumaccumulate(stones.begin(),stones.end(),0);int targetsum/2;vector<int> dp(target1,0);for(auto& stone: stones){for(int itarget; i>s…

Ansible——Playbook基本功能

文章目录 一、Ansible Playbook介绍1、Playbook的简单组成1&#xff09;“play”2&#xff09;“task”3&#xff09;“playbook” 2、Playbook与ad-hoc简单对比区别联系 3、YAML文件语法&#xff1a;1. 基本结构2. 数据类型3. 列表4. 字典&#xff08;映射&#xff09;5. 注释…

免费表格图片识别成表格小工具

自动提取图片中的文字&#xff0c;并按照表格的格式整理好 需要的自取吧&#xff0c;下载地址&#xff1a;https://pan.quark.cn/s/f4b1ac62b808