SpringSecurity对Spring Web项目提供支持,AbstractAuthenticationProcessingFilte 作为验证请求入口的。
AbstractAuthenticationProcessingFilter
继承自 GenericFilterBean
,而 GenericFilterBean
是 spring 框架中的过滤器类,实现了接口 javax.servlet.Filter
。
public abstract class AbstractAuthenticationProcessingFilter
extends GenericFilterBean implements ApplicationEventPublisherAware, MessageSourceAware {
AbstractAuthenticationProcessingFilte 成员变量
protected ApplicationEventPublisher eventPublisher;
protected AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
private AuthenticationManager authenticationManager;
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private RememberMeServices rememberMeServices = new NullRememberMeServices();
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();
AbstractAuthenticationProcessingFilter的主要职责和依赖组件
abstractAuthenticationProcessingFilter的职责也就非常明确——处理所有HTTP Request和Response对象,并将其封装成AuthenticationMananger可以处理的Authentication。并且在身份验证成功或失败之后将对应的行为转换为HTTP的Response。同时还要处理一些Web特有的资源比如Session和Cookie。总结成一句话,就是替AuthenticationMananger把所有和Authentication没关系的事情全部给包圆了。
AbstractAuthenticationProcessingFilter为了完成组织上交代的与浏览器和HTTP请求的验证任务,它将大任务拆成了几个子任务并交给了以下组件完成
- AuthenticationManager用于处理身份验证的核心逻辑;
AuthenticationSuccessHandler
用于处理验证成功的后续流程;AuthenticationFailureHandler
用于处理失败的后续流程;- 在验证成功后发布一个名为
InteractiveAuthenticationSuccessEvent
的事件通知给到应用上下文,用于告知身份验证已经成功; - 因为是基于浏览器所以相关的会话管理行为交由
SessionAuthenticationStrategy
来进行实现。 - 文档上还有一点没有写出来的是,如果用户开启了类似“记住我”之类的免密码登录,AbstractAuthenticationProcessingFilter还有一个名为RememberMeServices来进行管理。
流程分析
因为 AbstractAuthenticationProcessingFilter
为本质上是一个 servlet 过滤器,因此找到其入口函数 doFilter()
try
代码快中的 --> 1
处代码表示该处会调用 attemptAuthentication()
方法进行身份校验处理。attemptAuthentication()
方法本体如下所示:
public abstract Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException, IOException,
ServletException;
该方法是个等待子类实现的虚拟方法,对于用户帐号密码的校验在该方法中进行,最后放回的是Authentication 对象。
catch
代码块中的 --> 2
和 --> 3
处代码表示身份校验失败之后调用方法 unsuccessfulAuthentication()
,该方法本体如下所示:
protected void unsuccessfulAuthentication(HttpServletRequest request,
HttpServletResponse response, AuthenticationException failed)
throws IOException, ServletException {
SecurityContextHolder.clearContext();
if (logger.isDebugEnabled()) {
logger.debug("Authentication request failed: " + failed.toString(), failed);
logger.debug("Updated SecurityContextHolder to contain null Authentication");
logger.debug("Delegating to authentication failure handler " + failureHandler);
}
rememberMeServices.loginFail(request, response);
failureHandler.onAuthenticationFailure(request, response, failed);
}
该方法中最重要的一条语句是failureHandler.onAuthenticationFailure(request, response, failed);
,表明验证身份信息失败之后调用类 failureHandler
的 onAuthenticationFailure()
方法。而 failureHandler
是 AuthenticationFailureHandler
的实例变量。
--> 4
处代码表示验证身份信息成功后,调用successfulAuthentication()
方法,其方法本体如下:
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
+ authResult);
}
SecurityContextHolder.getContext().setAuthentication(authResult);
rememberMeServices.loginSuccess(request, response, authResult);
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
authResult, this.getClass()));
}
successHandler.onAuthenticationSuccess(request, response, authResult);
}
其中,最重要的一行代码是 successHandler.onAuthenticationSuccess(request, response, authResult);
,表示身份验证成功后调用 successHandler
的 onAuthenticationSuccess
方法。而 successHandler
为 AuthenticationSuccessHandler
的实现变量。

接下来我们讨论几个问题。
问题1. 怎么判断当前的请求是需要被验证访问的?
在正式进行身份之前,doFilter会通过Security中的MatcherRequest。尝试查找是否有匹配记录。
访问控制的代码:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
// inde.html对应的url允许所任人访问
.antMatchers("/").permitAll()
// user.html对应的url,则需要用户有USER的角色才可以访问
.antMatchers("/user").hasRole("USER")
.and()
.formLogin();
}
其中的matcher的规则便会在这个流程中预先被检查,如果需要进行身份验证则会进行写一个阶段:对请求进行必要的身份验证。
问题2. 如何进行身份验证?
doFilter中通过调用自己的attemptAuthentication方法,但并不进行身份验证的逻辑处理,而是委托AuthenticationManager去完成相关的身份验证流程。AbstractAuthenticationProcessingFilter将HttpServletRequest包装成了Authentication对象与核心的AuthenticationManager进行交互。这样的设计可以使AuthenticationManager不感知外部的Web环境,从而使Security不仅可以支持Web应用,同时也可以被所有Java应用进行使用——只要客制化外部参与并将其封装成Authentication与AuthenticationManager的进行身份验证。
这里还需要注意的是在AuthenticationManager中实际完成身份验证任务并不是AuthenticationManager它自己身。而是将相关的任务针对每一种身份验证协议的AuthenticationProvider去完成相关的身份验证工作。