安全性命名空間配置

命名空間配置自 Spring Framework 2.0 版本以來就已提供。它讓您可以使用來自其他 XML Schema 的元素來補充傳統的 Spring beans 應用程式上下文語法。您可以在 Spring 參考文件中找到更多資訊。您可以使用命名空間元素來更簡潔地配置個別 bean,或更強大地定義更貼近問題領域的替代配置語法,並對使用者隱藏底層的複雜性。一個簡單的元素可以隱藏多個 bean 和處理步驟正在新增至應用程式上下文的事實。例如,將以下來自 security 命名空間的元素新增至應用程式上下文,會啟動一個嵌入式 LDAP 伺服器,以供應用程式內測試使用

<security:ldap-server />

這比佈線等效的 Apache Directory Server beans 簡單得多。最常見的替代配置需求由 ldap-server 元素上的屬性支援,使用者無需擔心需要建立哪些 bean 以及 bean 屬性名稱是什麼。您可以在關於 LDAP 身份驗證的章節中找到更多關於 ldap-server 元素使用的資訊。編輯應用程式上下文檔案時,一個好的 XML 編輯器應提供關於可用屬性和元素的資訊。我們建議您試用 Spring Tool Suite,因為它具有用於處理標準 Spring 命名空間的特殊功能。

若要開始在您的應用程式上下文中使用 security 命名空間,請將 spring-security-config jar 新增至您的類別路徑。然後,您只需要將 Schema 宣告新增至您的應用程式上下文檔案即可

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/security
		https://www.springframework.org/schema/security/spring-security.xsd">
	...
</beans>

在您可以看到的許多範例中(以及在範例應用程式中),我們經常使用 security(而不是 beans)作為預設命名空間,這表示我們可以省略所有安全性命名空間元素上的前綴,使內容更易於閱讀。如果您將應用程式上下文劃分為不同的檔案,並且將大部分安全性配置放在其中一個檔案中,您可能也想這樣做。您的安全性應用程式上下文檔案將像這樣開始

<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
		https://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/security
		https://www.springframework.org/schema/security/spring-security.xsd">
	...
</beans:beans>

我們假設在本章的後續內容中將使用此語法。

命名空間的設計

命名空間旨在捕捉框架最常見的用途,並為在應用程式中啟用這些用途提供簡化且簡潔的語法。該設計基於框架內的大規模依賴關係,並且可以劃分為以下幾個領域

  • Web/HTTP 安全性是最複雜的部分。它設定用於應用框架身份驗證機制、保護 URL、呈現登入和錯誤頁面等等的篩選器和相關服務 beans。

  • 商業物件(方法)安全性定義用於保護服務層的選項。

  • AuthenticationManager 處理來自框架其他部分的身份驗證請求。

  • AccessDecisionManager 為 Web 和方法安全性提供存取決策。預設的 AccessDecisionManager 已註冊,但您可以選擇使用自訂的 AccessDecisionManager,使用正常的 Spring bean 語法宣告。

  • AuthenticationProvider 實例提供身份驗證管理器驗證使用者的機制。命名空間為幾個標準選項提供支援,並提供新增使用傳統語法宣告的自訂 beans 的方法。

  • UserDetailsService 與身份驗證提供者密切相關,但其他 beans 通常也需要它。

我們將在以下章節中了解如何配置這些。

開始使用安全性命名空間配置

本節介紹如何建構命名空間配置,以使用框架的一些主要功能。我們假設您最初希望盡快啟動並執行,並為現有的 Web 應用程式新增身份驗證支援和存取控制,並提供一些測試登入。然後,我們將了解如何變更為針對資料庫或其他安全性儲存庫進行身份驗證。在後續章節中,我們將介紹更進階的命名空間配置選項。

web.xml 配置

您需要做的第一件事是將以下篩選器宣告新增至您的 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>

DelegatingFilterProxy 是一個 Spring Framework 類別,它委派給一個篩選器實作,該實作定義為應用程式上下文中的 Spring bean。在這種情況下,bean 名稱為 springSecurityFilterChain,它是命名空間建立的內部基礎架構 bean,用於處理 Web 安全性。在這種情況下,bean 名稱為 "springSecurityFilterChain",它是命名空間建立的內部基礎架構 bean,用於處理 Web 安全性。請注意,您不應自行使用此 bean 名稱。將此 bean 新增至您的 web.xml 後,您就可以開始編輯您的應用程式上下文檔案。Web 安全性服務由 <http> 元素配置。

