计算机系统应用教程网站

网站首页 > 技术文章 正文

若依框架学习:带你详细剖析SpringSecurity的登录认证实现

btikc 2024-09-29 09:56:48 技术文章 17 ℃ 0 评论

若依框架中登录认证使用的是SpringSecurity安全管理框架,这个框架想必很多JAVA开发的朋友是比较熟悉的了,就算没有用过也听过,它和另外一个安全管理框架Shiro是java web开发中安全管理方案中常用的两个框架。若依并不会自己再去造一个轮子做权限管理,它也是直接集成了SpringSecurity这个框架,其实在若依中对SpringSecurity的配置比较简单,正是因为简单,所以本文就以若依为例子,一步一步带大家详细的了解SpringSecurity框架在项目中的应用

SpringSecurity整体上其实就是有两大模块,分别是认证和鉴权。为了实现这两个功能, SpringSecurity内部有一系列的组件,正是这些组件可以让SpringSecurity配置起来既简单又灵活。磨刀不误砍柴工,在剖析若依的认证原理之前,首先大概了解一下SpringSecurity的几个重要组件。

AuthenticationManager 认证管理器

SecurityContextHolder 安全上下文,可以理解成线程变量,底层就是基于ThreadLocal实现。

Authentication 认证结果接口

PasswordEncoder 密码编码器

UserDetailsService 用户认证接口

UserDetails 用户对象接口

AccessDeniedHandler 授权失败处理器

AuthenticationEntryPoint 认证失败处理器

LogoutSuccessHandler 退出成功处理器

了解完这些组件后,我们先看一下若依中对于用户认证这一部分的流程:

按这个流程图,我们一步一步看代码中是如何使用SpringSecurity实现用户认证的。首先看若依中对SpringSecurity的配置,在ruoyi-framework中的SecurityConfig.java这个配置类:

  1. 配置认证失败回调
  2. 配置鉴权失败回调
  1. 配置退出登录成功回调
  2. 配置jwt token验证过滤器
  3. 配置跨域过滤器
  4. 配置退出过滤器
  5. 配置密码编码器
  6. 配置自定义用户认证服务
  7. 配置认证密码编码器

其中2、5、6、7 是最核心的,这些组件都有各自的具体实现。接着我们看框架中是如何进行认证的。首先认证肯定就需要登录,所以入口肯定在登录的地方开始。

这个是登录的入口代码,登录成功后给前端返回一个token,接着往下看 loginService.login 这个方法的具体实现:

箭头部分就是具体的认证,这里就是使用SpringSecurity框架的认证服务入口。这一句代码看起来很简单,其实内部是各种接口委托完成的,我梳理了大概流程是这样的:

1、AuthenticationManager.authenticate() 内部会委托 ProviderManager.authenticate()

2、ProviderManager 内部又委托 AuthenticationProvider.authenticate()

3、AuthenticationProvider 的实现类 AbstractUserDetailsAuthenticationProvider 实现了认 authenticate接口,并新定义了两个抽象方法retrieveUser和additionalAuthenticationChecks 供子类去实现。

4、DaoAuthenticationProvider 继承了 AbstractUserDetailsAuthenticationProvider 并重写了其两个抽象方法retrieveUser,additionalAuthenticationChecks,其中retrieveUser内部又委托 UserDetailsService.loadUserByUsername 实现认证返回UserDetails

5、用户自定义UserDetailsService接口实现

按照这个流程,再继续往下看源码截图

ProviderManager 接口定义

AbstractUserDetailsAuthenticationProvider是一个抽象类,实现了ProviderManager

  1. retrieveUser 进行用户验证
  2. additionalAuthenticationChecks进行密码验证


DaoAuthenticationProvider部分代码

  1. 设置默认密码编码器
  2. 实现密码匹配

DaoAuthenticationProvider 继承 AbstractUserDetailsAuthenticationProvider 重写父类两个抽象方法。箭头地方委托 UserDetailsService.loadUserByUsername() 实现具体的认证逻辑

最终这个UserDetailsServiceImpl就是若依自定义的UserDetailsService接口实现类,主要逻辑就是验证用户是否存在数据库中,没有则抛出异常,否则返回一个UserDetails对象。密码验证则在 AbstractUserDetailsAuthenticationProvider中已经有默认实现。若依中LoginUser就是实现了UserDetails接口的类

上面就是SpringSecurity的认证逻辑过程,可以看到最终需要调用UserDetailsServiceImpl这个自己实现的类,因为在这个类里面可以根据用户名查询数据库用户是否存在从而完成真正的认证。通过框架认证成功后,最终返回LoginUser(其实就是UserDetails),接下来就是需要应用端自行保存这个用户信息了。

这里就是使用这个认证通过的用户创建一个jwt token返回给前端

creareToken这个方法主要有三个逻辑:

  1. 生成一随机唯一id
  2. 以这个id为key,将整个LoginUser对象存储到redis中(refreshToken方法)
  3. 正常创建token,token中的信息载体为第一步生成的id号

接着往下看 refreshToken方法

可以看到,设置失效时间后,将整个对象存入到redis中。接下来,就可以通过过滤器进行token的刷新了。在若依中是在 JwtAuthenticationTokenFilter 这个过滤器实现的

  1. 根据请求头获取当前token,通过token从redis中获取当前用户
  2. 验证当前用户是否快要过期则刷新过期时间
  3. 将当前用户设置到当前线程中,供后续的过滤器使用

到这里,若依中的用户认证具体实现就全部剖析完了,整个代码的实现其实也很简单,只不过若依对这个token做了一个封装以redis作为存储。认证通过后,接下来就是鉴权了,因为篇幅有限,下期和大家继续分享若依中是如何基于SpringSecurity实现用户鉴权的。

创作不易,如果觉得文章对你有用,请麻烦动动您灵活的手指点赞关注!

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表