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

news2024/12/23 18:46:53

         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")
    @ResponseBody
    public 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
    }




    @Override
    public Object getCredentials() {
        return null;
    }

    @Override
    public Object getPrincipal() {
        return this.principal;
    }

    @Override
    public 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);
    }

    @Override
    public 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);
    }


    @Override
    public 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);
    }

    @Nullable
    protected 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 {

    @Autowired
    private UserDetailsService userDetailsService;


    @Override
    public 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());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return (SmsAuthenticationToekn.class.isAssignableFrom(authentication));
    }

}


        4)UserDetilsService

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

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

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public 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, token
        StaticCredentialProvider 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 Client
        AsyncClient 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 request
        SendSmsRequest 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 request
        CompletableFuture<SendSmsResponse> response = client.sendSms(sendSmsRequest);
        // Synchronously get the return value of the API request
        SendSmsResponse 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 exceptions
            System.out.println(throwable.getMessage());
            return null;
        });*/

        // Finally, close the client
        client.close();
        return resp;
    }
}

 6)发送短信

        也算是成功了吧

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

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

相关文章

心觉:成功学就像一把刀,有什么作用关键在于使用者(一)

Hi&#xff0c;我是心觉&#xff0c;与你一起玩转潜意识、脑波音乐和吸引力法则&#xff0c;轻松掌控自己的人生&#xff01; 挑战每日一省写作173/1000天 很多人觉得成功学是鸡汤&#xff0c;是没用的&#xff0c;甚至是骗人的 我先保持中立&#xff0c;不知道对不对 我们先…

实习项目|苍穹外卖|day11

Apache ECharts 前端技术。 营业额统计 还是比较简单的。 用户统计 订单统计 以上所有需求。难点在于对时间类的处理&#xff1a; // 接收格式 GetMapping("/turnoverStatistics")ApiOperation("营业额统计")public Result<TurnoverReportVO>…

游戏开发引擎__游戏场景(灯光,摄像机)

1.灯光 重要参数介绍 类型: 控制灯光的类型&#xff0c;有“定向”“点”“区域”和“聚光”4种模式。颜色: 控制灯光的颜色。模式: 控制灯光的光照模式&#xff0c;有“实时”“混合”和“烘焙”3种模式。强度: 控制灯光的明亮程度。间接乘数: 改变间接光的强度。阴影类型: …

使用梯度下降法实现多项式回归

原文链接:使用梯度下降法实现多项式回归 - 柒墨轩 - 博客园 使用梯度下降法实现多项式回归 实验目的 本实验旨在通过梯度下降法实现多项式回归,探究不同阶数的多项式模型对同一组数据的拟合效果,并分析样本数量对模型拟合结果的影响。 实验材料与方法 数据准备 生成训练…

C# 离线激活码的实现方式

一、简介 离线激活码是一种在软件、游戏、应用程序或其他数字产品领域中常用的授权方式&#xff0c;旨在确保产品的合法使用并维护开发者的权益。当用户购买或获得这些产品的使用权后&#xff0c;开发者会提供一个唯一的、一次性的激活码给用户。与在线激活不同&#xff0c;离…

java工具安装教程

提示:先安装软件打开后关闭&#xff0c;在执行魔法操作 解压后会多个文件夹&#xff0c;从文件夹打开 要魔法哪款软件就打开对应的魔法脚本 比如&#xff1a;idea就运行idea魔法 点击打开 显示下面弹窗则成功&#xff0c;点击确定即可 打开IDEA查看&#xff1a;

51单片机-直流电机(PWM:脉冲宽度调制)实验-会呼吸的灯直流电机调速

作者&#xff1a;Whappy&#xff08;菜的扣脚&#xff09; 脉冲宽度调制&#xff08;Pulse Width Modulation&#xff0c;PWM&#xff09;是一种通过调节信号的占空比来控制功率输出的技术。它主要通过改变脉冲信号的高电平持续时间相对于低电平的时间来调节功率传递给负载的量…

上市公司-客户ESG数据集(dta+xlsx+参考文献)(2009-2023年)

参考《经济问题》中李普玲&#xff08;2024&#xff09;的做法&#xff0c;将供应商与主要客户数据对应起来&#xff0c;并对上市公司及关联上市公司的ESG数据进行匹配&#xff0c;形成“供应商——客户ESG”的数据集&#xff0c;保留客户的销售占比 一、数据介绍 数据名称&am…

标准管理系统Vue项目