最小的 <http> 配置

若要啟用 Web 安全性,您需要以下配置

<http>
<intercept-url pattern="/**" access="hasRole('USER')" />
<form-login />
<logout />
</http>

該列表表示我們想要

  • 我們應用程式中的所有 URL 都要受到保護,需要 ROLE_USER 角色才能存取它們

  • 使用帶有使用者名稱和密碼的表單登入應用程式

  • 註冊一個登出 URL,允許我們登出應用程式

<http> 元素是所有 Web 相關命名空間功能的父元素。<intercept-url> 元素定義一個 pattern,它使用 Ant 路徑語法與傳入請求的 URL 進行比對。請參閱關於 HttpFirewall 的章節,以了解關於實際如何執行比對的更多詳細資訊。您也可以使用正則表達式比對作為替代方案(請參閱命名空間附錄以了解更多詳細資訊)。access 屬性定義符合給定模式的請求的存取需求。使用預設配置,這通常是以逗號分隔的角色列表,使用者必須擁有其中一個角色才能被允許發出請求。ROLE_ 前綴是一個標記,表示應與使用者的授權進行簡單比較。換句話說,應使用正常的基於角色的檢查。Spring Security 中的存取控制不限於使用簡單的角色(因此使用前綴來區分不同類型的安全性屬性)。我們稍後將看到解釋如何變化。access 屬性中逗號分隔值的解釋取決於 AccessDecisionManager 的哪個實作被使用。自 Spring Security 3.0 起,您也可以使用 EL 表達式來填充該屬性。

您可以使用多個 <intercept-url> 元素來為不同的 URL 集合定義不同的存取需求,但它們會按照列出的順序進行評估,並使用第一個匹配項。因此,您必須將最特定的匹配項放在頂部。您也可以新增一個 method 屬性,以將匹配限制為特定的 HTTP 方法(GETPOSTPUT 等)。

若要新增使用者,您可以直接在命名空間中定義一組測試資料

<authentication-manager>
<authentication-provider>
	<user-service>
	<!-- Password is prefixed with {noop} to indicate to DelegatingPasswordEncoder that
	NoOpPasswordEncoder should be used. This is not safe for production, but makes reading
	in samples easier. Normally passwords should be hashed using BCrypt -->
	<user name="jimi" password="{noop}jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
	<user name="bob" password="{noop}bobspassword" authorities="ROLE_USER" />
	</user-service>
</authentication-provider>
</authentication-manager>

前面的列表顯示了一個安全儲存相同密碼的範例。密碼以 {bcrypt} 為前綴,以指示 DelegatingPasswordEncoder(它支援任何已配置的 PasswordEncoder 進行比對),密碼是使用 BCrypt 雜湊的

<authentication-manager>
<authentication-provider>
	<user-service>
	<user name="jimi" password="{bcrypt}$2a$10$ddEWZUl8aU0GdZPPpy7wbu82dvEw/pBpbRvDQRqA41y6mK1CoH00m"
			authorities="ROLE_USER, ROLE_ADMIN" />
	<user name="bob" password="{bcrypt}$2a$10$/elFpMBnAYYig6KRR5bvOOYeZr1ie1hSogJryg9qDlhza4oCw1Qka"
			authorities="ROLE_USER" />
	<user name="jimi" password="{noop}jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
	<user name="bob" password="{noop}bobspassword" authorities="ROLE_USER" />
	</user-service>
</authentication-provider>
</authentication-manager>

<http> 元素負責建立 FilterChainProxy 及其使用的篩選器 beans。先前常見的問題,例如不正確的篩選器順序,不再是問題,因為篩選器位置是預先定義的。

<authentication-provider> 元素建立一個 DaoAuthenticationProvider bean,而 <user-service> 元素建立一個 InMemoryDaoImpl。所有 authentication-provider 元素都必須是 <authentication-manager> 元素的子元素,後者建立一個 ProviderManager 並向其註冊身份驗證提供者。您可以在 命名空間附錄中找到關於已建立的 beans 的更多詳細資訊。如果您想開始了解框架中的重要類別是什麼以及它們是如何使用的,特別是如果您想稍後自訂內容,則應交叉檢查此附錄。

