閱讀本文之前,我默認您已經掌握 spring-web spring-boot 的基礎知識
本文的編寫依據是 Spring Security 官方文檔
Spring Security 是一個安全框架,前身是 Acegi Security,能夠爲 Spring 企業應用系統提供聲明式的安全訪問控制。Spring Security 基於 Servlet 過濾器、IoC 和 AOP,爲 Web 請求和方法調用提供身份確認和授權處理,避免了代碼耦合,減少了大量重複代碼工作。
既然Spring Security是基於Filter來實現的,那咱們先從Filter 開始:
1.FilterChain
Filter是在Servlet容器啓動前被註冊,如下:這是一個典型的過濾鏈,通過(chain.doFilter(request, response))一層層的過濾請求
2.DelegatingFilterProxy
Servlet容器允許使用其自己的標準註冊Filters,但它不瞭解Spring定義的Bean,而DelegatingFilterProxy 在Servlet容器和Spring的ApplicationContext之間搭建了一個橋樑。使得Servlet可以註冊在SpringApplicationContext中定義的Filter,如下圖所示
DelegatingFilterProxy從ApplicationContext查找Bean Filter0,然後調用Bean Filter0。 僞代碼如下:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// Lazily get Filter that was registered as a Spring Bean
// For the example in DelegatingFilterProxy delegate is an instance of Bean Filter0
Filter delegate = getFilterBean(someBeanName);
// delegate work to the Spring Bean
delegate.doFilter(request, response);
}
DelegatingFilter還有一個好處:延遲註冊Filter Bean
在 spring-web第一期 我曾分析過,因爲Servlet容器需要在容器啓動之前註冊Filter實例。但是,Spring通常使用ContextLoaderListener加載Spring Bean,因此在容器啓動前是不可以獲取Spring Bean的,直到Spring容器啓動後纔可以註冊這些Filter。
3. FilterChainProxy
Spring Security的對Servlet的支持包含在FilterChainProxy中。 FilterChainProxy是Spring Security提供的特殊過濾器,允許通過SecurityFilterChain委派許多過濾器實例。由於FilterChainProxy是一個Bean,因此通常將其包裝在DelegatingFilterProxy中。
4. SecurityFilterChain
FilterChainProxy使用SecurityFilterChain確定應對此請求調用哪些Spring Security過濾器
SecurityFilterChain中的Security FIlter 通常是Bean,但它們使用的是FilterChainProxy而不是DelegatingFilterProxy註冊的。 FilterChainProxy具有直接向Servlet容器或DelegatingFilterProxy註冊的許多優點。例如,它爲Spring Security的所有Servlet支持提供了一個起點。因此,如果您想對Spring Security的Servlet支持進行BUG排除,那麼在FilterChainProxy中添加調試點是一個很好的選擇。
實際上,FilterChainProxy 甚至可以確定來使用哪一個 SecurityFilterChain, 前提是你可以爲你的應用程序提供多個完全獨立的配置空間,如下圖所示:
FilterChainProxy決定應使用哪個SecurityFilterChain(每個FilterChain中擁有的Filter個數可不相同)。僅匹配的第一個SecurityFilterChain將被調用。如果請求的URL是/ api / messages /,則它將首先與SecurityFilterChain0的/ api / **模式匹配,因此,即使SecurityFilterChain0也與SecurityFilterChainn匹配,也只會調用它。假設都沒有匹配上,那麼默認使用 SecurityFilterChain n (即最後一個)
5.SecurityFilters的順序
SecurityFilters 通過SecurityFilterChain API插入到FilterChainProxy中。過濾器的順序很重要。雖然通常不必要知道Spring Security的過濾器的順序。但是,有時候瞭解還是有好處的,如下是Security Filters的完整列表:(藍色部分是較爲重要的過濾器)
-
ChannelProcessingFilter
-
ConcurrentSessionFilter
-
WebAsyncManagerIntegrationFilter
-
SecurityContextPersistenceFilter
-
HeaderWriterFilter
-
CorsFilter
-
CsrfFilter
-
LogoutFilter
-
OAuth2AuthorizationRequestRedirectFilter
-
Saml2WebSsoAuthenticationRequestFilter
-
X509AuthenticationFilter
-
AbstractPreAuthenticatedProcessingFilter
-
CasAuthenticationFilter
-
OAuth2LoginAuthenticationFilter
-
Saml2WebSsoAuthenticationFilter
-
ConcurrentSessionFilter
-
OpenIDAuthenticationFilter
-
DefaultLoginPageGeneratingFilter
-
DefaultLogoutPageGeneratingFilter
-
BearerTokenAuthenticationFilter
-
RequestCacheAwareFilter
-
SecurityContextHolderAwareRequestFilter
-
JaasApiIntegrationFilter
-
RememberMeAuthenticationFilter
-
AnonymousAuthenticationFilter
-
OAuth2AuthorizationCodeGrantFilter
-
SessionManagementFilter
-
SwitchUserFilter
6. Handling Security Exceptions
既然過濾鏈這麼多,那麼如果在哪一個環節碰到異常該怎麼拋出了,最理想的辦法當然是將異常以可閱讀,可定位的方式反饋給客戶端(如使用HTTP返回)
我們先來說說異常,Spring Security 中最常見的兩個異常是:
- AccessDeniedException (拒絕訪問異常)
- AuthenticationException (認證異常)
-
正常執行過濾鏈,如果發生異常,則判斷是什麼類型的異常
-
如果是AuthenticationException,則對用戶進行認證授權,步驟如下
-
將請求放入 RequestCache 中進行緩存,如果用戶認證通過,則從這裏重發此請求
-
AuthenticationEntryPoint用於從客戶端請求憑據
-
如果它是AccessDeniedException,則拒絕訪問。並調用AccessDeniedHandler處理拒絕的訪問
注:如果應用程序未引發AccessDeniedException或AuthenticationException,則ExceptionTranslationFilter不執行任何操作。
僞代碼如下:
try {
filterChain.doFilter(request, response);
} catch (AccessDeniedException | AuthenticationException e) {
if (!authenticated || e instanceof AuthenticationException) {
startAuthentication();
} else {
accessDenied();
}
}