Spring Security的作用
Spring Security是Spring全家桶裏面的一個項目,提供認證、授權以及應對一般攻擊的保護。憑藉對命令時和響應式應用程序的一流支持,它是保護基於Spring開發的應用程序的實際標準。
即這個項目主要提供3個功能:
- 認證 authentication
- 授權 authorization
- 保護 protection (一般的攻擊)
介紹
Spring Security 本質上使用的是標準Servlet中的Fliter
與Servlet容器集成。這意味着它可以與任何在Servlet容器中運行的任何應用一起工作。更準確地說,你可以在沒有使用Spring的基於Servlet的應用中使用Spring Sercurity。
Spring Security 的大體原理是通過一個標準過濾器,進入到自己體系的過濾器中來,通過層層過濾,將攻擊者的請求過濾掉。
比如最簡單的用戶名密碼,那麼就有一個org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
實現了默認的用戶名密碼校驗。可以在該類的attemptAuthentication
方法中設置斷點來觀察調用堆棧以追蹤源碼邏輯。
原理
本節可在初步搭建好工程之後再來看,會更加能理解。因爲原理性的東西一般比較抽象。
回顧Filter
(圖片以及表述均來自官網)
Spring Security的Servlet支持是建立在ServletFilter
之上,所以可以通過回顧Filter
原來對於單個HTTP請求的處理程序的流程來認識它。如下圖:
客戶端發送一個請求給應用程序,接着容器將創建一個包含Filter
的FilterChain
,並且Servlet
將會基於請求URI的路徑處理HttpServletRequest
。在一個Spring MVC應用程序中,Servlet
是一個DispatcherServlet
的實例。至少一個Servlet
能處理單個HttpServletRequest
和HttpServletResponse
。不管怎麼樣,至少一個Filter
會被用於:
- 防止下游
Filter
或Servlet
被調用。在這種情況下,過濾器通常將編寫HttpServletResponse
- 修改下游
Filter
或Servlet
使用的HttpServletRequest
或HttpServletResponse
Filter
的功能來自傳遞給它的FilterChain
。
用例
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// do something before the rest of the application
chain.doFilter(request, response); // invoke the rest of the application
// do something after the rest of the application
}
由於Filter
會影響下游Filter
和Servlet
,因此調用每個Filter
的順序非常重要。
DelegatingFilterProxy
Spring 提供了一個Filter
的實現,名字叫做DelegatingFilterProxy
。它允許橋街Servlet容器的生命週期和Spring的ApplicationContext
。Servlet容器允許使用自己的標準來註冊Filter
,但是它不知道Spring已定義的Bean
。能通過標準的Servlet容器機制註冊DelegatingFilterProxy
,並將所有工作委託給實現了Filter
接口的Spring Bean
。
以下是DelegatingFilterProxy
如何適應Filter
和FilterChain
。
DelegatingFilterProxy
從ApplicationContext
中查找 Bean Filter0 ,然後調用Bean Filter0。DelegatingFilterProxy
的僞代碼可以在下面看到。
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);
}
DelegatingFilterProxy
的另一個好處是允許延遲查找Filter
bean實例。這很重要,因爲容器需要在容器啓動之前註冊Filter
實例。Spring通常使用ContextLoaderListener
加載 Spring Beans,這個操作會在Filter
實例被註冊完成之後纔會完成。
FilterChainProxy
Spring Security的Servlet支持包含在FilterChainProxy
中。FilterChainProxy
是一個由Spring Security 提供的特別的Filter
。它通過SecurityFilterChain
來代理各種Filter
實例。因爲SecurityFilterChain
是一個Bean,它是被包裝在DelegatingFilterProxy
中。
SecurityFilterChain
FilterChainProxy
使用SecurityFilterChain
確定應對此請求調用哪些Spring Security過濾器。
SecurityFilterChain中的SecurityFilterChain
通常是Bean,但它們是使用FilterChainProxy
而不是DelegatingFilterProxy
註冊的。 FilterChainProxy
具有直接向Servlet容器或DelegatingFilterProxy
註冊的優點。
首先,它爲Spring Security的所有Servlet支持提供了一個起點。因此,如果您想對Spring Security的Servlet支持進行故障排除,那麼可以在FilterChainProxy中添加調試點。
其次,由於FilterChainProxy
對於Spring Security的使用至關重要,因此它可以執行不被視爲可選任務的任務。例如,它清除SecurityContext以避免內存泄漏。它還使用Spring Security的HttpFirewall
來保護應用程序免受某些類型的攻擊。
此外,它在確定何時調用SecurityFilterChain
時提供了更大的靈活性。在Servlet容器中,僅根據URL調用Filter
。而且,FilterChainProxy
可以利用RequestMatcher
接口,根據HttpServletRequest
中的任何內容確定調用。
實際上,FilterChainProxy
可用於確定應使用哪個SecurityFilterChain
。您的應用程序可以爲不同的片提供完全獨立的配置。
在多個SecurityFilterChain
情況中,由FilterChainProxy 決定應使用哪個SecurityFilterChain
。僅匹配上的第一個SecurityFilterChain
將被調用。如果請求的URL是/api/messages/
,則將首先在 SecurityFilterChain0 的/api/ **
模式下進行匹配,因此即使在 SecurityFilterChainn 上也進行了匹配,也只會調用SecurityFilterChain0。如果請求的URL是/messages/
,則在 SecurityFilterChain0 的/api/**
模式下將不匹配,因此FilterChainProxy
將繼續嘗試每個SecurityFilterChain
。假設沒有其他匹配 SecurityFilterChainn 的SecurityFilterChain
實例將被調用。
請注意,SecurityFilterChain0 僅配置了三個安全Filter
實例。但是,SecurityFilterChainn 配置了四個Filter
。重要的是要注意,每個SecurityFilterChain
可以是唯一的,並且可以獨立配置。實際上,如果應用程序希望Spring Security忽略某些請求,則SecurityFilterChain
可能沒有安全Filter
。
安全Filters
安全過濾器通過SecurityFilterChain
API 被插入到FilterChainProxy
。Filter
的順序很重要。通常不必知道Spring Security的Filters
順序,但是,又是都瞭解順序是有益的。
以下是Spring Security Filters訂購的完整列表:
- ChannelProcessingFilter
- WebAsyncManagerIntegrationFilter
- SecurityContextPersistenceFilter
- HeaderWriterFilter
- CorsFilter
- CsrfFilter
- LogoutFilter
- OAuth2AuthorizationRequestRedirectFilter
- Saml2WebSsoAuthenticationRequestFilter
- X509AuthenticationFilter
- AbstractPreAuthenticatedProcessingFilter
- CasAuthenticationFilter
- OAuth2LoginAuthenticationFilter
- Saml2WebSsoAuthenticationFilter
- UsernamePasswordAuthenticationFilter
- OpenIDAuthenticationFilter
- DefaultLoginPageGeneratingFilter
- DefaultLogoutPageGeneratingFilter
- ConcurrentSessionFilter
- DigestAuthenticationFilter
- BearerTokenAuthenticationFilter
- BasicAuthenticationFilter
- RequestCacheAwareFilter
- SecurityContextHolderAwareRequestFilter
- JaasApiIntegrationFilter
- RememberMeAuthenticationFilter
- AnonymousAuthenticationFilter
- OAuth2AuthorizationCodeGrantFilter
- SessionManagementFilter
- ExceptionTranslationFilter
- FilterSecurityInterceptor
- SwitchUserFilter
處理安全異常
ExceptionTranslationFilter允許將AccessDeniedException和AuthenticationException轉換爲HTTP響應。
ExceptionTranslationFilter作爲安全Filter
之一插入到FilterChainProxy中。
-
首先,
ExceptionTranslationFilter
調用FilterChain.doFilter(request,response)
來調用應用程序的其餘部分。 -
如果用糊沒被認證或者有一個
AuthenticationException
,則啓動身份驗證。SecurityContextHolder
會被清除,這個裏面有已認證的信息HttpServletRequest
會被緩存在RequestCache
。當用戶成功認證,RequestCache
將會被用來重放原始請求。AuthenticationEntryPoint
被用來從客戶端請求憑據。例如,它可能重定向到登錄頁面或發送WWW-Authenicate
標頭。
-
否則,如果它是
AccessDeniedException
,則拒絕訪問。AccessDeniedHandler
被調用以處理拒絕的訪問。
如果應用程序未引發AccessDeniedExeption或AuthenticationException,則ExceptionTranlationFilter不執行任何操作。
ExceptionTranslationFilter的僞代碼如下所示:
try {
filterChain.doFilter(request, response);
} catch (AccessDeniedException | AuthenticationException e) {
if (!authenticated || e instanceof AuthenticationException) {
startAuthentication();
} else {
accessDenied();
}
}
FilterChain.doFilter(request, response)
等效於調用應用程序的其餘部分。這意味着如果應用程序的另一部分(即FilterSecurityInterceptor
或安全方法)拋出一個AccessDeniedException
或 AuthenticationException
異常,那麼它將在這裏被捕獲。
如果用戶未被認證或者它是一個AuthenticationException
,那麼將會開始認證。
否則,就拒絕訪問。