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去完成相關的身份驗證工作。