认识Spring Securiy框架的认证流程

编程 2019-11-30

本文内容较长,涉及类较多,建议跟着我下面提到的类里的方法,用debug打断点自己走一遍,有助于理解

Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.

Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements

这是来自Spring官网对Spring Securiy的介绍,这个框架给Java程序提供 authentication and authorization ;翻译过来就是认证和授权。现在来一边看源代码,一看来理解他的认证流程。

首先有几个基本的知识需要了解下,Spring Security框架是一个安装在 Filter 中的一个框架,框架提供了许多 Filter ,形成了一一个过滤链,而那个开始和结尾的过滤器是 FilterChainProxy,所以从这个过滤器入手,认识Spring Securiy框架的认证流程。

FilterChainProxy

作为Filter里面肯定有一个doFilter的方法,从这里开始吧

可以看到,进入了一个doFilterInternal方法里

看我代码里红框的部分,里面有一个过滤器集合,也就是之前所说的过滤链,在这14个过滤器中,看名字,发现有一个叫做 UsernamePasswordAuthenticationFilter ,翻译过来就是用户名密码认证过滤器,那就找到这个过滤器,接着这个过滤器看。

UsernamePasswordAuthenticationFilter 

这个类继承自  AbstractAuthenticationProcessingFilter 类,而在父类里有doFilter方法,而子类没有,所以执行的是父类的 doFilter 方法,再进去在父类

AbstractAuthenticationProcessingFilter

在这个类里有一个 attemptAuthentication 翻译叫做尝试认证,在子类中是个抽象方法,也就是需要再去 UsernamePasswordAuthenticationFilter 子类里看这个方法

UsernamePasswordAuthenticationFilter 

这个类中的  attemptAuthentication 方法,对我们输入的用户名和密码做了一个封装,将其封装成一个Token(UsernamePasswordAuthenticationToken) ,而最后对这个Token进行判断类还需 AuthenticationManager 类里的 authenticate 方法。

所以我们还需要去看  getAuthenticationManager 获取出来的 AuthenticationManager

AuthenticationManager

而 AuthenticationManager 是一个接口,所以我们还需要看这个接口的实现类里的  authenticate 方法

可以看到 ProviderManager 是他的实现类,所以我们找到这个实现类

ProviderManager

关键点来了,认证,最重要的就是找相同,比如用户输入的用户名和密码,那我们就在数据找到对于的用户名和密码进行比对,相同,那么自然就认证通过了,而这个类里的 authenticate 就是在进行这样的工作。

不过这个方式是 AuthenticationProvider 里的方法,而这个类是个接口,所以我们还是要去找他的实现类,他有很多的实现类,这里我们找到  AbstractUserDetailsAuthenticationProvider 这个实现类

AbstractUserDetailsAuthenticationProvider 

这个来有两个重要的地方,一个是加载要比对的对象,一个是两个对象的密码进行比对

首先进入这个类,他会首先去寻找并创建 UserDetails 对象,这个对象里存放的就是我们系统保存的密码,而传进来的,就是用户输入的对象,如果在缓存中无法根据用户名拿到对象,就会进入  retrieveUser 方法,从这里拿对象,而这个方法最终是在  DaoAuthenticationProvider 类里

DaoAuthenticationProvider

注意我截图的红框代码,这里获取出来的 UserDetailsService 是一个接口,里面只有一个 loadUserByUsername 方法,那么我们就可以写个 UserDetailsService 的实现类,实现里的 loadUserByUsername 方法,这个方法里我们可以写查询数据库的代码逻辑,也可以固定写死用户名和密码,或者各种姿势返回一个 UserDetailds 对象即可,这个对象也就是用来与用户UsernamePasswordAuthenticationToken 进行比对的对象

当获取对了 UserDetails 对象之后,继续走 AbstractUserDetailsAuthenticationProvider 里的流程

AbstractUserDetailsAuthenticationProvider 

注意看我的这两个红框,这两个红框里的代码是一样的,随便挑一个来说,我就不截图了

check 方法点进去是对 UserDetails 进行一些判断,比如账号被锁定了之类的

我们看  additionalAuthenticationChecks,这个是抽象方法,所以我们还需找到他的实现方法,于是我们又来到了  DaoAuthenticationProvider

DaoAuthenticationProvider

追踪到了这个类里的 additionalAuthenticationChecks 方法

可以看到,最后的密码比对工作是在这里完成的,这里需要注意  PasswordEncoder 是一个接口,还记得配置Spring Security类里需要有一个返回 PasswordEncoder 的Bean方法吗,没错,具体使用哪种密码比对方法,是根据配置类里的Bean走的

认证流程简单来说这样,Spring Securiy框架在认证的前后也做了许多操作,不过我没有仔细看,有空在看看了。

Top