一、流程源码分析
1.首先调用了UsernamePasswordAuthenticationFilter的doFilter()方法。
前端请求时,请求信息首先到达UsernamePasswordAuthenticationFilter,该类的继承关系如下。
UsernamePasswordAuthenticationFilter是一个过滤器,所以首先执行doFilter()方法,该方法在它的父类AbstractAuthenticationProcessingFilter 中,方法源码如下:
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
// 判断当前过滤器能否处理该请求,如果不能,则交给下一个filter去处理
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
try {
// 调用子类中的方法,创建Authentication实现类
Authentication authenticationResult = attemptAuthentication(request, response);
if (authenticationResult == null) {
// return immediately as subclass has indicated that it hasn't completed
return;
}
// 如果成功则做些session相关操作,例如将信息保存到session
this.sessionStrategy.onAuthentication(authenticationResult, request, response);
// Authentication success
if (this.continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
successfulAuthentication(request, response, chain, authenticationResult);
}
catch (InternalAuthenticationServiceException failed) {
this.logger.error("An internal error occurred while trying to authenticate the user.", failed);
unsuccessfulAuthentication(request, response, failed);
}
catch (AuthenticationException ex) {
// Authentication failed
unsuccessfulAuthentication(request, response, ex);
}
}
2.doFilter方法中调用了自身的attemptAuthentication()方法
3.attemptAuthentication()方法中调用了AuthenticationManager接口实现类ProviderManager的authenticate方法。
(1)在attemptAuthentication()方法中,只是将账号密码和一些请求信息封装在了一个类中,但并没有对权限,密码是否正确等进行认证,该方法就是用来进行认证管理的。
(2)AuthenticationManager是一个接口,该接口中只有一个authenticate方法。
public interface AuthenticationManager {
Authentication authenticate(Authentication var1) throws AuthenticationException;
}
(3)该接口的主要实现类为ProviderManager,调用的就是该类的authenticate方法。该类有二个用于认证的成员变量:
private List<AuthenticationProvider> providers;
private AuthenticationManager parent;
AuthenticationProvider是一个接口,是用来提供认证服务的,ProviderManager只是用来管理认证服务的。
// 该接口有两个方法,该接口的实现类主要做认证的。
public interface AuthenticationProvider {
Authentication authenticate(Authentication var1) throws AuthenticationException;
// supports是用来检测该类型的认证信息是否可以被自己处理。可以被处理则调用自身的authenticate方法。
boolean supports(Class<?> var1);
}
(4)ProviderManager的authenticate()方法源码关键部分如下:
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Class toTest = authentication.getClass();
Object lastException = null;
Authentication result = null;
boolean debug = logger.isDebugEnabled();
// 拿到全部的provider
Iterator e = this.getProviders().iterator();
// 遍历provider
while(e.hasNext()) {
AuthenticationProvider provider = (AuthenticationProvider)e.next();
// 挨着个的校验是否支持当前token
if(provider.supports(toTest)) {
if(debug) {
logger.debug("Authentication attempt using " + provider.getClass().getName());
}
try {
// 找到后直接break,并由当前provider来进行校验工作
result = provider.authenticate(authentication);
if(result != null) {
this.copyDetails(authentication, result);
break;
}
} catch (AccountStatusException var11) {
this.prepareException(var11, authentication);
throw var11;
} catch (InternalAuthenticationServiceException var12) {
this.prepareException(var12, authentication);
throw var12;
} catch (AuthenticationException var13) {
lastException = var13;
}
}
}
// 若没有一个支持,则尝试交给父类来执行
if(result == null && this.parent != null) {
try {
result = this.parent.authenticate(authentication);
} catch (ProviderNotFoundException var9) {
;
} catch (AuthenticationException var10) {
lastException = var10;
}
}
..........................
}
其中会遍历调用AuthenticationProvider实现类对象的supports方法,如果方法返回true,则调用AuthenticationProvider实现类对象的authenticate()方法,该方法是用来进行认证的。
如果该ProviderManager的List<AuthenticationProvider> providers都无法处理,则会调用该ProviderManager的AuthenticationManager parent的authenticate方法,流程一样。
4.AuthenticationManager对象的authenticate方法中调用AuthenticationProvider接口实现类DaoAuthenticationProvider的authenticate方法
**(1) DaoAuthenticationProvider **
对于我们前面封装的UsernamePasswordAuthenticationToken 对象,它的认证处理可以被DaoAuthenticationProvider类进行认证。
DaoAuthenticationProvider类中其实没有定义authenticate方法,它是继承了父类AbstractUserDetailsAuthenticationProvider中的authenticate方法。
AbstractUserDetailsAuthenticationProvider的authenticate()方法源码如下:
// 实现了AuthenticationProvider接口
public abstract class AbstractUserDetailsAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported"));
String username = authentication.getPrincipal() == null?"NONE_PROVIDED":authentication.getName();
boolean cacheWasUsed = true;
UserDetails user = this.userCache.getUserFromCache(username);
if(user == null) {
cacheWasUsed = false;
try {
// 调用自类retrieveUser
user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);
} catch (UsernameNotFoundException var6) {
this.logger.debug("User \'" + username + "\' not found");
if(this.hideUserNotFoundExceptions) {
throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
throw var6;
}
Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");
}
try {
/*
* 前检查由DefaultPreAuthenticationChecks类实现(主要判断当前用户是否锁定,过期,冻结
* User接口)
*/
this.preAuthenticationChecks.check(user);
// 子类具体实现
this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);
} catch (AuthenticationException var7) {
if(!cacheWasUsed) {
throw var7;
}
cacheWasUsed = false;
user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);
this.preAuthenticationChecks.check(user);
this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);
}
// 检测用户密码是否过期
this.postAuthenticationChecks.check(user);
if(!cacheWasUsed) {
this.userCache.putUserInCache(user);
}
Object principalToReturn = user;
if(this.forcePrincipalAsString) {
principalToReturn = user.getUsername();
}
return this.createSuccessAuthentication(principalToReturn, authentication, user);
}
}
(2)在该方法中,调用了自身的retrieveUser方法,该方法在DaoAuthenticationProvider中,源码如下:
该方法首先调用UserDetailsService接口中的loadUserByUsername方法获得数据库或者内存等地方保存的用户信息,所以可以通过实现该接口,可以自定义设置用户信息的来源。
然后将获得到的密码和请求的密码进行比对,如果相同,则方法获取到的用户信息。接着做一些安全检查之类的:
this.preAuthenticationChecks.check(user);
// 子类具体实现
this.additionalAuthenticationChecks(user,(UsernamePasswordAuthenticationToken)authentication);
this.postAuthenticationChecks.check(user);
最后该方法返回
return createSuccessAuthentication(principalToReturn, authentication, user);
createSuccessAuthentication方法中调用UsernamePasswordAuthenticationToken的另一个构造器,该构造器会生成具有用户权限和验证通过标识的UsernamePasswordAuthenticationToken类的对象。
5.最后返回到UsernamePasswordAuthenticationFilter的doFilter方法中
二、流程总结
(1)UsernamePasswordAuthenticationFilter的doFilter()方法
(2)UsernamePasswordAuthenticationFilter的attemptAuthentication()方法
在该方法中主要生成了未通过验证的Authentication接口的实现类UsernamePasswordAuthenticationToken 对象。
(3)AuthenticationManager实现类ProviderManager的authenticate()方法
该方法主要通过轮询判断AuthenticationProvider接口实现类DaoAuthenticationProvider能否认证UsernamePasswordAuthenticationToken 对象,
(4)AuthenticationProvider接口实现类DaoAuthenticationProvider的authenticate()方法
该方法主要调用retrieveUser()进行密码判断,对密码正确后的用户信息进行一些其他的安全判断,并最后生通过验证的Authentication接口的实现类UsernamePasswordAuthenticationToken 对象。
(5)AuthenticationProvider接口实现类DaoAuthenticationProvider的retrieveUser()方法
该方法主要进行密码验证
(6)serDetailsService接口中的loadUserByUsername()方法
该方法获取之前保存的用户信息
(7)serDetailsService接口中的loadUserByUsername()方法返回验证结果
(8)最后验证成功,返回UsernamePasswordAuthenticationFilter的doFilter()方法
有问题欢迎指正,也欢迎各位交流讨论,有问题可以问。