美文网首页
Spring-Security登录认证授权原理

Spring-Security登录认证授权原理

作者: IT职业与自媒体思考 | 来源:发表于2020-06-30 05:42 被阅读0次

    spring-security源码下载地址:

    https://github.com/spring-projects/spring-security

    Spring-Security源码解读:

    1.使用ctrl+shift+n组合键查找UsernamePasswordAuthenticationFilter过滤器,该过滤器是用来处理用户认证逻辑的,进入后如图:

    (1)可以看到它默认的登录请求url是"/login",并且只允许POST方式的请求

    (2)obtainUsername()方法点进去发现它默认是根据参数名为"username"和"password"来获取用户名和密码的

    (3)通过构造方法实例化一个UsernamePasswordAuthenticationToken对象,此时调用的是UsernamePasswordAuthenticationToken的两个参数的构造函数,如图:

    其中super(null)调用的是父类的构造方法,传入的是权限集合,因为目前还没有认证通过,所以不知道有什么权限信息,这里设置为null,然后将用户名和密码分别赋值给principal和credentials,同样因为此时还未进行身份认证,所以setAuthenticated(false)

    (4)setDetails(request, authRequest)是将当前的请求信息设置到

    UsernamePasswordAuthenticationToken中

    (5)通过调用getAuthenticationManager()来获取AuthenticationManager,通过调用它的authenticate方法来查找支持该token(UsernamePasswordAuthenticationToken)认证方式的provider,然后调用该provider的authenticate方法进行认证

    2.AuthenticationManager是用来管理AuthenticationProvider的接口,通过查找后进入,然后使用ctrl+H组合键查看它的继承关系,找到ProviderManager实现类,它实现了AuthenticationManager接口,查看它的authenticate方法,它里面有段这样的代码:

    for(AuthenticationProvider provider:getProviders())

    {if(!provider.supports(toTest)){continue;}...

    try{result=provider.authenticate(authentication);...

    }}

    通过for循环遍历AuthenticationProvider对象的集合,找到支持当前认证方式的AuthenticationProvider,找到之后调用该AuthenticationProvider的authenticate方法进行认证处理:

    result = provider.authenticate(authentication);

    3.AuthenticationProvider接口,就是进行身份认证的接口,它里面有两个方法:authenticate认证方法和supports是否支持某种类型token的方法,通过ctrl+h查看继承关系,找到AbstractUserDetailsAuthenticationProvider抽象类,它实现了AuthenticationProvider接口,它的supports方法如下:

    public booleansupports(Class<?>authentication){

    return(UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));

    }

    说明它是支持UsernamePasswordAuthenticationToken类型的AuthenticationProvider

    再看它的authenticate认证方法,其中有一段这样的代码:

    用户信息UserDetails是个接口,我们进入查看,它包含以下6个接口方法:

    接着AbstractUserDetailsAuthenticationProvider往下看,找到下面的代码:

    preAuthenticationChecks.check(user);

    additionalAuthenticationChecks(user,(UsernamePasswordAuthenticationToken)authentication);

    preAuthenticationChecks预检查,在最下面的内部类DefaultPreAuthenticationChecks中可以看到,它会检查上面提到的三个boolean方法,即检查账户未锁定、账户可用、账户未过期,如果上面的方法只要有一个返回false,就会抛出异常,那么认证就会失败。

    additionalAuthenticationChecks是附加检查,是个抽象方法,等下看子类的具体实现。

    下面还有个postAuthenticationChecks.check(user)后检查,在最下面的DefaultPostAuthenticationChecks内部类中可以看到,它会检查密码未过期,如果为false就会抛出异常

    如果上面的检查都通过并且没有异常,表示认证通过,会调用下面的方法:

    createSuccessAuthentication(principalToReturn, authentication, user);

    跟进发现此时通过构造方法实例化对象UsernamePasswordAuthenticationToken时,调用的是三个参数的构造方法:

    publicUsernamePasswordAuthenticationToken(Object principal,Object credentials,Collection<?extendsGrantedAuthority>authorities)

    {super(authorities);

    this.principal=principal;

    this.credentials=credentials;

    super.setAuthenticated(true);// must use super, as we override}

    此时会调用父类的构造方法设置权限信息,并调用父类的setAuthenticated(true)方法,到这里就表示认证通过了。

    下面我们看看AbstractUserDetailsAuthenticationProvider的子类,同ctrl+h可查看继承关系,找到DaoAuthenticationProvider

    4.DaoAuthenticationProvider类

    (1)查看additionalAuthenticationChecks附加检查方法,它主要是检查用户密码的正确性,如果密码为空或者错误都会抛出异常

    (2)获取用户信息UserDetails的retrieveUser方法,主要看下面这段代码:

    UserDetails loadedUser=this.getUserDetailsService().loadUserByUsername(username);

    它是调用了getUserDetailsService先获取到UserDetailsService对象,通过调用UserDetailsService对象的loadUserByUsername方法获取用户信息UserDetails

    找到UserDetailsService,发现它是一个接口,查看继承关系,有很多实现,都是spring-security提供的实现类,并不满足我们的需要,我们想自己制定获取用户信息的逻辑,所以我们可以实现这个接口。比如从我们的数据库中查找用户信息

    5.SecurityContextPersistenceFilter过滤器

    那么用户认证成功之后,又是怎么保存认证信息的呢,在下一次请求过来是如何判断该用户是否已经认证了呢?

    请求进来时会经过SecurityContextPersistenceFilter过滤器,进入SecurityContextPersistenceFilter过滤器并找到以下代码:

    SecurityContext contextBeforeChainExecution = repo.loadContext(holder);

    从session中获取SecurityContext对象,如果没有就实例化一个SecurityContext对象

    SecurityContextHolder.setContext(contextBeforeChainExecution);

    将SecurityContext对象设置到SecurityContextHolder中

    chain.doFilter(holder.getRequest(),holder.getResponse());

    表示放行,执行下一个过滤器

    执行完后面的过滤并经过servlet处理之后,响应给浏览器之前再次经过此过滤器。查看以下代码:

    SecurityContext contextAfterChainExecution=SecurityContextHolder.getContext();

    SecurityContextHolder.clearContext();

    this.repo.saveContext(contextAfterChainExecution,holder.getRequest(),holder.getResponse());

    通过SecurityContextHolder获取SecurityContext对象,然后清除SecurityContext,最后将获取的SecurityContext对象放入session中

    其中SecurityContextHolder是与ThreadLocal绑定的,即本线程内所有的方法都可以获得SecurityContext对象,而SecurityContext对象中包含了Authentication对象,即用户的认证信息,spring-security判断用户是否认证主要是根据SecurityContext中的Authentication对象来判断。Authentication对象的详细信息如图:

    最后整个过程的流程大致如下图:

    相关文章

      网友评论

          本文标题:Spring-Security登录认证授权原理

          本文链接:https://www.haomeiwen.com/subject/sjqtqktx.html