結合上一期咱們介紹的AuthenticationManager爲入口的身份驗證的核心模塊,咱們此次討論的是爲了使SpringSecurity對Spring Web項目提供支持,做爲驗證請求入口的。html
瞭解AbstractAuthenticationProcessingFilter大概是幹嗎的的最簡單的方法就是直接去讀api-doc。git
Abstract processor of browser-based HTTP-based authentication requests.github
官方文檔說的很明白了:處理基於瀏覽器交互的HTTP驗證請求。因此AbstractAuthenticationProcessingFilter的職責也就很是明確——處理全部HTTP Request和Response對象,並將其封裝成AuthenticationMananger能夠處理的Authentication。而且在身份驗證成功或失敗以後將對應的行爲轉換爲HTTP的Response。同時還要處理一些Web特有的資源好比Session和Cookie。總結成一句話,就是替AuthenticationMananger把全部和Authentication不要緊的事情所有給包圓了。 web
繼續讀JavaDoc能夠得知AbstractAuthenticationProcessingFilter爲了完成組織上交代的與瀏覽器和HTTP請求的驗證任務。它將大任務拆成了幾個子任務並交給瞭如下組件完成:spring
AuthenticationSuccessHandler
用於處理驗證成功的後續流程;AuthenticationFailureHandler
用於處理失敗的後續流程;InteractiveAuthenticationSuccessEvent
的事件通知給到應用上下文,用於告知身份驗證已經成功;SessionAuthenticationStrategy
來進行實現。AbstractAuthenticationProcessingFilter本質上仍是個Filter,其核心的業務入口方法就是doFilter方法: api
在正式進行身份以前,doFilter會經過Security中的。嘗試查找是否有匹配記錄。 咱們回顧下以前咱們寫過的訪問控制的代碼:瀏覽器
@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的規則便會在這個流程中預先被檢查,若是須要進行身份驗證則會進行寫一個階段:對請求進行必要的身份驗證。安全
doFilter中經過調用本身的attemptAuthentication方法,但並不進行身份驗證的邏輯處理,而是委託AuthenticationManager去完成相關的身份驗證流程。AbstractAuthenticationProcessingFilter將HttpServletRequest包裝成了Authentication對象與核心的AuthenticationManager進行交互。這樣的設計可使AuthenticationManager不感知外部的Web環境,從而使Security不只能夠支持Web應用,同時也能夠被全部Java應用進行使用——只要客製化外部參與並將其封裝成Authentication與AuthenticationManager的進行身份驗證。 bash
這裏還須要注意的是在AuthenticationManager中實際完成身份驗證任務並非AuthenticationManager它本身身。而是將相關的任務針對每一種身份驗證協議的AuthenticationProvider去完成相關的身份驗證工做。 cookie
第一個概念會話驗證是什麼的一個概念?咱們知道HTTP的請求其實是無狀態的,瀏覽器爲了使HTTP之間能使用同一會話進行操做,在Java Web中一般會將Web容器(特指Tomcat)的JSESSIONID寫入請求的cookie中一同發送。
針對不一樣的會話管理策略場景,Security也提供了相應的實現,有機會再單獨開一篇單獨來介紹相關的策略。這邊就先了解下,在完成了AuthenticationManager的身份驗證後,還會對其進行必要的會話驗證。
驗證成功以後AuthenticationManager會返回一個經過UserDetail構造而且附帶上了全部受權信息的Authentication對象。 而驗證失敗的話則會拋出,AbstractAuthenticationProcessingFilter捕獲異常以後進行進行驗證失敗的處理。 成功的後續操做最主要的一個操做即是,經過SecurityContextHolder將本次驗證以後的Authentication對象塞到當前的SecurityContext中。在後續的操做中如須要使用到Authentication身份信息,則能夠直接經過SecurityContextHolder去獲取。
//成功後設置上下文二
SecurityContextHolder.getContext().setAuthentication(authResult);
//後續操做能夠從上下文中獲取身份信息
SecurityContextHolder.getContext().getAuthentication();
複製代碼
而後再經過ApplicationEventPublisher發送驗證成功的事件信息供其餘相關監聽器進行相關操做。
操做失敗就簡單了,既然成功是從新將最新的Authentication對象塞到SecurityContext上下文中,失敗即是直接清空了上下文,讓其Authentication對象變得「一無全部」。
而其餘的對於request的額外操做則能夠分別經過與
兩個接口去設置相關操做。
Remember-Me是指網站可以在Session之間記住登陸用戶的身份,具體來講就是我成功認證一次以後在必定的時間內我能夠不用再輸入用戶名和密碼進行登陸了,系統會自動給我登陸。這一般是經過服務端發送一個cookie給客戶端瀏覽器,下次瀏覽器再訪問服務端時服務端可以自動檢測客戶端的cookie,根據cookie值觸發自動登陸操做。 實現方式有不少種,通常來講最簡單的實現就是將用戶名與一些其餘字符組合進行編碼,而後服務端解碼以後提取出相關其中的用戶名,經過UserDetailsService獲取相關用戶信息的用戶驗證方式。 Spring Security中提供了兩種Remember-Me機制進行使用,若是有其餘實現方式也能夠經過繼承AbstractRememberMeServices類進行擴展。請必定牢記Remember-Me機制的現實是依賴瀏覽器Cookie的,在默認狀況下SpringSecurity會將編碼後的字符串存於Cookie中的remember-me鍵位。
#問題6. 如何在其餘服務中監聽驗證成功的事件
在完整整個驗證流程以後,AbstractAuthenticationProcessingFilter還會經過Spring容器的事件發佈器發射一個
。若是須要在應用其餘監聽器上處理相關驗證成功後操做。咱們能夠經過Spring中的@EventListener監聽InteractiveAuthenticationSuccessEvent事件即可以實現。 寫一個示例,若是咱們想在每一個用戶登陸成功後,在控制檯打印出登陸用戶的用戶名。
@Component
public class AuthenticationListener {
@EventListener
public void register(InteractiveAuthenticationSuccessEvent event)
{
//獲取登陸成功的Authentication對象
Authentication authentication = event.getAuthentication();
//打印用戶名
System.out.println("@EventListener註冊信息,用戶名:"+authentication.getName());
}
}
複製代碼
本期花了很大的篇幅介紹了整個Web驗證流程的核心組件AbstractAuthenticationProcessingFilter。下一期咱們將結合他最經常使用的實現類UsernamePasswordAuthenticationFilter作一個講解,但願經過講解UsernamePasswordAuthenticationFilter實現使你們瞭解客製化一個驗證協議須要注意的細節。 咱們下期再見。