SpringSecurity认证流程详解
作者:whyalwaysmea 发布时间:2022-06-25 21:44:49
SpringSecurity基本原理
在之前的文章《SpringBoot + Spring Security 基本使用及个性化登录配置》中对SpringSecurity进行了简单的使用介绍,基本上都是对于接口的介绍以及功能的实现。 这一篇文章尝试从源码的角度来上对用户认证流程做一个简单的分析。
在具体分析之前,我们可以先看看SpringSecurity的大概原理:
SpringSecurity基本原理
其实比较简单,主要是通过一系列的Filter对请求进行拦截处理。
认证处理流程说明
我们直接来看UsernamePasswordAuthenticationFilter
类,
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter
// 登录请求认证
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
// 判断是否是POST请求
if (this.postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
} else {
// 获取用户,密码
String username = this.obtainUsername(request);
String password = this.obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
// 生成Token,
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
this.setDetails(request, authRequest);
// 进一步验证
return this.getAuthenticationManager().authenticate(authRequest);
}
}
}
在attemptAuthentication
方法中,主要是进行username和password请求值的获取,然后再生成一个UsernamePasswordAuthenticationToken 对象,进行进一步的验证。
不过我们可以先看看UsernamePasswordAuthenticationToken 的构造方法
public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
// 设置空的权限
super((Collection)null);
this.principal = principal;
this.credentials = credentials;
// 设置是否通过了校验
this.setAuthenticated(false);
}
其实UsernamePasswordAuthenticationToken是继承于Authentication
,该对象在上一篇文章中有提到过,它是处理登录成功回调方法中的一个参数,里面包含了用户信息、请求信息等参数。
所以接下来我们看
this.getAuthenticationManager().authenticate(authRequest);
这里有一个AuthenticationManager,但是真正调用的是ProviderManager
。
public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Class<? extends Authentication> toTest = authentication.getClass();
AuthenticationException lastException = null;
Authentication result = null;
boolean debug = logger.isDebugEnabled();
Iterator var6 = this.getProviders().iterator();
while(var6.hasNext()) {
AuthenticationProvider provider = (AuthenticationProvider)var6.next();
// 1.判断是否有provider支持该Authentication
if (provider.supports(toTest)) {
// 2. 真正的逻辑判断
result = provider.authenticate(authentication);
}
}
}
这里首先通过provider判断是否支持当前传入进来的Authentication,目前我们使用的是UsernamePasswordAuthenticationToken,因为除了帐号密码登录的方式,还会有其他的方式,比如SocialAuthenticationToken。
根据我们目前所使用的UsernamePasswordAuthenticationToken,provider对应的是DaoAuthenticationProvider。
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
UserDetails user = this.userCache.getUserFromCache(username);
if (user == null) {
cacheWasUsed = false;
// 1.去获取UserDetails
user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);
}
try {
// 2.用户信息预检查
this.preAuthenticationChecks.check(user);
// 3.附加的检查(密码检查)
this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);
} catch (AuthenticationException var7) {
}
// 4.最后的检查
this.postAuthenticationChecks.check(user);
// 5.返回真正的经过认证的Authentication
return this.createSuccessAuthentication(principalToReturn, authentication, user);
}
去调用自己实现的UserDetailsService,返回UserDetails
对UserDetails的信息进行校验,主要是帐号是否被冻结,是否过期等
对密码进行检查,这里调用了PasswordEncoder
检查UserDetails是否可用。
返回经过认证的Authentication
这里的两次对UserDetails的检查,主要就是通过它的四个返回boolean类型的方法。
经过信息的校验之后,通过UsernamePasswordAuthenticationToken
的构造方法,返回了一个经过认证的Authentication。
拿到经过认证的Authentication之后,会再去调用successHandler。或者未通过认证,去调用failureHandler。
认证结果如何在多个请求之间共享
再完成了用户认证处理流程之后,我们思考一下是如何在多个请求之间共享这个认证结果的呢?
因为没有做关于这方面的配置,所以可以联想到默认的方式应该是在session中存入了认证结果。
那么是什么时候存放入session中的呢?
我们可以接着认证流程的源码往后看,在通过attemptAuthentication方法后,如果认证成功,会调用successfulAuthentication,该方法中,不仅调用了successHandler,还有一行比较重要的代码
SecurityContextHolder.getContext().setAuthentication(authResult);
SecurityContextHolder是对于ThreadLocal的封装。 ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据。 更多的关于ThreadLocal的原理可以看看我以前的文章。
一般来说同一个接口的请求和返回,都会是在一个线程中完成的。我们在SecurityContextHolder中放入了authResult,再其他地方也可以取出来的。
最后就是在SecurityContextPersistenceFilter
中取出了authResult,并存入了session
SecurityContextPersistenceFilter也是一个过滤器,它处于整个Security过滤器链的最前方,也就是说开始验证的时候是最先通过该过滤器,验证完成之后是最后通过。
获取认证用户信息
/**
* 获取当前登录的用户
* @return 完整的Authentication
*/
@GetMapping("/me1")
public Object currentUser() {
return SecurityContextHolder.getContext().getAuthentication();
}
@GetMapping("/me2")
public Object currentUser(Authentication authentication) {
return authentication;
}
/**
* @param userDetails
* @return 只包含了userDetails
*/
@GetMapping("/me3")
public Object cuurentUser(@AuthenticationPrincipal UserDetails userDetails) {
return userDetails;
}
来源:https://blog.csdn.net/u013435893/article/details/79605239


