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

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);}@Overridepublic Object getCredentials() {return this.credentials;}@Overridepublic 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);}@Overridepublic void eraseCredentials() {super.eraseCredentials();credentials = null;}
}

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

@Component
public class CasAuthenticationProvider implements AuthenticationProvider {@Autowiredprivate UserDetailsService userDetailsService;public CasAuthenticationProvider(UserDetailsService userDetailService) {this.userDetailsService = userDetailService;}@Overridepublic 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;}@Overridepublic 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.rhkb.cn/news/461012.html

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

相关文章

如何解决品牌商和经销商对接的难题

本文分享知名啤酒商大品牌公司如何和经销商的数据进行对接 某啤酒商业务场景&#xff1a;品牌商例如某啤酒商需要推广自己的订货商城助力全国的各大经销商提高营销竞争力 如何解决核心问题之一&#xff1a;商城打通ERP&#xff1f; 根据调研&#xff0c;经销商ERP系统分布情况&…

书生-第四期闯关:完成SSH连接与端口映射并运行hello_world.py

端口映射完成后&#xff0c;访问127.0.0.1&#xff1a;7860成功展示如下界面&#xff1a; 书生浦语大模型实战营 项目地址&#xff1a;https://github.com/InternLM/Tutorial/

ValueError: Object arrays cannot be loaded when allow_pickle=False

文章目录 问题解决方法1&#xff1a;allow_pickleTrue解决方法2&#xff1a;降低numpy版本错误原因&#xff1a;python和numpy版本不兼容 问题 Traceback (most recent call last): File “D:\project\test_st\retrieval\read_npy.py”, line 4, in data np.load(‘mosi0__le…

聆听用户声音的3个方法,挖掘客户真实潜在需求

聆听用户反馈&#xff0c;去挖掘用户真实需求的重要性是企业非常重要的意向工作之一&#xff0c;因为它直接关联到企业的产品开发、市场定位、客户满意度以及最终的商业成果。了解并满足用户的真实需求可以帮助企业创造更有价值的产品和服务&#xff0c;提升用户体验&#xff0…

docker离线安装达梦数据库

文章目录 下载达梦数据库docker镜像上传DM8镜像文件将DM8镜像导入到本地docker镜像仓库中查看本地docker镜像仓库是否存在DM8镜像带参数启动DM8docker启动DM8默认用户名/密码 下载达梦数据库docker镜像 达梦数据库官网 https://www.dameng.com/ 点击下载中心&#xff0c;选择D…

产品宣传册制作成电子产品宣传册用什么软件?

​随着科技的飞速发展&#xff0c;电子产品已经渗透到我们生活的方方面面&#xff0c;电子宣传册作为一种新兴的传播媒介&#xff0c;受到企业的青睐。将传统的纸质产品宣传册制作成电子宣传册&#xff0c;不仅能够降低成本、提高传播效率&#xff0c;还能更好地满足消费者的阅…

linux之网络子系统- TCP连接建立过程 三次握手四次挥手

一、相关实际问题 为什么服务端程序都需要先listen一下半连接队列和全连接队列长度如何确定“Cannot assign requested address”这个报错是怎么回事一个客户端端口可以同时用在两条连接上吗服务端半/全连接队列满了会怎么样新连接的soket内核对象是什么时候建立的建立一条TCP…

[JAVAEE] 多线程的案例(四) - 定时器

目录 一. 什么是定时器? 二. java中的定时器类 三. 定时器的简单使用. 四. 模拟实现定时器 4.1 实现 MyTimerTask 4.2 实现 MyTimer 一. 什么是定时器? 定时器相当于闹钟, 时间到了就执行一些逻辑. 二. java中的定时器类 使用Timer类实例化一个定时器对象. Timer类中的…

Java调用chatgpt

目前openai的chatgpt在国内使用有一定难度&#xff0c;不过国内的大模型在大部分情况下已经不弱于chatgpt&#xff0c;而且还更便宜&#xff0c;又能解决国内最敏感的内容安全问题。本文后续以spring ai调用国内chatgpt厂商实现为例&#xff0c;讲解怎么构建一个java调用chatgp…

海外云手机是什么?对外贸电商有什么帮助?

