SpringBoot3+SpringSecurity6基于若依系统整合自定义登录流程

news2024/11/24 12:30:53

SpringBoot3+SpringSecurity6基于若依系统整合自定义登录流程

问题背景

在做项目时遇到了要对接统一认证的需求,但是由于框架的不兼容性(我们项目是springboot3,jdk17,springsecurity6.1.5)等因素,不得不使用抽离服务的方法,将统一认证服务抽出去形成一个中介系统,统一认证服务使用jdk8的版本,这样可以兼容统一认证服务器,认证流程图大致如下:

在这里插入图片描述

也就是说,我们的任务变成了这样:中介系统会传入一个工号和一个密钥,主系统需要解密密钥并且将根据工号直接登录。一开始我想直接在这个第三方登录方法中就解密密钥,然后复用正常的登录逻辑,正常的登录逻辑是通过工号和密码,框架才会颁发token,密码可以直接从数据库里查出来,所以直接一股脑塞进去:

在这里插入图片描述

但是我们发现框架底层的鉴权流程是:将传入的密码加密,然后查询数据库中存储的密码,而数据库存储的密码本身就是加密后的,相当于匹配这两个字符串是否一致即可(因为这个加密方法是无法逆向解密的)。

这也就是说明我们如果直接从数据库查询密码塞到token里是不行的,因为框架会将你传入的密码再加密一次,就是原来的值了。

在网上看到有说在密码前加入{noop}就可以使得框架不对密码进行加密,但是我尝试后发现没有效果…

没办法,这时候只能自己去实现一个登录流程了。

尝试

在网上查阅了相关文章,找到了SpringSecurity中正常使用账号密码登录走的流程吧:

在这里插入图片描述

根据这个认证过程,我们应该可以大致去效仿一下:

首先我们提供一个实现了AbstractAuthenticationProcessingFilter抽象类的过滤器,用来代替UsernamePasswordAuthenticationFilter逻辑,然后提供一个AuthenticationProvider实现类代替AbstractUserDetailsAuthenticationProvider或DaoAuthenticationProvider,最后再提供一个UserDetailsService实现类。

大致流程是这样。但是别忘了,我们这是springboot3,网上大部分文章都是springboot2的,我们很难找到参考…

因为我们这个项目本身是想整合cas登录的,所以后面很多鉴权逻辑类的起名都跟cas有关。

照猫画虎,根据框架的UsernamePasswordAuthenticationToken我们也自定义了CasAuthenticationToken,大致就是把字段改改,剩下就是完全照搬。

public class CasAuthenticationToken  extends AbstractAuthenticationToken {

    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
    //对应工号
    private final Object principal;
    //对应验证码
    private Object credentials;

    public CasAuthenticationToken(String empId, Object credentials){
        super(null);
        this.principal = empId;
        this.credentials = credentials;
        setAuthenticated(false);
    }

    public CasAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities, Object credentials){
        super(authorities);
        this.principal = principal;
        this.credentials = credentials;
        super.setAuthenticated(true);
    }

    @Override
    public Object getCredentials() {
        return this.credentials;
    }

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

    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        if (isAuthenticated) {
            throw new IllegalArgumentException(
                    "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
        }

        super.setAuthenticated(false);
    }


    @Override
    public void eraseCredentials() {
        super.eraseCredentials();
        credentials = null;
    }
}

然后需要写一个Provider,参考SpringSecurity的AbstractUserDetailsAuthenticationProvider,我们也可以照猫画虎一下。注意一下:Provider里面的authenticate方法是鉴权核心代码,比如我们将如何判断密钥合法、根据工号查询用户信息及是否有这个用户等,这里的逻辑需要你们去个性化实现。

