Spring Security小教程 Vol 3. 身份驗證的入口-AbstractAuthenticationProcessingFilter

前言

結合上一期我們介紹的AuthenticationManager為入口的身份驗證的核心模塊挖滤,我們這次討論的是為了使SpringSecurity對Spring Web項目提供支持昙啄,作為驗證請求入口的\color{red}{AbstractAuthenticationProcessingFilter}窒所。

第三期 Authentication核心簡介

本期的任務清單

  1. AbstractAuthenticationProcessingFilter的依賴組件;
  2. AbstractAuthenticationProcessingFilter依賴組件的主要職責和相關設計動機。

1. AbstractAuthenticationProcessingFilter處理Request及與AuthenticationManager交互的流程

AbstractAuthenticationProcessingFilter的主要職責和依賴組件

了解AbstractAuthenticationProcessingFilter大概是干嘛的的最簡單的方法就是直接去讀api-doc。

Abstract processor of browser-based HTTP-based authentication requests.

官方文檔說的很明白了:處理基于瀏覽器交互的HTTP驗證請求。所以AbstractAuthenticationProcessingFilter的職責也就非常明確——處理所有HTTP Request和Response對象劝枣,并將其封裝成AuthenticationMananger可以處理的Authentication汤踏。并且在身份驗證成功或失敗之后將對應的行為轉換為HTTP的Response。同時還要處理一些Web特有的資源比如Session和Cookie舔腾∠海總結成一句話,就是替AuthenticationMananger把所有和Authentication沒關系的事情全部給包圓了稳诚。


雷鋒AbstractAuthenticationProcessingFilter

繼續(xù)讀JavaDoc可以得知AbstractAuthenticationProcessingFilter為了完成組織上交代的與瀏覽器和HTTP請求的驗證任務哗脖。它將大任務拆成了幾個子任務并交給了以下組件完成:

  1. AuthenticationManager用于處理身份驗證的核心邏輯;
  2. AuthenticationSuccessHandler用于處理驗證成功的后續(xù)流程扳还;
  3. AuthenticationFailureHandler用于處理失敗的后續(xù)流程才避;
  4. 在驗證成功后發(fā)布一個名為InteractiveAuthenticationSuccessEvent的事件通知給到應用上下文,用于告知身份驗證已經(jīng)成功氨距;
  5. 因為是基于瀏覽器所以相關的會話管理行為交由 SessionAuthenticationStrategy來進行實現(xiàn)桑逝。
  6. 文檔上還有一點沒有寫出來的是,如果用戶開啟了類似“記住我”之類的免密碼登錄俏让,AbstractAuthenticationProcessingFilter還有一個名為RememberMeServices來進行管理楞遏。
AbstractAuthenticationProcessingFilter主要組件

AbstractAuthenticationProcessingFilter的驗證流程

AbstractAuthenticationProcessingFilter本質上還是個Filter,其核心的業(yè)務入口方法就是doFilter方法:


官方API文檔

這里我們先設置一個問題首昔,然后帶著問題去分析AbstractAuthenticationProcessingFilter的doFilter都是怎么設計解決這些問題的寡喝?

  1. 怎么判斷當前的請求是需要被驗證訪問的?
  2. 如何進行身份驗證沙廉?
  3. 如何進行會話驗證?動機是什么臼节?
  4. 成功和失敗的后續(xù)流程都在干什么撬陵?
  5. Remember-Me功能實現(xiàn)流程是什么?
  6. 如何在其他服務中監(jiān)聽驗證成功的事件网缝?

問題1. 怎么判斷當前的請求是需要被驗證訪問的巨税?

在正式進行身份之前,doFilter會通過Security中的\color{red}{RequestMatcher}粉臊。嘗試查找是否有匹配記錄草添。
我們回顧下之前我們寫過的訪問控制的代碼:

    @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的規(guī)則便會在這個流程中預先被檢查扼仲,如果需要進行身份驗證則會進行寫一個階段:對請求進行必要的身份驗證远寸。

問題2. 如何進行身份驗證?

doFilter中通過調用自己的attemptAuthentication方法屠凶,但并不進行身份驗證的邏輯處理驰后,而是委托AuthenticationManager去完成相關的身份驗證流程。AbstractAuthenticationProcessingFilter將HttpServletRequest包裝成了Authentication對象與核心的AuthenticationManager進行交互矗愧。這樣的設計可以使AuthenticationManager不感知外部的Web環(huán)境灶芝,從而使Security不僅可以支持Web應用,同時也可以被所有Java應用進行使用——只要客制化外部參與并將其封裝成Authentication與AuthenticationManager的進行身份驗證。


