spring security系列二:過濾器機制

spring security框架的過濾器是基於基礎的filter來實現,這樣它能夠不須要依賴任何web框架,甚至連spring mvc框架都不須要依賴,這樣整個spring security過濾器就會變得異常的輕量級和無侵入性。java

spring security處理請求流程

clipboard.png

用戶發起請求,認證管理器(Authentication Manager)會發起攔截,驗證用戶發起請求時的一些憑證信息,未經過驗證信息審覈的那麼返回給用戶,經過審覈的,那麼繼續進行請求訪問,訪問頁面以前,會被訪問決策管理(Access Decision Manager)攔截,訪問決策管理器驗證用戶是否有訪問頁面的權限,若是有,那麼繼續到訪問頁面。web

另外spring security是經過委託代理(delegates)的方式去實現過濾器鏈的,
首先先經過過濾器攔截用戶的請求,攔截後經過servlet來進行處理,若是處理成功那麼進行正常訪問,在返回給用戶一個請求。正則表達式

clipboard.png

spring security 內置攔截器順序及用途

一、ChannelProcessingFilter,使用它由於咱們可能會指向不一樣的協議(如:Http,Https)spring

二、SecurityContextPersistenceFilter,負責從SecurityContextRepository 獲取或存儲 SecurityContext。SecurityContext 表明了用戶安全和認證過的session安全

三、ConcurrentSessionFilter,使用SecurityContextHolder的功能,更新來自「安全對象」不間斷的請求,進而更新SessionRegistrycookie

四、認證進行機制,UsernamePasswordAuthenticationFilter,CasAuthenticationFilter,BasicAuthenticationFilter等等--SecurityContextHolder可能會修改含有Authentication這樣認證信息的token值session

五、SecurityContextHolderAwareRequestFilter,若是你想用它的話,須要初始化spring security中的HttpServletRequestWrapper到你的servlet容器中。mvc

六、JaasApiIntegrationFilter,若是JaasAuthenticationToken在SecurityContextHolder的上下文中,在過濾器鏈中JaasAuthenticationToken將做爲一個對象。app

七、RememberMeAuthenticationFilter,若是尚未新的認證程序機制更新SecurityContextHolder,而且請求已經被一個「記住我」的服務替代,那麼將會有一個Authentication對象將存放到這(就是 已經做爲cookie請求的內容)。框架

八、AnonymousAuthenticationFilter,若是沒有任何認證程序機制更新SecurityContextHolder,一個匿名的對象將存放到這。

九、ExceptionTranslationFilter,爲了捕獲spring security的錯誤,因此一個http響應將返回一個Exception或是觸發AuthenticationEntryPoint。

十、FilterSecurityInterceptor,當鏈接被拒絕時,保護web URLS而且拋出異常。

安全過濾器鏈

Spring Security維護了一個過濾器鏈,每一個過濾器擁有特定的功能,過濾器須要服務也會對應添加和刪除。 過濾器的次序是很是重要的,它們之間都有依賴關係。

DelegatingFilterProxy

當使用servlet過濾器時,你很須要在你的web.xml中聲明它們, 它們可能被servlet容器忽略。在Spring Security,過濾器類也是定義在xml中的spring bean, 所以能夠得到Spring的依賴注入機制和生命週期接口。 spring的DelegatingFilterProxy提供了在 web.xml和application context之間的聯繫。

當使用DelegatingFilterProxy,你會看到像 web.xml文件中的這樣內容:

<filter>
    <filter-name>myFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>myFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

注意這個過濾器實際上是一個DelegatingFilterProxy,這個過濾器裏沒有實現過濾器的任何邏輯。 DelegatingFilterProxy作的事情是代理Filter的方法,從application context裏得到bean。 這讓bean能夠得到spring web application context的生命週期支持,使配置較爲輕便。 bean必須實現javax.servlet.Filter接口,它必須和filter-name裏定義的名稱是同樣的。

clipboard.png

FilterChainProxy

