Spring Security初見

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請求的處理程序的流程來認識它。如下圖:

Figure 1. FilterChain
客戶端發送一個請求給應用程序,接着容器將創建一個包含FilterFilterChain,並且Servlet將會基於請求URI的路徑處理HttpServletRequest。在一個Spring MVC應用程序中,Servlet是一個DispatcherServlet的實例。至少一個Servlet能處理單個HttpServletRequestHttpServletResponse。不管怎麼樣,至少一個Filter會被用於:

  • 防止下游FilterServlet被調用。在這種情況下,過濾器通常將編寫HttpServletResponse
  • 修改下游FilterServlet使用的HttpServletRequestHttpServletResponse

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會影響下游FilterServlet,因此調用每個Filter的順序非常重要。

DelegatingFilterProxy

Spring 提供了一個Filter的實現,名字叫做DelegatingFilterProxy。它允許橋街Servlet容器的生命週期和Spring的ApplicationContext。Servlet容器允許使用自己的標準來註冊Filter,但是它不知道Spring已定義的Bean。能通過標準的Servlet容器機制註冊DelegatingFilterProxy,並將所有工作委託給實現了Filter接口的Spring Bean

以下是DelegatingFilterProxy如何適應FilterFilterChain

Figure 2. DelegatingFilterProxy
DelegatingFilterProxyApplicationContext中查找 Bean Filter0 ,然後調用Bean Filter0DelegatingFilterProxy的僞代碼可以在下面看到。

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的另一個好處是允許延遲查找Filterbean實例。這很重要,因爲容器需要在容器啓動之前註冊Filter實例。Spring通常使用ContextLoaderListener加載 Spring Beans,這個操作會在Filter實例被註冊完成之後纔會完成。

FilterChainProxy

Spring Security的Servlet支持包含在FilterChainProxy中。FilterChainProxy是一個由Spring Security 提供的特別的Filter。它通過SecurityFilterChain來代理各種Filter實例。因爲SecurityFilterChain是一個Bean,它是被包裝在DelegatingFilterProxy中。

Figure 3. FilterChainProxy

SecurityFilterChain

FilterChainProxy使用SecurityFilterChain確定應對此請求調用哪些Spring Security過濾器。

Figure 4. SecurityFilterChain

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。您的應用程序可以爲不同的片提供完全獨立的配置。

Figure 5. Multiple SecurityFilterChain

在多個SecurityFilterChain情況中,由FilterChainProxy 決定應使用哪個SecurityFilterChain。僅匹配上的第一個SecurityFilterChain將被調用。如果請求的URL是/api/messages/,則將首先在 SecurityFilterChain0/api/ **模式下進行匹配,因此即使在 SecurityFilterChainn 上也進行了匹配,也只會調用SecurityFilterChain0。如果請求的URL是/messages/,則在 SecurityFilterChain0/api/**模式下將不匹配,因此FilterChainProxy將繼續嘗試每個SecurityFilterChain 。假設沒有其他匹配 SecurityFilterChainnSecurityFilterChain實例將被調用。

請注意,SecurityFilterChain0 僅配置了三個安全Filter實例。但是,SecurityFilterChainn 配置了四個Filter。重要的是要注意,每個SecurityFilterChain可以是唯一的,並且可以獨立配置。實際上,如果應用程序希望Spring Security忽略某些請求,則SecurityFilterChain可能沒有安全Filter

安全Filters

安全過濾器通過SecurityFilterChain API 被插入到FilterChainProxyFilter的順序很重要。通常不必知道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中。

在這裏插入圖片描述

  1. 首先,ExceptionTranslationFilter調用FilterChain.doFilter(request,response)來調用應用程序的其餘部分。

  2. 如果用糊沒被認證或者有一個AuthenticationException,則啓動身份驗證。

    • SecurityContextHolder會被清除,這個裏面有已認證的信息
    • HttpServletRequest會被緩存在 RequestCache。當用戶成功認證,RequestCache將會被用來重放原始請求。
    • AuthenticationEntryPoint被用來從客戶端請求憑據。例如,它可能重定向到登錄頁面或發送WWW-Authenicate標頭。
  3. 否則,如果它是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或安全方法)拋出一個AccessDeniedExceptionAuthenticationException 異常,那麼它將在這裏被捕獲。
如果用戶未被認證或者它是一個AuthenticationException,那麼將會開始認證。
否則,就拒絕訪問。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章