身份驗證委托與AuthenticationManager進行實現(xiàn)

這里還需要注意的是在AuthenticationManager中實際完成身份驗證任務并不是AuthenticationManager它自己身夜涕。而是將相關的任務針對每一種身份驗證協(xié)議的AuthenticationProvider去完成相關的身份驗證工作犯犁。


官方文檔attemptAuthentication方法

問題3. 如何進行會話驗證管理?動機是什么女器?

會話驗證策略

第一個概念會話驗證\color{red}{SessionAuthentication}是什么的一個概念酸役?我們知道HTTP的請求實際上是無狀態(tài)的,瀏覽器為了使HTTP之間能使用同一會話進行操作晓避,在Java Web中通常會將Web容器(特指Tomcat)的JSESSIONID寫入請求的cookie中一同發(fā)送簇捍。

cookie中的JSESSIONID

而服務端中通過中的JSESSIONID是通過request.getSession().getId()獲取的,這樣便使本來無狀態(tài)的HTTP通過客戶端的cookie中JSESSIONID與服務端的Session關聯(lián)了起來俏拱。
但是從安全角度來說這樣匹配機制存在許多問題暑塑,最簡單的問題就是Session id在整個會話失效之間是不會變更的,這樣就可以通過身份驗證通過后獲取了Session id從而通過其他客戶端偽造cookie與服務端進行交互锅必。有興趣的同學可以針對這問題去了解下客戶端cookie事格、服務端session以及一些CSRF攻擊的介紹。本人比較推薦這篇http://hengyunabc.github.io/slides/cookie-and-session-and-csrf.html#1搞隐。

針對不同的會話管理策略場景驹愚,Security也提供了相應的實現(xiàn),有機會再單獨開一篇單獨來介紹相關的策略劣纲。這邊就先了解下逢捺,在完成了AuthenticationManager的身份驗證后,還會對其進行必要的會話驗證癞季。


提供的幾種會話驗證策略

問題4. 成功和失敗的后續(xù)流程都在干什么劫瞳?

驗證成功與失敗流程控制

驗證成功之后AuthenticationManager會返回一個通過UserDetail構造并且附帶上了所有授權信息的Authentication對象。
而驗證失敗的話則會拋出\color{red}{AuthenticationException}绷柒,AbstractAuthenticationProcessingFilter捕獲異常之后進行進行驗證失敗的處理志于。
成功的后續(xù)操作最主要的一個操作便是,通過SecurityContextHolder將本次驗證之后的Authentication對象塞到當前的SecurityContext中废睦。在后續(xù)的操作中如需要使用到Authentication身份信息伺绽,則可以直接通過SecurityContextHolder去獲取。

     //成功后設置上下文二
    SecurityContextHolder.getContext().setAuthentication(authResult);

     //后續(xù)操作可以從上下文中獲取身份信息
     SecurityContextHolder.getContext().getAuthentication();

然后再通過ApplicationEventPublisher發(fā)送驗證成功的事件信息供其他相關監(jiān)聽器進行相關操作嗜湃。

操作失敗就簡單了奈应,既然成功是重新將最新的Authentication對象塞到SecurityContext上下文中,失敗便是直接清空了上下文购披,讓其Authentication對象變得“一無所有”钥组。

而其他的對于request的額外操作則可以分別通過\color{red}{AuthenticationSuccessHandler}\color{red}{AuthenticationFailureHandler}兩個接口去設置相關操作。

AuthenticationSuccessHandler

那么哪些工作屬于驗證成功后還需要額外操作的呢今瀑?舉個最簡單的例子程梦,在用戶想訪問一個受限的資源点把,他首先被重定向掉了登錄頁面讓其輸入用戶名和密碼,而在其驗證成功之后屿附,那么他是講指向到指定的某一個頁面還是重定向到本次操作本來想訪問的受限資源的路徑呢郎逃?
這些相關的操作便是在AuthenticationSuccessHandler中進行完成的。
同樣的如果登錄失敗需要做一些除了身份驗證以外挺份,有需要感知HTTP請求褒翰、響應對象的操作,同樣的也可以在AuthenticationFailureHandler中進行完成匀泊。

問題5. Remember-Me功能實現(xiàn)流程是什么优训?

瀏覽器特色服務RememberMe快速登錄