若是咱們把全部的過濾器做爲一個DelegatingFilterProxy入口添加到web.xml, 確認它們的次序是正確的。 這是一種繁瑣的方式,會讓web.xml顯得十分雜亂,若是咱們配置了太多過濾器的話。 咱們最好添加一個單獨的入口,在web.xml中,而後在application context中處理實體, 管理咱們的web安全bean。 這就是FilterChainProxy所作的事情。它使用DelegatingFilterProxy (就像上面例子中那樣),可是對應的class是org.springframework.security.web.FilterChainProxy。 過濾器鏈是在application context中聲明的。這裏有一個例子:

<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
  <sec:filter-chain-map path-type="ant">
     <sec:filter-chain pattern="/webServices/**" filters="
           securityContextPersistenceFilterWithASCFalse,
           basicAuthenticationFilter,
           exceptionTranslationFilter,
           filterSecurityInterceptor" />
     <sec:filter-chain pattern="/**" filters="
           securityContextPersistenceFilterWithASCFalse,
           formLoginFilter,
           exceptionTranslationFilter,
           filterSecurityInterceptor" />
  </sec:filter-chain-map>
</bean>

你可能注意到FilterSecurityInterceptor聲明的不一樣方式。 命名空間元素filter-chain-map被用來設置安全過濾器鏈。 它映射一個特定的URL模式,到過濾器鏈中,從bean名稱來定義的filters元素。 它同時支持正則表達式和ant路徑,而且只使用第一個出現的匹配URI。 在運行階段FilterChainProxy會定位當前web請求匹配的第一個URI模式,由filters屬性指定的過濾器bean列表將開始處理請求。 過濾器會按照定義的順序依次執行,因此你能夠對處理特定URL的過濾器鏈進行徹底的控制。

你可能注意到了,咱們在過濾器鏈裏聲明瞭兩個SecurityContextPersistenceFilter(ASC是allowSessionCreation的簡寫,是SecurityContextPersistenceFilter的一個屬性)。 由於web服務歷來不會在請求裏帶上jsessionid,爲每一個用戶代理都建立一個HttpSession徹底是一種浪費。 若是你須要構建一個高等級最高可擴展性的系統,咱們推薦你使用上面的配置方法。 對於小一點兒的項目,使用一個HttpSessionContextIntegrationFilter(讓它的allowSessionCreation默認爲true)就足夠了。

在有關聲明週期的問題上,若是這些方法被FilterChainProxy本身調用,FilterChainProxy會始終根據下一層的Filter代理init(FilterConfig)和destroy()方法。 這時,FilterChainProxy會保證初始化和銷燬操做只會在Filter上調用一次, 而無論它在過濾器鏈中被聲明瞭多少次)。你控制着全部的抉擇,好比這些方法是否被調用 或targetFilterLifecycle初始化參數DelegatingFilterProxy。 默認狀況下,這個參數是false,servlet容器生命週期調用不會傳播到 DelegatingFilterProxy。

當咱們瞭解如何使用命名控制配置構建web安全。 咱們使用一個DelegatingFilterProxy,它的名字是「springSecurityFilterChain」。 你應該如今能夠看到FilterChainProxy的名字,它是由命名空間建立的。

clipboard.png

clipboard.png

自定義Filter

自定義Filter 建議繼承 GenericFilterBean

clipboard.png

配置自定義 Filter 在 Spring Security 過濾器鏈中的位置

clipboard.png

HttpSecurity 有三個經常使用方法來配置:

  • addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter)在 beforeFilter 以前添加 filter
  • addFilterAfter(Filter filter, Class<? extends Filter> afterFilter)在 afterFilter 以後添filter
  • addFilterAt(Filter filter, Class<? extends Filter> atFilter)在 atFilter 相同位置添加 filter,此 filter 不覆蓋 filter.

經過在不一樣 Filter 的 doFilter() 方法中加斷點調試,能夠判斷哪一個 filter 先執行,從而判斷 filter 的執行順序 。

相關文章
相關標籤/搜索