前言

了解SpringSecurity更多功能前,我们先了解下其认证流程源码

我们在前面几节中说了SpringSecurity的个性化用户认证流程:
image.png
自定义用户认证逻辑:
image.png

我们上面都是去实现Spring给我们的接口,比如自定义用户认证逻辑:
实现UserDetails、UserDetailsService、接口;
在个性化用户认证流程时候实现SpringSecurity自带的:登录失败实现:AuthenticationFailureHandler;登录成功实现:
AuthenticationSuccessHandler接口。
目前头脑里就是碎片化的登录认证流程,只知道登录成功怎么做,登录失败后如何做?但是在Spring里面是怎样把所有逻辑串起来的?

内容

认证流程源码级详解主要讲解以下3点:

  1. 认证处理流程说明
  2. 认证结果如何在多个请求之间共享
  3. 获取认证用户信息

1. 认证处理流程说明

image.png

我们以表单认证为例:从发起认证请求到认证过滤器,接着认证成功后,响应从认证过滤器返回的整个过程。SpringSecurity做了什么,设计到了哪些类?他们之间如何调用?
SpringSecurity认证流程中涉及到的主要的类和接口如下:
image.png

我们按照上面图示剖析源码:

应用debug启动,浏览器登录:
image.png

image.png
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

image.png
super((Collection)null);//调用父类构造函数;
image.png

AuthenticationManager:自身并不包含验证逻辑,作用用来管理下面的AuthenticationProvider;AuthenticationManager下面有很多实现类,最后是在
ProviderManager:
image.png
image.png

ProviderManager在public Authentication authenticate(Authentication authentication)会进行for循环,获取所有AuthenticationProvider,所有校验逻辑是在AuthenticationProvider里面的,为什么这里是一个集合:是因为不同的登录方式其认证逻辑是不一样的,我们现在是用户名密码登录,是需要去校验密码。如果是微信登录,则又是不一样的。AuthenticationManager作用就是把所有AuthenticationProvider搜集起来,认证时候,挨个去问,你当前的provider支不支持我现在的的登录方式(其实就是做循环,然后调用supports方法)。
image.png

image.png

然后进入DaoAuthenticationProvider,DaoAuthenticationProvider会调用其父类的AbstractUserDetailsAuthenticationProvider的

public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider 

authenticate方法
image.png

这里和我们自定义认证逻辑上符合了,我们自定义认证逻辑时候就是实现了一个UserDetailsService接口。

image.png

预检查---->检查UserDetails的3个boolean:
image.png
附加检查:在DaoAuthenticationProvider--->检查密码是否匹配
image.png

然后进行后检查:后检查主要检查4个boolean中最后一个:
image.png

如果上面检验全部没问题的话,就认为认证是合法的,然后我们再创建一个已经授权的Authentication
image.png
image.png

image.png
最后就会按照下图链返回:一直到:UsernamePasswordAuthenticationFilter
image.png

再往下: AbstractAuthenticationProcessingFilter
image.png

登录成功之后走到我们自定义的成功处理器:
image.png

SecurityContextHolder.getContext().setAuthentication(authResult);
image.png

自定义成功处理器:
image.png
登录成功后:把认证信息打印出去:

response.getWriter().write(objectMapper.writeValueAsString(authentication));

try-catch补货时候,如果有异常抛出就会执行:
image.png

2.认证结果如何在多个请求之间共享

多个请求共享肯定是放到session里,那么SpringSecurity是什么时候?什么东西放到了session里面?什么时候又从session里面读取出来的?
在这个过程中涉及到如下右边类:

image.png

AbstractAuthenticationProcessingFilter里面successfulAuthentication有一个:

SecurityContextHolder.getContext().setAuthentication(authResult);

其实是把我们认证成功的Authentication放到我们的: SecurityContext里面,然后SecurityContext放到SecurityContextHolder里面。

image.png
SecurityContext其实很简单,他就是一个接口,其唯一的实现类是:

image.png

package org.springframework.security.core.context;

import org.springframework.security.core.Authentication;

public class SecurityContextImpl implements SecurityContext {
    private static final long serialVersionUID = 420L;
    private Authentication authentication;

    public SecurityContextImpl() {
    }

    public boolean equals(Object obj) {
        if (obj instanceof SecurityContextImpl) {
            SecurityContextImpl test = (SecurityContextImpl)obj;
            if (this.getAuthentication() == null && test.getAuthentication() == null) {
                return true;
            }

            if (this.getAuthentication() != null && test.getAuthentication() != null && this.getAuthentication().equals(test.getAuthentication())) {
                return true;
            }
        }

        return false;
    }

    public Authentication getAuthentication() {
        return this.authentication;
    }

    public int hashCode() {
        return this.authentication == null ? -1 : this.authentication.hashCode();
    }

    public void setAuthentication(Authentication authentication) {
        this.authentication = authentication;
    }

    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(super.toString());
        if (this.authentication == null) {
            sb.append(": Null authentication");
        } else {
            sb.append(": Authentication: ").append(this.authentication);
        }

        return sb.toString();
    }
}

SecurityContext说明:我们看SecurityContextImpl源码知道,其实他就是对Authentication的包装,并且重写其hashCode和equals,保证其唯一性。

SecurityContextHolder:他实际上是ThreaadLocal封装。ThreaadLocal是跟线程绑定的一个Map,在这个线程里面存放的东西可以在另一个线程读取出来,你可以理解为:线程的全局变量。

image.png

最后:SecurityContextHolder会交给SecurityContextPersistenceFilter过滤器:他的位置在过滤器链的最前端:

image.png

SecurityContextPersistenceFilter过滤器作用:

  1. 当请求进来的时候:检查session里面是否有SecurityContext,如果有将其拿出来,然后放到线程里面,如果没有就过去了;响应时候:从线程里面(因为其在过滤器链上最前端,请求先经过此过滤器,然后最后会从这个过滤器出去)
  2. 当整个请求响应回来以后:最后一个过他的时候,他检查线程,如果线程里面有:SecurityContext,就拿出来放到session里面去。

这样不同的请求可以从线程里面拿到认证信息。拿到以后放到线程里面,因为请求和响应是在一个线程中。

3.获取认证用户信息

我们如何用SecurityContext获取用户信息。我们在Spring-Security-demo 里面的controller里面获取用户信息

image.png

浏览器访问:
image.png

我们也可以通过SpringMvc自己做的注入查找,我们修改如下:
image.png

也可以要求放回用户详情信息,其他信息不要了。
image.png


startshineye
91 声望26 粉丝

我在规定的时间内,做到了我计划的事情;我自己也变得自信了,对于外界的人跟困难也更加从容了,我已经很强大了。可是如果我在规定时间内,我只有3分钟热度,哎,我不行,我就放弃了,那么这个就是我自己的问题,因为你自己...


引用和评论

0 条评论