AbstractAuthenticationProcessingFilter 的职责也就非常明确:处理所有HTTP Request和Response对象,并将其封装成AuthenticationMananger可以处理的Authentication。并且在身份验证成功或失败之后将对应的行为转换为HTTP的Response,同时还要处理一些Web特有的资源比如Session和Cookie。
AbstractAuthenticationProcessingFilter 源码:
public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean implements ApplicationEventPublisherAware, MessageSourceAware {
//事件发布管理器
protected ApplicationEventPublisher eventPublisher;
protected AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
// 认证管理器,定义了SpringSecurity如何进行认证操作
// 认证成功后会返回一个Authentication对象,这个对象会被设置到SecurityContextHolder中
private AuthenticationManager authenticationManager;
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
// 如果用户开启了类似“记住我”之类的免密码登录,RememberMeServices来进行管理。
private RememberMeServices rememberMeServices = new NullRememberMeServices();
// 请求匹配器,定义了match()方法,匹配请求HttpServletRequest是否符合定义的规则
private RequestMatcher requiresAuthenticationRequestMatcher;
private boolean continueChainBeforeSuccessfulAuthentication = false;
// 会话验证管理
private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();
private boolean allowSessionCreation = true;
// 用户登录成功的后续处理
private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
// 用户登录失败的后续处理
private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
// 判断是否需要认证
if (!this.requiresAuthentication(request, response)) {
// 如果不需要认证,继续执行下一个过滤器
chain.doFilter(request, response);
} else {
Authentication authResult;
try {
// 认证处理,该方法需要子类去重写
authResult = this.attemptAuthentication(request, response);
if (authResult == null) {
return;
}
// 身份认证成功,保存session
this.sessionStrategy.onAuthentication(authResult, request, response);
} catch (InternalAuthenticationServiceException var8) {
this.logger.error("An internal error occurred while trying to authenticate the user.", var8);
// 认证失败的处理逻辑
this.unsuccessfulAuthentication(request, response, var8);
return;
} catch (AuthenticationException var9) {
// 认证失败的处理逻辑
this.unsuccessfulAuthentication(request, response, var9);
return;
}
if (this.continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
// 认证成功的处理逻辑
this.successfulAuthentication(request, response, chain, authResult);
}
}
// 判断该filter是否需要处理该次请求,即请求的路径和该filter配置的要处理的url是否匹配
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
return this.requiresAuthenticationRequestMatcher.matches(request);
}
// 需要子类提供身份认证的具体实现。
public abstract Authentication attemptAuthentication(HttpServletRequest var1, HttpServletResponse var2) throws AuthenticationException, IOException, ServletException;
// 认证成功的处理逻辑
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
// 将认证成功的用户信息保存到 SecurityContextHolder
SecurityContextHolder.getContext().setAuthentication(authResult);
// 处理记住我逻辑
this.rememberMeServices.loginSuccess(request, response, authResult);
// 发布时间,即发布认证成功消息,供其他的bean接收和处理
if (this.eventPublisher != null) {
this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
}
// 认证成功的处理逻辑
this.successfulAuthentication(request, response, chain, authResult);
}
}
// 判断该filter是否需要处理该次请求,即请求的路径和该filter配置的要处理的url是否匹配
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
return this.requiresAuthenticationRequestMatcher.matches(request);
}
// 需要子类提供身份认证的具体实现。
public abstract Authentication attemptAuthentication(HttpServletRequest var1, HttpServletResponse var2) throws AuthenticationException, IOException, ServletException;
// 认证成功的处理逻辑
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
// 将认证成功的用户信息保存到 SecurityContextHolder
SecurityContextHolder.getContext().setAuthentication(authResult);
// 处理记住我逻辑
this.rememberMeServices.loginSuccess(request, response, authResult);
// 发布时间,即发布认证成功消息,供其他的bean接收和处理
if (this.eventPublisher != null) {
this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
}
// 认证成功后后续处理
this.successHandler.onAuthenticationSuccess(request, response, authResult);
}
// 认证失败的处理逻辑
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
// 清除 SecurityContextHolder 存储的认证信息
SecurityContextHolder.clearContext();
// 清除 rememberMe 存储的认证信息
this.rememberMeServices.loginFail(request, response);
// 认证失败的后续处理
this.failureHandler.onAuthenticationFailure(request, response, failed);
}
}
AbstractAuthenticationProcessingFilter为了完成与浏览器和HTTP请求的验证,它将大任务拆成了几个子任务并交给了以下组件完成:
- AuthenticationManager用于处理身份验证的核心逻辑;
- AuthenticationSuccessHandler用于处理验证成功的后续流程;
- AuthenticationFailureHandler用于处理失败的后续流程;
- 在验证成功后发布一个名为InteractiveAuthenticationSuccessEvent的事件通知给到应用上下文,用于告知身份验证已经成功;
因为是基于浏览器所以相关的会话管理行为交由 SessionAuthenticationStrategy来进行实现。 - 如果用户开启了类似“记住我”之类的免密码登录,AbstractAuthenticationProcessingFilter还有一个名为RememberMeServices来进行管理。
AbstractAuthenticationProcessingFilte 作为身份认证请求入口,是一个抽象类。OAuth2ClientAuthenticationProcessingFilter(Spriing OAuth2)、RememberMeAuthenticationFilter(RememberMe)都继承了 AbstractAuthenticationProcessingFilter ,并重写了方法 attemptAuthentication 进行身份认证。
UsernamePasswordAuthenticationFilter 过滤器:SpringSecurity 中默认的是表单登录格式,即用户在表单中输入用户名和密码进行登录,登录参数的提取在 UsernamePasswordAuthenticationFilter 过滤器中完成,UsernamePasswordAuthenticationFilter 是AbstractAuthenticationProcessingFilter 针对使用用户名和密码进行身份认证而定制化的一个过滤器。其添加是在调用http.formLogin() 时作用,默认的登录请求 url 为 “/login”,并且为 POST 请求。当我们登录的时候,也就是匹配到loginProcessingUrl,这个过滤器就会委托认证管理器 authenticationManager 来验证登录。
ClientCredentialsTokenEndpointFilter 过滤器:Spring Security对于获取TOKEN的请求(默认是"/oauth/token"),需要认证client_id和client_secret。认证client_id和client_secret可以有2种方式,一种是通过本节讲的ClientCredentialsTokenEndpointFilter,另一种是通过BasicAuthenticationFilter。ClientCredentialsTokenEndpointFilter首先比对请求URL是否是TOKEN请求路径以及请求参数中是否包含client_id,如果满足以上条件,再调用ProviderManager认证client_id和client_secret是否与配置的一致。如果通过认证,会把身份认证信息保存打SecurityContext上下文中。
OAuth2AuthenticationProcessingFilter 过滤器:授权认证服务通过认证后会返回 Access Token,该token可用于请求资源服务(业务系统)的接口。我们需要把特定的信息放到请求头中,例如在请求头中写入Authorization: Bearer !xBYUEBY0N3o234N,Authorization为key,Bearer !xBYUEBY0N3o234N为value,!xBYUEBY0N3o234N是Access Token。请求经过OAuth2AuthenticationProcessingFilter后,过滤器中的认证管理器会调用配置的授权认证服务的check token接口校验token是否有效,token有效则可以继续访问了。需要注意的是,对于资源认证服务Spring Security会把这个过滤器加入到过滤链里,但授权认证认证服务却不能。