spring security過濾器鏈及認證流程
一、過濾器鏈
spring Security功能的實現主要是由一系列過濾器鏈相互配合完成。
下面介紹過濾器鏈中主要的幾個過濾器及其作用:
1.SecurityContextPersistenceFilter 會在請求開始時從配置好的 SecurityContextRepository 中獲取 SecurityContext,然後
把它設定給 SecurityContextHolder。在請求完成後將 SecurityContextHolder 持有的 SecurityContext 再儲存到配置好的
SecurityContextRepository,同時清除 securityContextHolder 所持有的 SecurityContext;
2.UsernamePasswordAuthenticationFilter 用於處理來自表單提交的認證。該表單必須提供對應的使用者名稱和密碼,其內部還有
登入成功或失敗後進行處理的 AuthenticationSuccessHandler 和 AuthenticationFailureHandler,這些都可以根據需求做相
關改變;
3.FilterSecurityInterceptor 是用於保護Http 資源的,它需要一個AccessDecisionManager和一個AuthenticationManager 的
引用。它會從 SecurityContextHolder 獲取 Authentication,然後通過 SecurityMetadataSource 可以得知當前請求是否在請
求受保護的資源。對於請求那些受保護的資源,如果Authentication.isAuthenticated()返回false或者FilterSecurityInterceptor
的alwaysReauthenticate 屬性為 true,那麼將會使用其引用的 AuthenticationManager 再認證一次,認證之後再使用認證後
的 Authentication 替換 SecurityContextHolder 中擁有的那個。然後就是利用 AccessDecisionManager 進行許可權的檢查;
4.ExceptionTranslationFilter 能夠捕獲來自 FilterChain 所有的異常,並進行處理。
但是它只會處理兩類異常:AuthenticationException 和 AccessDeniedException,其它的異常它會繼續丟擲。
--- 如果捕獲到的是 AuthenticationException,那麼將會使用其對應的 AuthenticationEntryPoint 的commence()處理。在處
理之前,ExceptionTranslationFilter先使用 RequestCache 將當前的HttpServerletRequest的資訊儲存起來,以至於使用者成功
登入後可以跳轉到之前的介面;
--- 如果捕獲到的是 AccessDeniedException,那麼將視當前訪問的使用者是否已經登入認證做不同的處理,如果未登入,則會使
用關聯的 AuthenticationEntryPoint 的 commence()方法進行處理,否則將使用關聯的 AccessDeniedHandler 的handle()方
法進行處理。
說明:
1、AuthenticationEntryPoint 是在使用者沒有登入時用於引導使用者進行登入認證的;
2、AccessDeniedHandler 用於在使用者已經登入了,但是訪問其自身沒有許可權的資源時做出對應的處理 [預設實現類:
AccessDeniedHandlerImpl];
3、RequestCache [預設實現類:HttpSessionRequestCache]會將 HttpServletRequest 相關資訊封裝為一個
SavedRequest 並儲存到 HttpSession中;
二、認證流程
認證流程分為登入流程和登出流程,下面將一一敘述。
(注意:這裡的登入方式為"帳號+密碼";箭頭代表整個流程執行方向)
1)登入流程
---> SecurityContextPersistenceFilter(作用在第一節已經介紹)
---> UsernamePasswordAuthenticationFilter
(先獲取使用者名稱和密碼,並將其封裝成UsernamePasswordToken,然後呼叫AuthenticationManager進行驗證)
---> AuthenticationManager
(根據token型別選擇合適的AuthenticationProvider來處理認證請求) [預設實現類:ProviderManager]
AuthenticationManager是一個用來處理請求的介面,它自己不直接處理認證請求,而是委託給其所配置的Authentication
Provider列表,然後會依次使用每一個 AuthenticationProvider 進行認證,如果有一個AuthenticationProvider 認證後的結果
不為 null,則表示該AuthenticationProvider已經認證成功,之後的AuthenticationProvider 將不再繼續認證。然後直接以該
AuthenticationProvider 的認證結果作為 ProviderManager 的認證結果。如果所有的 AuthenticationProvider 的認證結果都
為null,則表示認證失敗,將丟擲一個 ProviderNotFoundException。
---> AuthenticationProvider
(請求認證處理) [預設實現類:DaoAuthencationProvider]
DaoAuthenticationProvider認證過程:
DaoAuthenticationProvider先呼叫UserDetailsService 的loadUserByUsername()方法獲取UserDetails,獲取後再與
UsernamePasswordAuthenticationFilter獲取的username和password進行比較;如果認證通過後會將該 UserDetails 賦給認
證通過的 Authentication的principal,然後再把該 Authentication 存入到 SecurityContext 中。預設情況下,在認證成功後
ProviderManager也將清除返回的Authentication中的憑證資訊。
注意在這裡面根據需要增加[自定義關鍵類(UserDetailService):實現UserDetailService介面並複寫loadUserByUsername()]
問:為什麼AuthenticationManager不直接認證請求?
答:因為token有多種型別。比如最簡單的UsernamePasswordAuthenticationToken,還有spring social的token;
---> Authentication物件
Spring Security使用一個Authentication 物件來描述當前使用者的相關資訊。SecurityContextHolder中持有的是當前使用者的
SecurityContext,而 SecurityContext 持有的是代表當前使用者相關資訊的 Authentication 的引用。這個 Authentication 物件
不需要我們自己去建立,在與系統互動的過程中,Spring Security會自動為我們建立相應的Authentication物件,然後賦值給當
前的SecurityContext。
2)登出流程
1、使HttpSession失效;
2、清除所有已經配置的remember-me認證;
3、清除SecurityContextHolder中的user資訊,並設定Authentication中的Authenticated屬性為false;
4、跳轉到指定url;