使用一個新的框架之前,首先我們來認識一下springSecurity,畢竟框架這種東西有時靠不住,所以學到他的思想纔是最重要的,很多人都知道這麼用,具體爲什麼,沒有人告訴我們,首先我們從最基本的看起,瞭解一些入門知識是有必要的:
1.4.1.1. Core -spring-security-core.jar
包含了核心認證和權限控制類和接口, 運程支持和基本供應 API。使用 spring Security
所必須的。支持單獨運行的應用, 遠程客戶端,方法(服務層)安全和 JDBC 用戶供應。
包含頂級包:
org.springframework.security.core
org.springframework.security.access
org.springframework.security.authentication
org.springframework.security.provisioning
org.springframework.security.remoting
1.4.1.2. Web -spring-security-web.jar
包含過濾器和對應的 web 安全架構代碼。任何需要依賴 servlet API 的。 你將需要它,如
果 你 需 要 Spring Security Web 認 證 服 務 和 基 於 URL 的 權 限 控 制 。 主 包 是
org.springframework.security.web。
1.4.1.3. Config -spring-security-config.jar
包含安全命名控制解析代碼(因此我們不能直接把它用在你的應用中)。你需要它, 如果使用了SpringSecurityXML命名控制來進行配置。主包是
org.springframework.security.config。
1.4.1.4. LDAP -spring-security-ldap.jar
LDAP 認證和實現代碼,如果你需要使用 LDAP 認證或管理 LDAP 用戶實體就是必須的。頂
級包是org.springframework.security.ldap。
1.4.1.5. ACL -spring-security-acl.jar
處理領域對象 ACL 實現。用來提供安全給特定的領域對象實例,在你的應用中。 頂級包是
org.springframework.security.acls。
1.4.1.6. CAS -spring-security-cas-client.jar
Spring Security 的 CAs 客戶端集成。如果你希望使用 Spring Security web 認證 整合
一個 CAS 單點登錄服務器。頂級包是 org.springframework.security.cas。
1.4.1.7. OpenID -spring-security-openid.jar
OpenID web 認 證 支 持 。 用 來 認 證 用 戶 , 通 過 一 個 外 部 的 OpenID 服 務 。
org.springframework.security.openid。需要 OpenID4Java。
在許多例子裏,你會看到(在示例中)應用,我們通常使用”security”作爲默認的命名空間,
而不是”beans”,這意味着我們可以省略所有 security 命名空間元素的前綴,使上下文更
容易閱讀。 如果你把應用上下文分割成單獨的文件,讓你的安全配置都放到其中一個文件
裏,這樣更容易使用這種配置方法。 你的安全應用上下文應該像這樣開頭:
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">
...
</beans:beans>
2.1.1. 命名空間的設計
命名空間被用來設計成,處理框架內最常見的功能,提供一個簡化和簡潔的語法,使他們在
一個應用程序裏。 這種設計是基於框架內的大型依賴,可以分割成下面這些部分:
Web/HTTP 安全 - 最複雜的部分。設置過濾器和相關的服務 bean 來應用框架驗證機制,
保護 URL,渲染登錄和錯誤頁面還有更多。
業務類(方法)安全 - 可選的安全服務層。
AuthenticationManager - 通過框架的其它部分,處理認證請求。
AccessDecisionManager - 提供訪問的決定,適用於 web 以及方法的安全。一個默認的
主體會被註冊,但是你也可以選擇自定義一個,使用正常的 spring bean 語法進行聲明。
AuthenticationProviders - 驗證管理器驗證用戶的機制。 該命名空間提供幾種標準選
項,意味着使用傳統語法添加自定義 bean。
UserDetailsService - 密切相關的認證供應器,但往往也需要由其他 bean 需要。
2.2.1. 配置 web.xml
我們要做的第一件事是把下面的 filter 聲明添加到 web.xml 文件中:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
這是爲 Spring Security 的 web 機制提供了一個調用鉤子。 DelegatingFilterProxy 是一個 SpringFramework 的類,它可以代理一個 applicationcontext 中定義的 Springbean 所實現的 filter。 這種情況下,bean 的名字是”springSecurityFilterChain”,這是由命名空間創建的用於處理 web 安全的一個內部的機制。 注意,你不應該自己使用這個bean 的 名 字 。 一 旦 你 把 這 個 添 加 到 你 的 web.xml 中 , 你 就 準 備 好 開 始 編 輯 呢 applicationcontext 文件了。 web 安全服務是使用元素配置的。
2.2.2. 最小 配置
只需要進行如下配置就可以實現安全配置:
[html] view plain copy print?
<authentication-manager>
<authentication-provider>
<user-service>
<user name="jimi" password="jimispassword" authorities="ROLE_USER,ROLE_ADMIN" />
<usernameusername="bob" password="bobspassword" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
這些過濾器的位置都是預定義好的。
元 素 創 建 了 一 個 DaoAuthenticationProvider bean ,
元素創建了一個 InMemoryDaoImpl。 所有 authentication-provider元 素 必 須 作 爲 的 子 元 素 ,它 創 建 了 一 個ProviderManager,並把 authentication provider 註冊到它裏面。 你可以在命名空間附錄中找到關於創建這個 bean 的更新信息。很值得去交叉檢查一下這裏,如果你希望開始理解框架中哪些是重要的類 以及它們是如何使用的,特別是如果你希望以後做一些自定義工作。
上面的配置定義了兩個用戶,他們在應用程序中的密碼和角色(用在權限控制上)。 也可以從一個標準 properties 文件中讀取這些信息,使用 user-service 的 properties 屬性。 參考 in-memoryauthentication 獲得更多信息。 使用元素意味着 用 戶 信 息 將 被 認 證 管 理 用 作 處 理 認 證 請 求 。你 可 以 擁 有 多 個元素來定義不同的認證數據, 每個會被需要時使用。
現在,你可以啓動程序,然後就會進入登錄流程了。 試試這個,或者試試工程裏的“tutorial”
例子. 上述配置實際上把很多服務添加到了程序裏,因爲我們使用了 auto-config 屬性。
比如,表單登錄和”remember-me”服務自動啓動了。
2.2.2.1. auto-config 包含了什麼?
我們在上面用到的 auto-config 屬性,其實是下面這些配置的縮寫:
<http>
<form-login />
<http-basic />
<logout />
</http>
這些元素分別與 form-login,基本認證和註銷處理對應。
他們擁有各自的屬性,來改
變他們的具體行爲。
2.2.2.2. 表單和基本登錄選項
你也許想知道,在需要登錄的時候,去哪裏找這個登錄頁面,到現在爲止我們都沒有提到任
何的 HTML 或 JSP 文件。 實際上,如果我們沒有確切的指定一個頁面用來登錄, Spring
Security 會自動生成一個,基於可用的功能,爲這個 URL 使用標準的數據,處理提交的登
錄,然後在登陸後發送到默認的目標 URL。 然而,命名空間提供了許多支持,讓你可以自
定義這些選項。 比如,如果你想實現自己的登錄頁面,你可以使用:
<http auto-config='true'>
<intercept-urlpatternintercept-urlpattern="/login.jsp*"access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-urlpatternintercept-urlpattern="/**" access="ROLE_USER" />
<form-loginlogin-pageform-loginlogin-page='/login.jsp'/>
</http>
注意,你依舊可以使用 auto-config。 這個 form-login 元素會覆蓋默認的設置。 也要注
意我們需要添加額外的 intercept-url 元素,指定用來做登錄的頁面的 URL, 這些 URL 應
該可以被匿名訪問。[4] 否則,這些請求會被/**部分攔截,它沒法訪問到登錄頁面。 這是
一個很常見的配置錯誤,它會導致系統出現無限循環。 Spring Security 會在日誌中發出
一個警告,如果你的登錄頁面是被保護的。 也可能讓所有的請求都匹配特定的模式,通過
完全的安全過濾器鏈:
<http auto-config='true'>
<intercept-urlpatternintercept-urlpattern="/css/**" filters="none"/>
<intercept-urlpatternintercept-urlpattern="/login.jsp*" filters="none"/>
<intercept-urlpatternintercept-urlpattern="/**" access="ROLE_USER" />
<form-login login-page='/login.jsp'/>
</http>
主要的是意識到這些請求會被完全忽略,對任何 Spring Security 中 web 相關的配置,或
額外的屬性,比如requires-channel, 所以你會不能訪問當前用戶信息,或調用被保護
方法,在請求過程中。 使用access=’IS_AUTHENTICATED_ANONYMOUSLY’作爲一個
選擇方式 如果你還想要安全過濾器鏈起作用。
如果你希望使用基本認證,代替表單登錄,可以把配置改爲:
<http auto-config='true'>
<intercept-urlpatternintercept-urlpattern="/css/**" filters="none"/>
<intercept-urlpatternintercept-urlpattern="/login.jsp*" filters="none"/>
<intercept-urlpatternintercept-urlpattern="/**" access="ROLE_USER" />
<form-login login-page='/login.jsp'/>
</http>
基本身份認證會被優先用到,在用戶嘗試訪問一個受保護的資源時,用來提示用戶登錄。 在
這種配置中,表單登錄依然是可用的,如果你還想用的話,比如,把一個登錄表單內嵌到其
他頁面裏。
2.2.2.2.1. 設置一個默認的提交登陸目標
如果在進行表單登陸之前,沒有試圖去訪問一個被保護的資源,default-target-url 就會起作用。
這 是 用 戶 登 陸 後 會 跳 轉 到 的 URL , 默 認 是 “/” 。你也可以把always-use-default-target 屬性配置成”true”,這樣用戶就會一直跳轉到這一頁(無論登陸是“跳轉過來的”還是用戶特定進行登陸)。 如果你的系統一直需要用戶從首頁進入,就可以使用它了,比如:
<http>
<intercept-urlpatternintercept-urlpattern='/login.htm*' filters='none'/>
<intercept-urlpatternintercept-urlpattern='/**' access='ROLE_USER' />
<form-loginlogin-pageform-loginlogin-page='/login.htm' default-target-url='/home.htm'
always-use-default-target='true'/>
</http>
2.2.3. 使用其他認證提供器
現實中,你會需要更大型的用戶信息源,而不是寫在 application context 裏的幾個名字。多數情況下,你會想把用戶信息保存到數據庫或者是 LDAP 服務器裏。 LDAP 命名空間會在 LDAP 章裏詳細討論,所以我們這裏不會講它。 如果你自定義了一個 Spring Security的UserDetailsService實現,在你applicationcontext中 名 叫”myUserDetailsService”,然後你可以使用下面的驗證。
<authentication-manager>
<authentication-provideruser-service-refauthentication-provideruser-service-ref='myUserDetailsService'/>
</authentication-manager>
如果你想用數據庫,可以使用下面的方式
<authentication-manager>
<authentication-provider>
<jdbc-user-servicedata-source-refjdbc-user-servicedata-source-ref="securityDataSource"/>
</authentication-provider>
</authentication-manager>
這裏的“securityDataSource”就是 DataSource bean 在 application context 裏的名
字,它指向了包含着 Spring Security 用戶信息的表。 另外,你可以配置一個 Spring
SecurityJdbcDaoImpl bean,使用user-service-ref 屬性指定:
<authentication-manager>
<authentication-provideruser-service-refauthentication-provideruser-service-ref='myUserDetailsService'/>
</authentication-manager>
<beans:beanidbeans:beanid="myUserDetailsService"
class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<beans:propertynamebeans:propertyname="dataSource" ref="dataSource"/>
</beans:bean>
你也可以使用標準的AuthenticationProvider 類,像下面
<authentication-manager>
<authentication-providerrefauthentication-providerref='myAuthenticationProvider'/>
</authentication-manager>
這裏myAuthenticationProvider 是你的 applicationcontext 中的一個 bean 的名字,
它實現了AuthenticationProvider。 查看 Section 2.6, “驗證管理器和命名空間 ”瞭解更多信
息,AuthenticationManager 使用命名空間在 Spring Security 中是如何配置的。
2.2.3.1. 添加一個密碼編碼器
你的密碼數據通常要使用一種散列算法進行編碼。 使用元素支持
這個功能。 使用 SHA 加密密碼,原始的認證供應器配置,看起來就像這樣:
<authentication-manager>
<authentication-provider>
<password-encoderhashpassword-encoderhash="sha"/>
<user-service>
<usernameusername="jimi"password="d7e6351eaa13189a5a3641bab846c8e8c69ba39f"
authorities="ROLE_USER,ROLE_ADMIN" />
<usernameusername="bob" password="4e7421b1b8765d8f9406d87e7cc6aa784c4ab97f"
authorities="ROLE_USER"/>
</user-service>
</authentication-provider>
</authentication-manager>
在使用散列密碼時,用鹽值防止字典攻擊是個好主意,Spring Security 也支持這個功能。
理想情況下,你可能想爲每個用戶隨機生成一個鹽值,不過,你可以使用 從
UserDetailsService 讀取出來的 UserDetails 對象中的屬性。 比如,使用 username 屬
性,你可以這樣用:
<password-encoderhashpassword-encoderhash="sha">
<salt-sourceuser-propertysalt-sourceuser-property="username"/>
</password-encoder>
你可以通過password-encoder 的 ref 屬性,指定一個自定義的密碼編碼器 bean。 這應
該 包 含 applicationcontext 中 一 個 bean 的 名 字 , 它 應 該 是 Spring Security 的
PasswordEncoder 接口的一個實例。
2.3. 高級 web 特性
2.3.1. Remember-Me 認證
參考 Remember-Me 章獲得 remember-me 命名空間配置的詳細信息。
2.3.2. 添加 HTTP/HTTPS 信道安全
如果你的同時支持 HTTP 和 HTTPS 協議,然後你要求特定的 URL 只能使用 HTTPS,這時
可以直接使用的requires-channel 屬性:
<http>
<intercept-urlpatternintercept-urlpattern="/secure/**" access="ROLE_USER"requires-channel="https"/>
<intercept-urlpatternintercept-urlpattern="/**" access="ROLE_USER" requires-channel="any"/>
...
</http>
使用了這個配置以後,如果用戶通過 HTTP 嘗試訪問”/secure/**”匹配的網址,他們會先
被重定向到 HTTPS 網址下。 可用的選項有”http”,”https” 或 “any”。 使用”any”意味
着使用 HTTP 或 HTTPS 都可以。
如果你的程序使用的不是 HTTP 或 HTTPS 的標準端口,你可以用下面的方式指定端口對應
關係:
<http>
...
<port-mappings>
<port-mappinghttpport-mappinghttp="9080" https="9443"/>
</port-mappings>
</http>
2.3.3. 會話管理
2.3.3.1. 檢測超時
你可以配置 Spring Security 檢測失效的 session ID, 並把用戶轉發到對應的 URL。這
可 以通 過session-management 元 素配 置:
<http>
...
<session-management invalid-session-url="/sessionTimeout.htm"/>
</http>
2.3.3.2. 同步會話控制
如果你希望限制單個用戶只能登錄到你的程序一次,Spring Security 通過添加下面簡單的
部分支持這個功能。首先,你需要把下面的監聽器添加到你的 web.xml 文件裏,讓 Spring
Security 獲 得 session 生 存 周 期 事 件 :
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
然後,在你的 applicationcontext 加入如下部分:
<http>
...
<session-management>
<concurrency-controlmax-sessionsconcurrency-controlmax-sessions="1" />
</session-management>
</http>
這將防止一個用戶重複登錄好幾次-第二次登錄會讓第一次登錄失效。通常我們更想防止第
二次登錄,這時候我們可以使用
<http>
...
<session-management>
<concurrency-controlmax-sessionsconcurrency-controlmax-sessions="1" error-if-maximum-exceeded="true" />
</session-management>
</http>
第二次登錄將被阻止,通過 “ 注入 ” ,我們的意思是用戶會被轉發到
authentication-failure-url,如果使用了 form-based 登錄。 如果第二次驗證使用了其
他非內置的機制,比如“remember-me”,一個“未認證”(402)錯誤就會發送給客戶端。如
果 你 希 望 使 用 一 個 錯 誤 頁 面 替 代 , 你 可 以 在session-management 中 添 加
session-authentication-error-url 屬性。
如果你爲 form-based 登錄使用了自定義認證, 你就必須特別配置同步會話控制。更多的
細節可以在 會話管理章節找到。
2.3.3.3. 防止 Session 固定攻擊
Session 固定攻擊是一個潛在危險,當一個惡意攻擊者可以創建一個 session 訪問一個網站
的時候,然後說服另一個用戶登錄到同一個會話上(比如,發送給他們一個包含了 session
標識參數的鏈接)。 Spring Security 通過在用戶登錄時,創建一個新 session 來防止這
個問題。 如果你不需要保護,或者它與其他一些需求衝突,你可以通過使用 中的
session-fixation-protection 屬性來配置它的行爲,它有三個選項
migrateSession - 創建一個新 session,把原來 session 中所有屬性複製到新 session
中。這是默認值。
none - 什麼也不做,繼續使用原來的 session。
newSession - 創建一個新的“乾淨的”session,不會複製 session 中的數據。
2.4.1.1. 使用protect-pointcut 添加安全切點
protect-pointcut 是非常強大的,它讓你可以用簡單的聲明對多個 bean 的進行安全聲明。
參考下面的例子:
<global-method-security>
<protect-pointcutexpressionprotect-pointcutexpression="execution(* com.mycompany.*Service.*(..))"
access="ROLE_USER"/>
</global-method-security>
這樣會保護 applicationcontext 中的符合條件的 bean 的所有方法,這些 bean 要在
com.mycompany 包下,類名以”Service”結尾。 ROLE_USER 的角色才能調用這些方
法。 就像 URL 匹配一樣,指定的匹配要放在切點隊列的最前面,第一個匹配的表達式纔會
被用到。
2.5. 默認的AccessDecisionManager
這章假設你有一些 Spring Security 權限控制有關的架構知識。 如果沒有,你可以跳過這
段,以後再來看,因爲這章只是爲了自定義的用戶設置的,需要在簡單基於角色安全的基礎
上加一些客戶化的東西。
當你使用命名空間配置時,默認的 AccessDecisionManager 實例會自動註冊,然後用來
爲 方 法 調 用 和 web URL 訪 問 做 驗 證 , 這 些 都 是 基 於 你 設 置 的 intercept-url 和
protect-pointcut 權限屬性內容(和註解中的內容,如果你使用註解控制方法的權限)。
默認的策略是使用一個 AffirmativeBased AccessDecisionManager ,以及 RoleVoter
和AuthenticatedVoter。 可以在 authorization 中獲得更多信息。
2.5.1. 自定義AccessDecisionManager
如果你需要使用一個更復雜的訪問控制策略,把它設置給方法和 web 安全是很簡單的。
對於方法安全,你可以設置 global-security 裏的access-decision-manager-ref 屬性,
用對應AccessDecisionManager bean 在 applicationcontext 裏的 id:
<global-method-securityaccess-decision-manager-refglobal-method-securityaccess-decision-manager-ref="myAccessDecisionManagerBean">
...
</global-method-security>
web 安全安全的語法也是一樣,但是放在 http 元素裏:
<httpaccess-decision-manager-refhttpaccess-decision-manager-ref="myAccessDecisionManagerBean">
...
</http>
2.6. 驗證管理器和命名空間
主要接口提供了驗證服務在 Spring Security 中, 是 AuthenticationManager。 通常
是 Spring Security 中 ProviderManager 類的一個實例, 如果你以前使用過框架,你可
能已經很熟悉了。 如果沒有,它會在稍後被提及,在 #tech-intro-authentication。 bean
實例被使用authentication-manager 命名空間元素註冊。 你不能好似用一個自定義的
AuthenticationManager 如果你使用 HTTp 或方法安全,在命名空間中,但是它不應該
是一個問題, 因爲你完全控制了使用的 AuthenticationProvider。
你可能註冊額外的AuthenticationProviderbean, 在 ProviderManager 中,你可以使
用做這些事情,使用 ref 屬性, 這個屬性的值,是你希望
添加的 provider 的 bean 的名字,比如:
<authentication-manager>
<authentication-providerrefauthentication-providerref="casAuthenticationProvider"/>
</authentication-manager>
<bean id="casAuthenticationProvider"
class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<security:custom-authentication-provider/>
...
</bean>
另一個常見的需求是,上下文中的另一個 bean 可能需要引用AuthenticationManager。
你可以爲AuthenticationManager 註冊一個別名,然後在 applicationcontext 的其他
地方使用這個名字。
<security:authentication-manageraliassecurity:authentication-manageralias="authenticationManager">
...
</security:authentication-manager>
<bean id="customizedFormLoginFilter"
class="com.somecompany.security.web.CustomFormLoginFilter">
<propertynamepropertyname="authenticationManager"ref="authenticationManager"/>
...
</bean>