SpringSecurity用户认证过程原理剖析

1. 用户认证处理流程原理

在这里插入图片描述

1.1 AuthenticationFilter

  1. 根据拦截链模型,不同的登录方式会有不同的AuthenticationFilter
  2. 如果使用的账号密码登录,将被UsernamePasswordAuthenticationFilter进行拦截
  3. 通过UsernamePasswordAuthenticationFilterattemptAuthentication()方法创建一个UsernamePasswordAuthenticationToken对象
  • UsernamePasswordAuthenticationFilter.java, 账号密码过滤器
public Authentication attemptAuthentication(HttpServletRequest request,
			HttpServletResponse response) throws AuthenticationException {
    //1. 创建UsernamePasswordAuthenticationToken对象
		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
				username, password);
	//2. 接着进入AuthenticationManager管理器中选择对应的AuthenticationProvider验证器进行校验
		return this.getAuthenticationManager().authenticate(authRequest);
	}
  • UsernamePasswordAuthenticationToken.java,创建对象
//构造器
public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
		super(null);//未认证,将权限信息设置为null
		this.principal = principal;//将账号密码保存到本地
		this.credentials = credentials;//将权限信息保存到本地
		setAuthenticated(false);//认证状态为false
}

1.2 AuthenticationManager

  1. AuthenticationManager负责管理许多的AuthenticationProvider,这些Provider可以针对不同的登陆方式提供不同的校验流程
  2. 通过获取ProvicerManager,并调用其authenticate()方法遍历所有的校验方式提供者,获取一个能够支持当前登录方式的校验器去进行校验
  3. 找到对应登录方式的Provider后将通过provider.authenticate(authentication);调用provider提供的校验方式进行校验

1.3 AuthenticationProvider

  1. 这里举例用了账号密码登录的方式,所以会使用实现了AbstractUserDetailsAuthenticationProvider的具体实现类DaoAuthenticationProvider来执行校验;
  2. 校验过程调用了AbstractUserDetailsAuthenticationProviderauthenticate方法
  3. 校验过程分为三个阶段:
    1. preAuthenticationChecks.check(user);预检查,根据UserDetails接口中的四个方法,对账号是否启用、账号是否过期、账号是否锁定的状态进行检查
    2. additionalAuthenticationChecks();,密码校验,该方法在是DaoAuthenticationProvider中进行了实现,通过我们配置的PasswordEncoder进行密码校验
    3. postAuthenticationChecks.check(user);,后校验,检验密码是否过期;
  4. 用户认证成功后,就会根据我们提供的UserDetails信息,执行createSuccessAuthentication(principalToReturn, authentication, user);方法;
  5. 创建了一个新的UsernamePasswordAuthenticationToken对象,并以Authentication的形式返回
  • UsernamePasswordAuthenticationToken.java
public UsernamePasswordAuthenticationToken(Object principal, Object credentials,
			Collection<? extends GrantedAuthority> authorities) {
    super(authorities);//保存用户权限信息
    this.principal = principal;
    this.credentials = credentials;
    super.setAuthenticated(true); // 认证成功
}

1.4 返回AuthenticationFilter处理登录结果

  1. Authentication保存最终认证成功的用户认证对象,该对象将返回到最开始的拦截器当中
  2. 最终返回拦截器链中的AbstractAuthenticationProcessingFilter,执行该过滤器的doFilter方法
  3. 在doFilter中,执行successfulAuthentication(request, response, chain, authResult);来执行登录成功的处理器,
  4. successHandler.onAuthenticationSuccess(request, response, authResult);登录成功处理器执行登陆成功的业务逻辑;
  5. 如果在这个认证过程中出现失败都会抛出异常并被过滤器捕获,执行登录失败处理器

2.在多个请求中共享认证结果原理

在这里插入图片描述

  1. 在用户认证流程过程中,若认证成功的认证信息最后会传回到认证拦截器链当中,并在doFilter中执行successfulAuthentication(request, response, chain, authResult);,去调用我们自定义的认证成功处理器,在调用认证成功处理器之前有一句代码是将用户信息传入SecurityContext
  2. SecurityContextHolder.getContext().setAuthentication(authResult);
  3. SecurityContext是用来包装Authentication对象而已
  4. SecurityContextHodler来获取容器保存用户认证信息,这里的SercurityContextHolder可以视为一个线程级别的变量对象,可被多个线程所共享;、
  5. 在拦截器链的最开始,有一个拦截器叫SecurityContextPersistenceFilter,它的作用有两个
    1. 请求进来时:检查Session,判断session中是否有SecurityContext(即有没有用户认证信息),如果有就将其拿出来,放到这个请求线程中;
    2. **请求完成返回时:**检查线程中是否有SecurityContext,将认证信息放到session中

在这里插入图片描述

3. 获取用户认证信息

  1. 方法一:SecurityContextHolder.getContext().getAuthentication()
  2. 方法二:在控制器的入参中,声明Authentication类型,SpringMVC将自动给我们注入用户认证信息;
  • 获取完整的认证信息
@GetMapping("/authentication")
public Authentication getUser(Authentication authentication){
    return authentication;
}
  • 只获取用户基础信息
@GetMapping("/userDetail")
public UserDetails getUser(@AuthenticationPrincipal  UserDetails userDetails){
    return userDetails;
}
发布了5 篇原创文章 · 获赞 0 · 访问量 209

猜你喜欢

转载自blog.csdn.net/drama_CJL/article/details/104237856