前面的配置定義了兩個使用者、他們的密碼以及他們在應用程式中的角色(用於存取控制)。您也可以透過設定 user-service 元素上的 properties 屬性,從標準屬性檔案載入使用者資訊。請參閱關於 記憶體中身份驗證的章節,以了解關於檔案格式的更多詳細資訊。使用 <authentication-provider> 元素表示使用者資訊由身份驗證管理器用於處理身份驗證請求。您可以有多個 <authentication-provider> 元素來定義不同的身份驗證來源。每個來源都會依序諮詢。

此時,您應該能夠啟動您的應用程式,並且您應該被要求登入才能繼續。試試看,或試著試驗專案附帶的「教學課程」範例應用程式。

設定預設登入後目的地

如果表單登入不是由嘗試存取受保護資源而提示的,則 default-target-url 選項就會發揮作用。這是使用者成功登入後將前往的 URL。它預設為 /。您也可以配置讓使用者始終最終到達此頁面(無論登入是「按需」還是他們明確選擇登入),方法是將 always-use-default-target 屬性設定為 true。如果您的應用程式始終要求使用者從「首頁」開始,這會很有用,例如

<http pattern="/login.htm*" security="none"/>
<http use-expressions="false">
<intercept-url pattern='/**' access='ROLE_USER' />
<form-login login-page='/login.htm' default-target-url='/home.htm'
		always-use-default-target='true' />
</http>

為了更精確地控制目的地,您可以使用 authentication-success-handler-ref 屬性來替代 default-target-url。引用的 bean 應該是 AuthenticationSuccessHandler 的實例。

進階 Web 功能

本節涵蓋了超越基礎知識的各種功能。

新增您自己的篩選器

如果您之前使用過 Spring Security,您就會知道框架維護一個篩選器鏈,它使用該篩選器鏈來應用其服務。您可能想要在特定位置將您自己的篩選器新增至堆疊,或者使用目前沒有命名空間配置選項的 Spring Security 篩選器(例如 CAS)。或者,您可能想要使用標準命名空間篩選器(例如 UsernamePasswordAuthenticationFilter(由 <form-login> 元素建立))的自訂版本,以利用在您明確使用 bean 時可用的某些額外配置選項。您如何使用命名空間配置來做到這一點,因為篩選器鏈未直接公開?

當您使用命名空間時,篩選器的順序始終會嚴格強制執行。當應用程式上下文正在建立時,篩選器 beans 會由命名空間處理程式碼排序,標準 Spring Security 篩選器在命名空間中都有一個別名和一個眾所周知的位置。

在先前的版本中,排序發生在篩選器實例建立之後,在應用程式上下文的後處理期間。在 3.0+ 版本中,排序現在在 bean Metadata 層級完成,在類別實例化之前。這對於您如何將自己的篩選器新增至堆疊具有影響,因為整個篩選器列表必須在剖析 <http> 元素期間已知,因此語法在 3.0 中略有變更。

下表顯示了篩選器、別名以及建立篩選器的命名空間元素和屬性,它們在篩選器鏈中出現的順序

表 1. 標準篩選器別名和順序
別名 篩選器類別 命名空間元素或屬性

DISABLE_ENCODE_URL_FILTER

DisableEncodeUrlFilter

http@disable-url-rewriting

FORCE_EAGER_SESSION_FILTER

ForceEagerSessionCreationFilter

http@create-session="ALWAYS"

CHANNEL_FILTER

ChannelProcessingFilter

http/intercept-url@requires-channel

SECURITY_CONTEXT_FILTER

SecurityContextPersistenceFilter

http

CONCURRENT_SESSION_FILTER

ConcurrentSessionFilter

session-management/concurrency-control

HEADERS_FILTER

HeaderWriterFilter

http/headers

CSRF_FILTER

CsrfFilter

http/csrf

LOGOUT_FILTER

LogoutFilter

http/logout

X509_FILTER

X509AuthenticationFilter

http/x509

PRE_AUTH_FILTER

AbstractPreAuthenticatedProcessingFilter 子類別

