web-security第二期:Spring Security 框架概覽

閱讀本文之前,我默認您已經掌握 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))一層層的過濾請求

filterchain

2.DelegatingFilterProxy

Servlet容器允許使用其自己的標準註冊Filters,但它不瞭解Spring定義的Bean,而DelegatingFilterProxy 在Servlet容器和Spring的ApplicationContext之間搭建了一個橋樑。使得Servlet可以註冊在SpringApplicationContext中定義的Filter,如下圖所示

delegatingfilterproxy

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中。

filterchainproxy

4. SecurityFilterChain

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

securityfilterchain

SecurityFilterChain中的Security FIlter 通常是Bean,但它們使用的是FilterChainProxy而不是DelegatingFilterProxy註冊的。 FilterChainProxy具有直接向Servlet容器或DelegatingFilterProxy註冊的許多優點。例如,它爲Spring Security的所有Servlet支持提供了一個起點。因此,如果您想對Spring Security的Servlet支持進行BUG排除,那麼在FilterChainProxy中添加調試點是一個很好的選擇。

實際上,FilterChainProxy 甚至可以確定來使用哪一個 SecurityFilterChain, 前提是你可以爲你的應用程序提供多個完全獨立的配置空間,如下圖所示:

multi 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

  • UsernamePasswordAuthenticationFilter

  • ConcurrentSessionFilter

  • OpenIDAuthenticationFilter

  • DefaultLoginPageGeneratingFilter

  • DefaultLogoutPageGeneratingFilter

  • DigestAuthenticationFilter

  • BearerTokenAuthenticationFilter

  • BasicAuthenticationFilter

  • RequestCacheAwareFilter

  • SecurityContextHolderAwareRequestFilter

  • JaasApiIntegrationFilter

  • RememberMeAuthenticationFilter

  • AnonymousAuthenticationFilter

  • OAuth2AuthorizationCodeGrantFilter

  • SessionManagementFilter

  • ExceptionTranslationFilter

  • FilterSecurityInterceptor

  • SwitchUserFilter

6. Handling Security Exceptions

既然過濾鏈這麼多,那麼如果在哪一個環節碰到異常該怎麼拋出了,最理想的辦法當然是將異常以可閱讀,可定位的方式反饋給客戶端(如使用HTTP返回)

我們先來說說異常,Spring Security 中最常見的兩個異常是:

  • AccessDeniedException (拒絕訪問異常)
  • AuthenticationException (認證異常)

exceptiontranslationfilter

 

  • number 1 正常執行過濾鏈,如果發生異常,則判斷是什麼類型的異常

  •  number 2 如果是AuthenticationException,則對用戶進行認證授權,步驟如下

    • 清除 SecurityContextHolder

    • 將請求放入 RequestCache 中進行緩存,如果用戶認證通過,則從這裏重發此請求

    • AuthenticationEntryPoint用於從客戶端請求憑據

  • number 3 如果它是AccessDeniedException,則拒絕訪問。並調用AccessDeniedHandler處理拒絕的訪問

注:如果應用程序未引發AccessDeniedException或AuthenticationException,則ExceptionTranslationFilter不執行任何操作。

僞代碼如下:

try {
    filterChain.doFilter(request, response); 
} catch (AccessDeniedException | AuthenticationException e) {
    if (!authenticated || e instanceof AuthenticationException) {
        startAuthentication(); 
    } else {
        accessDenied(); 
    }
}

 

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