Spring Security 账号密码认证源码解析,java 项目开发全程实录第四版视频
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 v
ar1) 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 {
评论