@Component
public class CasAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private UserDetailsService userDetailsService;

    public CasAuthenticationProvider(UserDetailsService userDetailService) {
        this.userDetailsService = userDetailService;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        CasAuthenticationToken token = (CasAuthenticationToken) authentication;
        String empId = (String)token.getPrincipal();
        //首先,验证验证码是否正确
        String code = (String)token.getCredentials();
        //这里应该写一下如何解密的逻辑
       -----------------
        //然后,查询对应用户
        UserDetails user = userDetailsService.loadUserByUsername(empId);
        if (Objects.isNull(user)) {
            throw new InternalAuthenticationServiceException("根据工号:" + empId + ",无法获取对应的用户信息!");
        }
        CasAuthenticationToken authenticationResult = new CasAuthenticationToken(user, user.getAuthorities(), token.getCredentials());
        authenticationResult.setDetails(token.getDetails());
        return authenticationResult;
    }

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

值得注意的是,返回新的CasAuthenticationToken中第一个参数user信息要是一个对象,这样便于后面解析成LoginUser对象。

看网上大部分文章还需要配置专门的第三方过滤器、专门的登录成功类和登录失败类,一开始我也根据这个创了好多,最后打断点才发现根本都没进到这些代码里来。

大部分文章还要求在SpringSecurity配置类配置下:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .antMatchers("/error","/login/**","/login/goLogin","/login/doLogin","/login/code","/login/authorization_code").anonymous()
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .loginPage("/login/goLogin")
            .loginProcessingUrl("/login/doLogin")
            .failureUrl("/login/error")
            .permitAll()
            .successHandler(new QriverAuthenticationSuccessHandler("/index/toIndex"));

	//这里我们省略了一些配置 ……

	//应用前面定义的配置
    http.apply(thirdAuthenticationSecurityConfig);
}

但是我的项目是springboot3+springsecurity6,不能这么写,已经从configure方法变成了filterChain方法,像一个链子一样调用不同的过滤器来实现统一过滤。

看到过滤器链里有这个UsernamePasswordAuthenticationFilter,我也不假思索的觉得应该弄一个CasAuthenticationFilter并且插入进去,但是不知为什么弄完后,填入过滤链项目就无法启动了,报错:The Filter class org.**.ThirdAuthenticationFilter does not have a registered order…网上搜了一下说加@Order的仍然还是一样报错。哎,那还是注掉吧

在这里插入图片描述

嗯,那就算了?要不就先这样试试能不能成功

满怀期待的启动项目测试,结果报错:No AuthenticationProvider found for org.***.CasAuthenticationToken

根据资料显示,是由于我定义了一个新的AuthenticationToken,在

authenticationManager.authenticate(authenticationToken);

时候发现authenticationManager的parent属性是null,他不知道如何对我们这个自定义的token进行鉴权。哎不对啊,我前面明明写了个Provider为什么还不行!

看一些可能是SpringBoot2的文章说要弄filter,但是我弄了但是根本不走代码,放到SecurityConfig里又报错无法启动项目。应该是不同版本写法已经不一样了。

其实首要问题就是AuthenticationManager的parent属性是null,应该从这里入手。但是我走错方向了,不停的在SecurityConfig配置上、过滤器上改…

后面我想到了这个AuthenticationManager,偶然在SecurityConfig看到了:

在这里插入图片描述

还记得账号密码怎么登录的吗,他用的DaoAuthenticationProvider!这里他给ProviderManager注入了!

点进去看了一下,他可以注入多个Provider!那我们直接把CasAuthenticationProvider注入!
在这里插入图片描述

那我们就可以照猫画虎!注入!

在这里插入图片描述

最后测试一下,能成功返回token了!

参考文章:

一文理清SpringSecurity中基于用于名密码的登录认证流程-腾讯云开发者社区-腾讯云 (tencent.com)

SpringSecurity自定义多Provider时提示No AuthenticationProvider found for问题的解决方案与原理(二)-CSDN博客

SpringSecurity系列 之 集成第三方登录(包括默认的用户名密码、短信验证码和github三种登录方式)_springsecurity第三方登录-CSDN博客

Spring Security authenticationManager()返回null,必须定义authenticationManagerBean的原因分析-CSDN博客

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

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

