上篇中我們對 securityContextHolderAwareRequestFilter的豐富多彩有了個體驗, 最後對這個類的名字也做了一個望文生義的解釋. 本篇中我們將接著看上篇提到的子類,即SavedRequestAwareWrapper. 這個類在父親的基業上又有什麼新的突破呢? 這得從它的賢內助說起, 即這個子類的屬性savedRequest . 呵呵, 這也正是組合的好處.
我們看到SavedRequestAwareWrapper類裡所有方法的實現都是基於這個賢內助的幫助. 拿我們熟悉的getCookies方法來說, 在執行這個方法時, 先要看賢內助的臉色: 如果 savedRequest沒什麼話說,才能執行父親所傳授的,即父類的方法 super.getCookies();
剛才看天下足球的"瘋狂的足球", 而上面的內容也僅是我的想像,希望它較為貼切.
下面,我們看這個賢內助是怎麼回事? Ta是何許人也呢? 讀源碼時發現, 這個SavedRequest沒什麼背景, 沒有繼承, 只是實現了 Serializable接口. 那有什麼用? 單獨地看這個類是不行了, 不過發現這麼一條語句: SavedRequest saved = (SavedRequest) session.getAttribute(AbstractProcessingFilter.ACEGI_SAVED_REQUEST_KEY); 這裡有一個get,那也應該有"人"set過, 難道說通往發現之路的入口?
經過一段時間的偵查, 終於發現那別有洞天! 在描述這個洞天前, 先看這麼一個實際例子.
在CSDN網站裡下載東西, 剛開始時我們先找, 找到一個一看評價不錯, 那就下載吧, 可點下載按鈕時,CSDN網站把我們引到登錄頁面, 噢, 原來還沒登錄呢. 很麻利地填寫了登錄信息後,點確定, 這時CSDN直接就把剛才要下載的東西拿出來了. 8錯! 登錄完後, 不必再從頭找那個鏈接了. 可這是"8錯"是怎麼實現的呢?
我們用Acegi裡的SavedRequest來描述下這個功能的實現, 當然CSDN不一定是用Acegi來實現的, 這裡只是借用下那個情景. 在點"下載"按鈕前, CSDN是允許我們浏覽的, 也就是我們有匿名浏覽的權限, 可當下載時, CSDN發現這個人沒有下載的權限, 得讓Ta登錄. 於是, CSDN裡的安全機制讓頁面跳轉到登錄界面, 同時,為了用戶的使用方便, 把用戶剛才想下載的那個鏈接也記了下來, 放到了Session中, 而我們說的 SavedRequest正是干這個用的. 用戶登錄後, CSDN的安全機制再從Session中取出剛才那個想下載的鏈接, 於是下載順利進行了.
下面結合前面介紹的Acegi概念,把這個過程再描述一遍. 剛才開始用戶找東西時用浏覽的權限, 當Ta想下載時, Acegi的 FilterSecurityInterceptor在執行 beforeInvocation方法時發現當前用戶沒有相應的權限, 於是 FilterSecurityInterceptor拋出一個 accessDeniedException異常, 這個異常在 ExceptionTranslationFilter裡給catch住了, 隨後的 handleException時, 由 sendStartAuthentication方法負責 new了一個SavedRequest 對象, 這個對象裡正是裝了剛才那個鏈接,隨後把這個 SavedRequest對象放進了Session . 於是用戶登錄, 登錄妥當後, AbstractProcessingFilter裡的 successfulAuthentication方法出面通過 determineTargetUrl方法從session中取出前面放到session中的 SavedRequest對象, 最後再交由 sendRedirect方法, 把用戶想要的東東呈現在了眼前.
說了半天了, SavedRequest搶去了大多數的風頭, 不過也好, 把這個 SavedRequest研究透了後, SavedRequestAwareWrapper的功能就不攻自破了. 也就是說從歷史地角度看清了SavedRequest對象的來龍去脈, SavedRequestAwareWrapper對象的現在問題也就應刃而解了.