系列文章目录 第一章 基础知识、数据类型学习 第二章 万年历项目 第三章 代码逻辑训练习题 第四章 方法、数组学习 第五章 图书管理系统项目 第六章 面向对象编程&#xff1a;封装、继承、多态学习 第七章 封装继承多态习题 第八章 常用类、包装类、异常处理机制学习 第九章 集…

Springboot与minio:

一、介绍 Minio是一个简单易用的云存储服务&#xff0c;它让你可以轻松地把文件上传到互联网上&#xff0c;这样无论你在哪里&#xff0c;只要有网络&#xff0c;就能访问或分享这些文件。如果你想要从这个仓库里取出一张图片或一段视频&#xff0c;让网站的访客能看到或者下载…

硬件体系架构的学习

硬件体系架构的学习 RISC全称Reduced Instruction Set Compute&#xff0c;精简指令集计算机&#xff1b; CISC全称Complex Instruction Set Computers&#xff0c;复杂指令集计算机。 SOC片上系统概念 System on Chip&#xff0c;简称Soc&#xff0c;也即片上系统。从狭义…

Spark-ShuffleWriter-UnsafeShuffleWriter-钨丝内存分配

一、上下文 《Spark-ShuffleWriter-UnsafeShuffleWriter》中提到在进行Page内存分配时&#xff0c;调用了一行代码 MemoryBlock page memoryManager.tungstenMemoryAllocator().allocate(acquired); 这里就会走MemoryManager的钨丝内存分配&#xff0c;下面我们来详细看下 …

python运行时错误:找不到fbgemm.dll

python运行时错误&#xff1a;找不到fbgemm.dll 报错&#xff1a; OSError: [WinError 126] 找不到指定的模块。 Error loading "D:\program\py\312\Lib\site-packages\torch\lib\fbgemm.dll" or one of its dependencies. 原因是Windows下缺失&#xff1a;libomp140…

Mastering openFrameworks_第十一章_网络

网络 网络为多个设备之间的数据交换提供了一种方式。它是一个主要组成部分&#xff0c;允许远程控制移动和平板设备应用程序中的一些参数&#xff0c;也用于使交互式项目在多台计算机上同步工作。在本章中&#xff0c;您将学习如何在openFrameworks项目中实现和使用OSC和TCP协…

BrainSegFounder:迈向用于神经影像分割的3D基础模型|文献速递--Transformer架构在医学影像分析中的应用

Title 题目 BrainSegFounder: Towards 3D foundation models for neuroimagesegmentation BrainSegFounder&#xff1a;迈向用于神经影像分割的3D基础模型 01 文献速递介绍 人工智能&#xff08;AI&#xff09;与神经影像分析的融合&#xff0c;特别是多模态磁共振成像&am…

系统安装CH384串口卡驱动

1. 解压驱动文件CH38XDRV.tar&#xff0c;并进入驱动目录 cd CH38XDRV/DRV_28S/LINUX/driver$ 2. 编译 sudo make edgeedge-PC:~/CH38XDRV/DRV_28S/LINUX/driver$ sudo make 请输入密码: 验证成功 make -C /lib/modules/4.19.0-arm64-desktop/build M/home/edge/CH38XDRV/DRV…

2024年【四川省安全员B证】新版试题及四川省安全员B证考试试卷

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 四川省安全员B证新版试题参考答案及四川省安全员B证考试试题解析是安全生产模拟考试一点通题库老师及四川省安全员B证操作证已考过的学员汇总&#xff0c;相对有效帮助四川省安全员B证考试试卷学员顺利通过考试。 1、…

数据库事务的详解

1、 介绍 什么是事务? 事务是一个原子操作。是一个最小执行单元。可以由一个或多个SQL语句组成&#xff0c;在同一个事务当中&#xff0c;所有的SQL语句都成功执行时&#xff0c;整个事务成功&#xff0c;有一个SQL语句执行失败&#xff0c;整个事务都执行失败。(一组操作同时…

计算机人工智能前沿进展-大语言模型方向-2024-09-14

计算机人工智能前沿进展-大语言模型方向-2024-09-14 1. Multimodal learning using large language models to improve transient identification of nuclear power plants B Qi, J Sun, Z Sui, X Xiao, J Liang - Progress in Nuclear Energy, 2024 使用大型语言模型进行多…

Html在线编辑器

Html在线编辑器提供富文本编辑器,在线文章编辑器,富文本编辑器,Html在线编辑器使用&#xff0c;具有高级功能的Html在线编辑器可全屏编辑,Web版Html在线编辑器在线使用,文章,网站编辑,微信公众号可以在线使用编辑器功能等。