Remember-Me是指網(wǎng)站能夠在Session之間記住登錄用戶的身份,具體來說就是我成功認證一次之后在一定的時間內我可以不用再輸入用戶名和密碼進行登錄了各聘,系統(tǒng)會自動給我登錄揣非。這通常是通過服務端發(fā)送一個cookie給客戶端瀏覽器,下次瀏覽器再訪問服務端時服務端能夠自動檢測客戶端的cookie躲因,根據(jù)cookie值觸發(fā)自動登錄操作早敬。
實現(xiàn)方式有很多種,一般來說最簡單的實現(xiàn)就是將用戶名與一些其他字符組合進行編碼大脉,然后服務端解碼之后提取出相關其中的用戶名搞监,通過UserDetailsService獲取相關用戶信息的用戶驗證方式。
Spring Security中提供了兩種Remember-Me機制進行使用镰矿,如果有其他實現(xiàn)方式也可以通過繼承AbstractRememberMeServices類進行擴展琐驴。請一定牢記Remember-Me機制的現(xiàn)實是依賴瀏覽器Cookie的,在默認情況下SpringSecurity會將編碼后的字符串存于Cookie中的remember-me鍵位秤标。


RememberMeServices

問題6. 如何在其他服務中監(jiān)聽驗證成功的事件

在完整整個驗證流程之后绝淡,AbstractAuthenticationProcessingFilter還會通過Spring容器的\color{red}{ApplicationEventPublisher}事件發(fā)布器發(fā)射一個\color{red}{InteractiveAuthenticationSuccessEvent}。如果需要在應用其他監(jiān)聽器上處理相關驗證成功后操作抛杨。我們可以通過Spring中的@EventListener監(jiān)聽InteractiveAuthenticationSuccessEvent事件便可以實現(xiàn)够委。
寫一個示例荐类,如果我們想在每個用戶登錄成功后怖现,在控制臺打印出登錄用戶的用戶名。

@Component
public class AuthenticationListener {
    @EventListener
    public void register(InteractiveAuthenticationSuccessEvent event)
    {
        //獲取登錄成功的Authentication對象
        Authentication authentication = event.getAuthentication();
        //打印用戶名
        System.out.println("@EventListener注冊信息玉罐,用戶名:"+authentication.getName());
    }
}

結尾

本期花了很大的篇幅介紹了整個Web驗證流程的核心組件AbstractAuthenticationProcessingFilter屈嗤。下一期我們將結合他最常用的實現(xiàn)類UsernamePasswordAuthenticationFilter做一個講解,希望通過講解UsernamePasswordAuthenticationFilter實現(xiàn)使大家了解客制化一個驗證協(xié)議需要注意的細節(jié)吊输。
我們下期再見饶号。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市季蚂,隨后出現(xiàn)的幾起案子茫船,更是在濱河造成了極大的恐慌琅束,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件算谈,死亡現(xiàn)場離奇詭異涩禀,居然都是意外死亡,警方通過查閱死者的電腦和手機然眼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門艾船,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人高每,你說我怎么就攤上這事屿岂。” “怎么了鲸匿?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵爷怀,是天一觀的道長。 經(jīng)常有香客問我晒骇,道長霉撵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任洪囤,我火速辦了婚禮徒坡,結果婚禮上,老公的妹妹穿的比我還像新娘瘤缩。我一直安慰自己喇完,他們只是感情好,可當我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布剥啤。 她就那樣靜靜地躺著锦溪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪府怯。 梳的紋絲不亂的頭發(fā)上刻诊,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天,我揣著相機與錄音牺丙,去河邊找鬼则涯。 笑死,一個胖子當著我的面吹牛冲簿,可吹牛的內容都是我干的粟判。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼峦剔,長吁一口氣:“原來是場噩夢啊……” “哼档礁!你這毒婦竟也來了?” 一聲冷哼從身側響起吝沫,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤呻澜,失蹤者是張志新(化名)和其女友劉穎递礼,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體羹幸,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡宰衙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了睹欲。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片供炼。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖窘疮,靈堂內的尸體忽然破棺而出袋哼,到底是詐尸還是另有隱情,我是刑警寧澤闸衫,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布涛贯,位于F島的核電站,受9級特大地震影響蔚出,放射性物質發(fā)生泄漏弟翘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一骄酗、第九天 我趴在偏房一處隱蔽的房頂上張望稀余。 院中可真熱鬧,春花似錦趋翻、人聲如沸睛琳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽师骗。三九已至,卻和暖如春讨惩,著一層夾襖步出監(jiān)牢的瞬間辟癌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工荐捻, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留黍少,地道東北人。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓靴患,卻偏偏與公主長得像仍侥,于是被迫代替她去往敵國和親要出。 傳聞我的和親對象是個殘疾皇子鸳君,可洞房花燭夜當晚...
    茶點故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內容