众所周知Spring security是Spring家族一员。是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它的核心功能主要有 认证 Authentication(比如用户登入、系统授权访问 )、授权Authorization(当前用户有哪些资源可以访问 )、攻击防护(防止伪造身份)。其核心就是一组过滤器链,项目启动后将会自动配置。

本篇文章将对security中过滤器链中关于用户名密码认证的 UsernamePasswordAuthenticationFilter过滤器进行源码追踪与分析。

首先在idea里连点两下shift找到UsernamePasswordAuthenticationFilter

spring security 配置 超时 spring security check_java

因为security核心是一组过滤器链,即过滤器拦截到响应url的请求后会先执行doFilter()方法中chain.doFilter()之前的代码,然后执行下一个过滤器或者servelt。紧接着执行chain.doFilter()之后的代码。所以必须找到doFilter()方法

但是在UsernamePasswordAuthenticationFilter并没有找到doFilter方法,但是发现继承了

AbstractAuthenticationProcessingFilter这个类,那么根据多态就说明调用的其实就是父类中的doFilter()方法。

spring security 配置 超时 spring security check_spring_02

 在父类中寻找果然找到了,在执行到attemptAuthentication这一步发现this对象是UsernamePasswordAuthenticationFilter又跳回到了子类中

spring security 配置 超时 spring security check_spring_03

 果然在子类UsernamePasswordAuthenticationFilter中找到了 (首先判断了发送的请求是不是POST,这也是为什么不能用GET请求的原因)

spring security 配置 超时 spring security check_子类_04

 继续向下追踪

spring security 配置 超时 spring security check_debug_05

到了this.getAuthenticationManager().authenticate(authRequest)这一步。这一步比较复杂需要分成两部分来看。

spring security 配置 超时 spring security check_debug_06

 先看ProviderManager类,先DEBUG进去发现ProviderManager实现了AuthenticationManager接口

spring security 配置 超时 spring security check_debug_07

看下这个AuthenticationManager接口

spring security 配置 超时 spring security check_子类_08

果然不出意外,找到了authenticate()方法,那么就说明ProviderManager类一定有这个authenticate()方法。

spring security 配置 超时 spring security check_debug_09

ProviderManagerauthenticate()方法中继续向下查找发现了这一步调用了DaoAuthenticationProvider这个类

spring security 配置 超时 spring security check_ide_10

 我们继续向下DEBUG发现直接跳入到了AbstractUserDetailsAuthenticationProvider类中并不是我们想象中的DaoAuthenticationProvider

spring security 配置 超时 spring security check_ide_11

 这是为什么呢?查看DaoAuthenticationProvider发现其实他是AbstractUserDetailsAuthenticationProvider的子类,那么真相大白又是调用了父类的方法所以才会跳到AbstractUserDetailsAuthenticationProvider里面

spring security 配置 超时 spring security check_java_12

AbstractUserDetailsAuthenticationProviderauthenticate()方法中继续向下查找发现在该方法中用retrieveUser()方法中找到user并且进行密码比对(密码比对的底层其实就是equals判断两个字符串是否相等)

spring security 配置 超时 spring security check_java_13

现在追踪源码已经进入尾声,最后再看看是怎么找到user对象的

进入到retrieveUser()中

spring security 配置 超时 spring security check_java_14

 发现回到了DaoAuthenticationProvider子类

在这个地方又需要分成两部分去观察

spring security 配置 超时 spring security check_java_15

 this.getUserDetailsService()是一个类因为我的数据密码是写死的所以指的InMemoryUserDetailsManager

spring security 配置 超时 spring security check_java_16

 继续向下DEBUG发现确实找到了我写的固定的数据(这里InMemoryUserDetailsManager其实就相当于一个假的数据库)

spring security 配置 超时 spring security check_子类_17

而在使用真实的数据库时,可以使用自定义实现类继承UserDetailService接口

来代替InMemoryUserDetailsManager

到这里对security认证的源码追踪就结束了。

截取的简化版的认证流程图

spring security 配置 超时 spring security check_spring_18