在外贸电商领域&#xff0c;流量引流已成为卖家们关注的核心问题。越来越多的卖家开始利用海外云手机&#xff0c;通过TikTok等社交平台吸引流量&#xff0c;以推动商品在海外市场的销售。那么&#xff0c;海外云手机到底是什么&#xff1f;它又能为外贸电商卖家提供哪些支持呢…

uniapp 底部导航栏tabBar设置后不显示的问题——已解决

uniapp 底部导航栏tabBar设置后不显示的问题——已解决 网上找了一堆解决办法&#xff0c;挨个对着试吧 解决办法一&#xff1a;tabBar里的list第一项和page中的第一项要相同&#xff0c;确实就能显示了。但是问题来了&#xff0c;page中的第一项是入口页&#xff0c;那就意味…

【AI开源项目】OneAPI -核心概念、特性、优缺点以及如何在本地和服务器上进行部署!

本文将深入探讨OneAPI的核心概念、特性以及如何在本地和服务器上进行部署&#xff0c;帮助开发者更高效地利用这一强大的工具。 文章目录 什么是OneAPI&#xff1f;OneAPI的核心特性 OneAPI的优势与缺点OneAPI的安装与使用教程1. OneAPI的本地构建1.1 下载源代码1.2 构建前端1…

什么是x86架构,什么是arm架构

什么是 x86 架构&#xff1f; x86 架构是一种经典的指令集架构&#xff08;ISA&#xff09;&#xff0c;最早由英特尔在 1978 年推出&#xff0c;主要用于 PC、服务器等领域。 它是一种复杂指令集计算&#xff08;CISC&#xff09;架构&#xff0c;支持大量的复杂指令和操作&…

基于单片机的智能家居排气扇系统设计

1系统方案设计 本设计基于单片机的智能家居排气扇系统采用STM32单片机作为主控制器&#xff0c;通过DHT11温湿传感器和MQ-2烟雾传感器实现温度、湿度、烟雾检测&#xff0c;在自动模式下&#xff0c;可以根据烟雾浓度通过PWM调速的方式自动调节排气扇的速度&#xff0c;而在手动…

C++学习笔记3——存储持续性、作用域和链接性

1. 存储持续性 自动存储持续性&#xff1a;在函数中定义或声明的变量存储持续性为自动的&#xff0c;它们在程序开始执行其所属的函数或代码块时被创建&#xff0c;在执行完函数或代码块时&#xff0c;使用的内存被释放&#xff1b; 静态存储持续性&#xff1a;在函数定义外定义…

ios Framework版本号的问题。

自己创建的framework和普通的app的版本号设置的地方是有所有不同的。 framework 的版本号是在 TARGETS -> Build Settings -> current Project Version 这个地方设置的&#xff0c; 在创建framework的时候xcode 会自动创建一个framework.h的文件名&#xff0c;framewo…

Linux 开机自动挂载硬盘

在日常使用 Linux 系统的过程中&#xff0c;我们可能需要挂载一些机械硬盘或者移动硬盘来存储数据。手动挂载虽然简单&#xff0c;但每次重启后都需要重新操作&#xff0c;未免有些繁琐。那么&#xff0c;如何让硬盘在开机时自动挂载呢&#xff1f;本篇博客将详细介绍如何通过配…

SSRF-pikachu

系列目录 第一章 暴力破解 第二章 Cross-Site Scripting-pikachu 第三章 CSRF 第四章 sql-injection 第五章 RCE 第六章 File inclusion 第七章 Unsafe filedownload 第八章 Unsafe fileupload 第九章 Over Permission 第十章 ../../ 第十一章 敏感信息泄露 第十二…

配电柜弧光保护装置的应用与功能

随着配电系统复杂度的提升&#xff0c;电弧故障可能带来的高温与巨大电磁冲击对系统及人员的安全构成了威胁。弧光保护装置因其快速识别和切断故障的能力&#xff0c;成为现代配电系统中不可或缺的一部分。本文将结合ARB5系列弧光保护装置的设计&#xff0c;对弧光保护的工作原…

12-Docker发布微服务

12-Docker发布微服务 Docker发布微服务 搭建SpringBoot项目 新建一个SpringBoot项目 选择依赖项Spring Web和Spring Boot Actuator 在com.qi.docker_boot下创建controller目录&#xff0c;并在该目录下创建OrderController的java类 OrderControllerjava类的内容如下&#xf…