相关文章

WPF+MVVM案例实战(十四)- 封装一个自定义消息弹窗控件(下)

文章目录 1、案例效果2、弹窗空间使用1.引入用户控件2、按钮命令实现 3、总结4、源代码获取 1、案例效果 2、弹窗空间使用 1.引入用户控件 打开 Wpf_Examples 项目&#xff0c;在引用中添加用户控件库&#xff0c;在 MainWindow.xaml 界面引用控件库&#xff0c;代码如下&…

【论文精读】ID-like Prompt Learning for Few-Shot Out-of-Distribution Detection

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;论文精读_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 注&#xff1a;下文…

PIDNet(语义分割)排坑

PIDNet训练自己的数据集 1. 前言2. 准备工作3. 配置环境4. 排坑过程4.1.1 configs增加了VOC文件夹 并在里面写了yaml参数文件4.1.2 加载VOC格式数据集的类4.1.3 train.py调试 1. 前言 paper小修时reviewer说baseline太老&#xff0c;所以对CVPR2023的PIDNet进行复现&#xff0…

Google Recaptcha V2 简单使用

最新的版本是v3&#xff0c;但是一直习惯用v2&#xff0c;就记录一下v2 的简单用法&#xff0c;以免将来忘记了 首先在这里注册你域名&#xff0c;如果是本机可以直接直接填 localhost 或127.0.0.1 https://www.google.com/recaptcha/about/ 这是列子 网站密钥&#xff1a;是…

autMan奥特曼机器人-内置Redis

autMan内置了redis服务&#xff0c;有的脚本运行需要redis支持 几个注意事项&#xff1a; 启用redis服务后要重启autMan生效&#xff0c;关闭一样的道理。启用redis服务后会增加约200M的内存占用多个autMan的redis服务可以组成集群redis服务

五、快速入门K8s之Pod容器的生命周期

一、容器的初始化init ⭐️ init c &#xff1a; init contariner 初始化容器&#xff0c;只是用来初始化&#xff0c;初始化完成就会死亡可以大于的等于一也可以没有&#xff0c;每个init只有在前一个init c执行完成后才可以执行下一个、init容器总是运行到成功完成为止&#…

sqoop问题汇总记录

此篇博客仅记录在使用sqoop时遇到的各种问题。持续更新&#xff0c;有问题评论区一起探讨&#xff0c;写得有不足之处见谅。 Oracle_to_hive 1. main ERROR Could not register mbeans java.security.AccessControlException: access denied ("javax.management.MBeanTr…

C++对象模型:Function 语意学

Member 的各种调用方式 Nonstatic Member Function 使用C时&#xff0c;成员函数和非成员函数在性能上应该是等价的。当设计类时&#xff0c;我们不应该因为担心效率问题而避免使用成员函数。 实现&#xff1a;编译器会将成员函数转换为一个带有额外this指针参数的非成员函数…

二叉树中的深搜 算法专题

