安全過濾器鏈
Spring Security的web架構是完全基於標准的servlet過濾器的。它沒有在內部使用servlet或任何其他基於servlet的框架(比如spring mvc),所以它沒有與任何特定的web技術強行關聯。 它只管處理HttpServletRequest和HttpServletResponse,不關心請求時來自瀏覽器,web服務客戶端,HttpInvoker還是一個AJAX應用。
Spring Security維護了一個過濾器鏈,每個過濾器擁有特定的功能,過濾器需要服務也會對應添加和刪除。過濾器的次序是非常重要的,它們之間都有依賴關系。 如果你已經使用了命名空間配置,過濾器會自動幫你配置, 你不需要定義任何SpringBean,但是有時候你需要完全控制Spring過濾器鏈,因為你使用了命名空間沒有提供的特性,或者你需要使用你自己自定義的類。
1. DelegatingFilterProxy
當使用servlet過濾器時,你很需要在你的web.xml中聲明它們, 它們可能被servlet容器忽略。在SpringSecurity,過濾器類也是定義在xml中的spring bean, 因此可以獲得Spring的依賴注入機制和生命周期接口。spring的DelegatingFilterProxy提供了在 web.xml和applicationcontext之間的聯系。
當使用DelegatingFilterProxy,你會看到像web.xml文件中的這樣內容:
myFilter org.springframework.web.filter.DelegatingFilterProxy myFilter /*
注意這個過濾器其實是一個DelegatingFilterProxy,這個過濾器里沒有實現過濾器的任何邏輯。DelegatingFilterProxy做的事情是代理Filter的方法,從application context里獲得bean。這讓bean可以獲得spring web application context的生命周期支持,使配置較為輕便。bean必須實現javax.servlet.Filter接口,它必須和filter-name里定義的名稱是一樣的。查看DelegatingFilterProxy的javadoc獲得更多信息。
2. FilterChainProxy
現在應該清楚了,你可以聲明每個Spring Security過濾器bean,你在application context中需要的。把一個DelegatingFilterProxy入口添加到web.xml, 確認它們的次序是正確的。這是一種繁瑣的方式,會讓web.xml顯得十分雜亂,如果我們配置了太多過濾器的話。我們最好添加一個單獨的入口,在web.xml中,然后在application context中處理實體,管理我們的web安全bean。 這就是FilterChainProxy所做的事情。它使用DelegatingFilterProxy(就像上面例子中那樣),但是對應的class是org.springframework.security.web.FilterChainProxy。過濾器鏈是在application context中聲明的。這里有一個例子:
<sec:filter-chain-mappath-type="ant"><sec:filter-chainpattern=" ebservices="" *"="" filters="securityContextPersistenceFilterWithASCFalse,basicAuthenticationFilter, exceptionTranslationFilter,filterSecurityInterceptor">
你可能注意到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的名字,它是由命名空間創建的。
2.1. 繞過過濾器鏈
通過命名空間,你可以使用filters = "none",來提供一個過濾器bean列表。這會朝向請求模式,使用安全過濾器鏈整體。注意任何匹配這個模式的路徑不會有任何授權或校驗的服務起作用,它們是可以自由訪問的。
3. 過濾器順序
定義在web.xml里的過濾器的順序是非常重要的。不論你實際使用的是哪個過濾器, 的順序應該像下面這樣:
ChannelProcessingFilter,因為它可能需要重定向到其他協議。
ConcurrentSessionFilter,因為它不使用SecurityContextHolder功能,但是需要更新SessionRegistry 來從主體中放映正在進行的請求。
SecurityContextPersistenceFilter,這樣 SecurityContext可以在web請求的開始階段通過SecurityContextHolder建立,然后SecurityContext的任何修改都會在web請求結束的時候(為下一個web請求做准備)復制到HttpSession中。
驗證執行機制 - UsernamePasswordAuthenticationFilter,CasAuthenticationFilter, BasicAuthenticationFilter 等等 - 這樣SecurityContextHolder 可以被修改,並包含一個合法的 Authentication請求標志。
SecurityContextHolderAwareRequestFilter,如果,你使用它,把一個SpringSecurity提醒HttpServletRequestWrapper安裝到你的servlet容器里。
RememberMeAuthenticationFilter,這樣如果之前的驗證執行機制沒有更新SecurityContextHolder,這個請求提供了一個可以使用的remember-me服務的cookie,一個對應的已保存的Authentication對象會被創建出來。
AnonymousAuthenticationFilter,這樣如果之前的驗證執行機制沒有更新SecurityContextHolder,會創建一個匿名Authentication對象。
ExceptionTranslationFilter,用來捕捉 SpringSecurity異常,這樣,可能返回一個HTTP錯誤響應,或者執行一個對應的AuthenticationEntryPoint。
FilterSecurityInterceptor,保護web URI。
4. 使用其他過濾器 —— 基於框架
如果你在使用SiteMesh,確認Spring Security過濾器在SiteMesh過濾器之前調用。這可以保證SecurityContextHolder為每個SiteMesh渲染器及時創建。
5. 其他配置例子
方法一:
web.xml配置一個
DelegatingFilterProxy
org.springframework.web.filter.DelegatingFilterProxy
targetBeanName
myFilter //自己過濾器的名字
targetFilterLifecycle
true
DelegatingFilterProxy
/*
方法二:
web.xml配置一個
myFilter
org.springframework.web.filter.DelegatingFilterProxy
targetFilterLifecycle
true
myFilter
/*
方法一或者二不同的地方就是在web.xml中的寫法不同而已沒有太大的區別,配完web.xml之后還要配置applicationContext.xml中的bean。
applicationContext.xml配置:
//指名具體的filter類
<propertyname="service"> //需要注入的具體參數
<refbean="service">
//這里的service封裝了所有對數據庫的操作
<propertyname="target">
<beanclass="com.maimaiche.service.maimaicheserviceimpl">
......
--------------------------------------------------
1、web.xml
14379709.67309
- appFilters
- class >org.springframework.web.filter.DelegatingFilterProxy class>
- targetFilterLifecycle
- true
- appFilters
- /*
2、applicationContext-filter.xml
- "appFilters" class = "org.springframework.security.util.FilterChainProxy" >
- "ant" >
- "characterEncodingFilter,commonParamsFilter"
- pattern="/**"/>
- "characterEncodingFilter" class="org.springframework.web.filter.CharacterEncodingFilter">
- "encoding" value="UTF-8"/>
- "forceEncoding" value="true"/>
- "commonParamsFilter" class="com.renren.wap.fuxi.filter.CommonParamsFilter"/>