不適用

CAS_FILTER

CasAuthenticationFilter

不適用

FORM_LOGIN_FILTER

UsernamePasswordAuthenticationFilter

http/form-login

BASIC_AUTH_FILTER

BasicAuthenticationFilter

http/http-basic

SERVLET_API_SUPPORT_FILTER

SecurityContextHolderAwareRequestFilter

http/@servlet-api-provision

JAAS_API_SUPPORT_FILTER

JaasApiIntegrationFilter

http/@jaas-api-provision

REMEMBER_ME_FILTER

RememberMeAuthenticationFilter

http/remember-me

ANONYMOUS_FILTER

AnonymousAuthenticationFilter

http/anonymous

SESSION_MANAGEMENT_FILTER

SessionManagementFilter

session-management

EXCEPTION_TRANSLATION_FILTER

ExceptionTranslationFilter

http

FILTER_SECURITY_INTERCEPTOR

FilterSecurityInterceptor

http

SWITCH_USER_FILTER

SwitchUserFilter

不適用

您可以使用 custom-filter 元素和這些名稱之一來指定您的篩選器應出現的位置,從而將您自己的篩選器新增至堆疊

<http>
<custom-filter position="FORM_LOGIN_FILTER" ref="myFilter" />
</http>

<beans:bean id="myFilter" class="com.mycompany.MySpecialAuthenticationFilter"/>

如果您希望您的篩選器插入到堆疊中另一個篩選器的前面或後面,您也可以使用 afterbefore 屬性。您可以使用 FIRSTLAST 以及 position 屬性來指示您希望您的篩選器分別出現在整個堆疊的前面或後面。

避免篩選器位置衝突

如果您插入的自訂篩選器可能佔用與命名空間建立的標準篩選器之一相同的位置,則您不應錯誤地包含命名空間版本。移除任何建立您想要替換其功能的篩選器的元素。

請注意,您無法替換透過使用 <http> 元素本身建立的篩選器:SecurityContextPersistenceFilterExceptionTranslationFilterFilterSecurityInterceptor。預設情況下,會新增一個 AnonymousAuthenticationFilter,除非您停用了 session-fixation 保護,否則 SessionManagementFilter 也會新增至篩選器鏈。

如果您替換需要身份驗證進入點的命名空間篩選器(也就是說,身份驗證程序是由未經身份驗證的使用者嘗試存取受保護資源而觸發的),您也需要新增一個自訂進入點 bean。

方法安全性

自 2.0 版本以來,Spring Security 對於將安全性新增至您的服務層方法提供了實質性的支援。它提供對 JSR-250 註解安全性和框架原始 @Secured 註解的支援。自 3.0 版本以來,您也可以使用 基於表達式的註解。您可以將安全性應用於單個 bean(透過使用 intercept-methods 元素來裝飾 bean 宣告),或者您可以使用 AspectJ 樣式切入點來保護整個服務層中的多個 beans。

預設 AccessDecisionManager

本節假設您對 Spring Security 中存取控制的底層架構有所了解。如果您不了解,您可以跳過它並稍後再返回,因為本節僅適用於需要進行一些自訂以使用超出簡單基於角色的安全性的人員。

當您使用命名空間配置時,會自動為您註冊 AccessDecisionManager 的預設實例,並用於根據您在 intercept-urlprotect-pointcut 宣告(以及註解中,如果您使用註解來保護方法)中指定的存取屬性,對方法調用和 Web URL 存取做出存取決策。

預設策略是使用帶有 RoleVoterAuthenticatedVoterAffirmativeBased AccessDecisionManager。您可以在關於 授權的章節中找到關於這些的更多資訊。

自訂 AccessDecisionManager

如果您需要使用更複雜的存取控制策略,您可以為方法和 Web 安全性設定替代方案。

對於方法安全性,您可以透過將 global-method-security 上的 access-decision-manager-ref 屬性設定為應用程式上下文中適當的 AccessDecisionManager bean 的 id 來執行此操作

<global-method-security access-decision-manager-ref="myAccessDecisionManagerBean">
...
</global-method-security>

Web 安全性的語法相同,但屬性位於 http 元素上

<http access-decision-manager-ref="myAccessDecisionManagerBean">
...
</http>