二叉树中的深搜 一. 计算布尔二叉树的值 计算布尔二叉树的值 class Solution {public boolean evaluateTree(TreeNode root) {if(root.left null) return root.val 0? false: true;boolean left evaluateTree(root.left);boolean right evaluateTree(root.right);return…

【Linux】环境ChatGLM-4-9B 模型部署

一、模型介绍 GLM-4-9B 是智谱 AI 推出的最新一代预训练模型 GLM-4 系列中的开源版本。 在语义、数学、推理、代码和知识等多方面的数据集测评中&#xff0c; GLM-4-9B 及其人类偏好对齐的版本 GLM-4-9B-Chat 均表现出超越 Llama-3-8B 的卓越性能。除了能进行多轮对话&#xf…

深入理解Java 线程并发编排工具: 概述和应用场景

目录 前言概述1. CountDownLatch2. CyclicBarrier3. Semaphore&#xff08;信号量)4. Condition 案例CountDownLatch-马拉松场景CyclicBarrier-马拉松场景Semaphore-公交车占座场景Condition-线程等待唤醒场景 前言 在 Java 的 java.util.concurrent (JUC) 包中&#xff0c;提…

C++初阶(八)--内存管理

目录 引入&#xff1a; 一、C中的内存布局 1.内存区域 2.示例变量存储位置说明 二、C语言中动态内存管理 三、C内存管理方式 1.new/delete操作内置类型 2.new和delete操作自定义类型 四、operator new与operator delete函数&#xff08;重要点进行讲解&#xff09; …

架构的本质之 MVC 架构

前言 程序员习惯的编程方式就是三步曲。 所以&#xff0c;为了不至于让一个类撑到爆&#x1f4a5;&#xff0c;需要把黄色的对象、绿色的方法、红色的接口&#xff0c;都分配到不同的包结构下。这就是你编码人生中所接触到的第一个解耦操作。 分层框架 MVC 是一种非常常见且常…

Node学习记录-child_process 子进程

来自&#xff1a;https://juejin.cn/post/7277045020422930488 child_process用于处理CPU密集型应用&#xff0c;Nodejs创建子进程有7个API&#xff0c;其中带Async的是同步API,不带的是异步API child_process.exec(command[, options][, callback]) command:要运行的命令&am…

NVR批量管理软件/平台EasyNVR多个NVR同时管理支持对接阿里云、腾讯云、天翼云、亚马逊S3云存储

随着云计算技术的日益成熟&#xff0c;越来越多的企业开始将其业务迁移到云端&#xff0c;以享受更为灵活、高效且经济的服务模式。在视频监控领域&#xff0c;云存储因其强大的数据处理能力和弹性扩展性&#xff0c;成为视频数据存储的理想选择。NVR批量管理软件/平台EasyNVR&…

2024年编程语言排行榜:技术世界的新星与常青树

随着技术的不断进步&#xff0c;编程语言的流行度也在不断变化。今天&#xff0c;就让我们一起来看看2024年的编程语言排行榜&#xff0c;探索哪些语言在技术世界中占据了主导地位。 1. Python&#xff1a;稳居榜首 Python以其在人工智能、数据科学、网络开发等多个领域的广泛…

MFC工控项目实例二十八模拟量信号每秒采集100次

采用两个多媒体定时器&#xff0c;一个0.1秒计时,另一个用来对模拟量信号采集每秒100次.。 1、在SEAL_PRESSUREDlg.h中添加代码 class CSEAL_PRESSUREDlg : public CDialog { public:CSEAL_PRESSUREDlg(CWnd* pParent NULL); // standard constructor&#xff0e;&#xff0e…

基于MoviNet检测视频中危险暴力行为

项目源码获取方式见文章末尾&#xff01; 600多个深度学习项目资料&#xff0c;快来加入社群一起学习吧。 《------往期经典推荐------》 项目名称 1.【Faster & Mask R-CNN模型实现啤酒瓶瑕疵检测】 2.【卫星图像道路检测DeepLabV3Plus模型】 3.【GAN模型实现二次元头像生…

ArcGIS003:ArcMap常用操作0-50例动图演示

摘要&#xff1a;本文以动图形式介绍了ArcMap软件的基本操作&#xff0c;包括快捷方式创建、管理许可服务、操作界面元素&#xff08;如内容列表、目录树、搜索窗口、工具箱、Python窗口、模型构建器窗口等&#xff09;的打开与关闭、位置调整及合并&#xff0c;设置默认工作目…

NVR批量管理软件/平台EasyNVR多个NVR同时管理支持视频投放在电视墙上

在当今智能化、数字化的时代&#xff0c;视频监控已经成为各行各业不可或缺的一部分&#xff0c;无论是公共安全、交通管理、企业监控还是智慧城市建设&#xff0c;都离不开高效、稳定的视频监控系统的支持。而在这些应用场景中&#xff0c;将监控视频实时投放到大屏幕电视墙上…