一、UsernamePasswordAuthenticationFilter
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
第一个验证处理过滤器类,负责处理表单登录请求获,取请求中带的username、password,使用username、password构建org.springframework.security.authentication.UsernamePasswordAuthenticationToken
从UsernamePasswordAuthenticationToken这个对象从继承书上看,实际上是Authentication接口的实现,Authentication接口实际上是封装了认证信息。
UsernamePasswordAuthenticationToken
UsernamePasswordAuthenticationFilter用username、password构建UsernamePasswordAuthenticationToken的时候:
1、把用户名和密码都设置到自己本地变量上
2、super(null);父类构造函数需要传一组权限进来,但是执行到当前代码的时候,还没有进行过身份认证,不知道这个用户的权限是什么,所以这里是null
3、setAuthenticated(false);表示前面存储进去的信息是否经过身份认证,false表示没有
具体代码:
public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
// 父类构造函数需要传一组权限进来,但是执行到当前代码的时候,还没有进行过身份认证,不知道这个用户的权限是什么,所以这里是null
super(null);
// 把用户名和密码都设置到自己本地变量上
this.principal = principal;
this.credentials = credentials;
// 表示前面存储进去的信息是否经过身份认证,false表示没有
setAuthenticated(false);
}
setDetails
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#setDetails(HttpServletRequest,UsernamePasswordAuthenticationToken)
把请求的信息设置到UsernamePasswordAuthenticationToken里面,包括发请求的ip、session等
二、AuthenticationProvider
然后开始调用AuthenticationManagerorg.springframework.security.authentication.AuthenticationManager
,AuthenticationManager本身并不包含验证的逻辑,它的作用是用来管理AuthenticationProvider org.springframework.security.authentication.ProviderManager
,实现了AuthenticationManager。
ProviderManager.authenticate(Authentication authentication)
方法
里面有一个for循环,getProviders()拿到所有的AuthenticationProvider接口,真正的校验是写在AuthenticationProvider里面的。
为什么是for循环?因为不同的登录方式,它的认证逻辑是不一样的。用户名密码登录验密码。第三方登录,是不需要密码的,两种登录,逻辑不一样,spring security 提供两个AuthenticationProvider。
AuthenticationManager负责把所有的AuthenticationProvider收集起来,收集之后,挨个比对该AuthenticationProvider支不支持对应的登录方式,即:provider.supports(toTest) ,如果不支持,继续比对下一个;支持,执行真正的校验逻辑 result = provider.authenticate(authentication);
。
AuthenticationManager校验AuthenticationProvider支持的登录方式
result = provider.authenticate(authentication);
主要的校验逻辑在调抽象类org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider
的Authentication authenticate(Authentication authentication)方法里面的authenticate(Authentication authentication)。
retrieveUser()方法
retrieveUser()方法获取UserDetails对象,retrieveUser()是一个抽象方法,它的实现org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser()
中有:
UserDetails loadedUser ==this.getUserDetailsService().loadUserByUsername(username);
实际上是在调UserDetailsService接口的实现来获取UserDetailsService,就和前面的自定义用户逻辑对接上了,如果拿到了用户信息,就有一个(preAuthenticationChecks.check(user);
);
预检查
preAuthenticationChecks.check(user);的实现是一个内部类:
org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.DefaultPreAuthenticationChecks.check(user);
这里面做了三个检查,返回的是UserDetails的实现,而UserDetails里面有四个Boolean方法分别判断用户是否可用等,这里检查其中三个,并抛出对应账户锁定、账户过期异常。
additionalAuthenticationChecks
additionalAuthenticationChecks(user,(UsernamePasswordAuthenticationToken) authentication);附加的检查,在抽象类中是一个抽象方法,在DaoAuthenticationProvider中具体的实现,用PasswordEncoder校验密码是否匹配。
后检查
如果前面的都通过后再有一个,postAuthenticationChecks.check(user);后检查,DefaultPostAuthenticationChecks实现,检查四个Boolean里面的最后一个,所有的检查都通过,表明用户认证是成功的,然后在创建一个Authentication。
创建Authentication
createSuccessAuthentication(principalToReturn, authentication, user);
重新实例一个UsernamePasswordAuthenticationToken,不过构造方法不一样了,传入了Authentication,setAuthenticated(true);认证已经通过
/**
* This constructor should only be used by <code>AuthenticationManager</code> or
* <code>AuthenticationProvider</code> implementations that are satisfied with
* producing a trusted (i.e. {@link #isAuthenticated()} = <code>true</code>)
* authentication token.
*
* @param principal
* @param credentials
* @param authorities
*/
public UsernamePasswordAuthenticationToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true); // must use super, as we override
}
最后Authentication再返回会UsernamePasswordAuthenticationFilter调登陆成功的自定义方法,org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.successfulAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, Authentication)
和失败的自定义方法。