猜你喜欢
- 一:需求详情:OpenOffice.org 是一套跨平台的办公室软件套件,能在 Windows、Linux、MacOS X (X11)、和
- 一、效果图:二、导入 jar 包1.由于这是大神写好封装起来的一个框架,所有我们使用前得先下载相关的 jar 包第一种:maven<!
- 前言上一篇 文章我们完成了在 thymeleaf 模板引擎中使用 shiro 标签,也就是根据不同的用户身份信息,前端页面来显示不同的页面内
- 在需要线程同步的时候如何选择合适的线程锁?例:选择可以存入到常量池当中的对象,String对象等public class SyncTest{
- Junit中的基本注解:@Test:使用该注解标注的public void方法会表示为一个测试方法;@BeforeClass:表示在类中的任
- 前言C# 语言是在2000发布的,至今已正式发布了7个版本,每个版本都包含了许多令人兴奋的新特性和功能更新。同时,C# 每个版本的发布都与同
- 在Android中要让一个程序的界面始终保持一个方向,不随手机方向转动而变化的办法: 只要在AndroidManifest.xml里面配置一
- 本文实例为大家分享了Android简单实现文件下载的具体代码,供大家参考,具体内容如下权限<!-- 文件读写权限 &nbs
- 前言上周末我们一起分析了ArrayList的源码并进行了一些总结,因为最近在看Collection这一块的东西,下面的图也是大致的总结了Co
- 本文实例讲述了Android编程实现启动界面的方法。分享给大家供大家参考,具体如下:最近在弄一个程序启动界面程序,在这里贴下代码。解释一下:
- 这篇文章主要介绍了Java Collection集合iterator方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的
- 实现过滤器和 * 首先,我们先来看一下二者在 Spring Boot 项目中的具体实现,这对后续理解二者的区别有很大的帮助。a) 实现过滤器
- 前言随着使用es场景的增多,工作当中避免不了去使用es进行数据的存储,在数据存储到es当中以后就需要使用DSL语句进行数据的查询、聚合等操作
- springboot启动失败的问题springboot版本是1.3.0.M1,连接的mysql版本为8,用spring-boot-start
- 桥接模式桥接模式是将抽象部分与它的实现部分分离,使他们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)
- 这篇文章主要介绍了Java获取时间打印到控制台代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋
- [LeetCode] 205. Isomorphic Strings 同构字符串Given two strings s
- 1: Nacos搭建可以参考 https://www.jb51.net/article/196842.htmSpringCloud 版本&l
- 1、来源random.nextInt() 为 java.util.Random类中的方法; Math.random() 为 java.lan
- 本文实例为大家分享了java实现文件夹解压和压缩的具体代码,供大家参考,具体内容如下效果实现多个文件以及文件夹的压缩和解压代码分析impor