本章解釋如何將 WS-Security 方面新增到您的 Web 服務中。我們將重點關注 WS-Security 的三個不同領域,即:
認證。 這是確定主體是否是其聲稱的身份的過程。在此上下文中,“主體”通常指使用者、裝置或執行應用程式中操作的其他系統。
數字簽名。 訊息的數字簽名是基於文件和簽名者私鑰的資訊。它透過使用雜湊函式和私有簽名函式(使用簽名者的私鑰進行加密)建立。
加密和解密。 加密是將資料轉換為沒有相應金鑰就無法讀取的形式的過程。它主要用於將資訊隱藏起來,不讓不應看到它的人看到。解密是加密的逆過程;它是將加密資料轉換回可讀形式的過程。
所有這三個領域都透過使用 XwsSecurityInterceptor 或 Wss4jSecurityInterceptor 實現,我們將在第 7.2 節,“ XwsSecurityInterceptor ”和第 7.3 節,“ Wss4jSecurityInterceptor ”中分別進行描述
請注意,WS-Security(尤其是加密和簽名)需要大量的記憶體,並且還會降低效能。如果效能對您很重要,您可能需要考慮不使用 WS-Security,或者只使用基於 HTTP 的安全性。
XwsSecurityInterceptor 是一個 EndpointInterceptor(參見第 5.5.2 節,“攔截請求 - EndpointInterceptor 介面”),它基於 SUN 的 XML 和 Web Services Security 包 (XWSS)。此 WS-Security 實現是 Java Web Services Developer Pack (Java WSDP) 的一部分。
像任何其他端點攔截器一樣,它在端點對映中定義(參見第 5.5 節,“端點對映”)。這意味著您可以有選擇地新增 WS-Security 支援:有些端點對映需要它,而有些則不需要。
請注意,XWSS 需要 SUN 1.5 JDK 和 SUN SAAJ 參考實現。WSS4J 攔截器沒有這些要求(參見第 7.3 節,“ Wss4jSecurityInterceptor ”)。
XwsSecurityInterceptor 需要一個安全策略檔案才能執行。此 XML 檔案告訴攔截器從傳入的 SOAP 訊息中要求哪些安全方面,以及向傳出訊息中新增哪些方面。策略檔案的基本格式將在以下章節中解釋,但您可以在 此處 找到更深入的教程。您可以使用policyConfiguration屬性設定策略,該屬性需要一個 Spring 資源。策略檔案可以包含多個元素,例如要求傳入訊息中包含使用者名稱令牌,並對所有傳出訊息進行簽名。它包含一個 SecurityConfiguration 元素作為根(而不是 JAXRPCSecurity 元素)。
此外,安全攔截器需要一個或多個 CallbackHandler 才能執行。這些處理程式用於檢索證書、私鑰、驗證使用者憑據等。Spring-WS 為大多數常見的安全問題提供處理程式,例如針對 Spring Security 身份驗證管理器進行身份驗證,基於 X509 證書對傳出訊息進行簽名。以下章節將指出針對哪個安全問題使用哪個回撥處理程式。您可以使用 callbackHandler 或 callbackHandlers 屬性設定回撥處理程式。
這是一個示例,展示瞭如何連線 XwsSecurityInterceptor
<beans>
<bean id="wsSecurityInterceptor"
class="org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor">
<property name="policyConfiguration" value="classpath:securityPolicy.xml"/>
<property name="callbackHandlers">
<list>
<ref bean="certificateHandler"/>
<ref bean="authenticationHandler"/>
</list>
</property>
</bean>
...
</beans>
此攔截器使用類路徑上的 securityPolicy.xml 檔案進行配置。它使用檔案中進一步定義的兩個回撥處理程式。
對於大多數加密操作,您將使用標準 java.security.KeyStore 物件。這些操作包括證書驗證、訊息簽名、簽名驗證和加密,但不包括使用者名稱和時間戳驗證。本節旨在為您提供有關金鑰庫的一些背景知識,以及可用於在金鑰庫檔案中儲存金鑰和證書的 Java 工具。此資訊大多與 Spring-WS 無關,而與 Java 的一般加密功能有關。
java.security.KeyStore 類表示用於加密金鑰和證書的儲存設施。它可以包含三種不同型別的元素
私鑰。 這些金鑰用於自身份驗證。私鑰附帶相應公鑰的證書鏈。在 WS-Security 領域中,這相當於訊息簽名和訊息解密。
對稱金鑰。 對稱(或秘密)金鑰也用於訊息加密和解密。區別在於雙方(傳送方和接收方)共享相同的秘密金鑰。
受信任證書。 這些 X509 證書被稱為受信任證書,因為金鑰庫所有者信任證書中的公鑰確實屬於證書所有者。在 WS-Security 中,這些證書用於證書驗證、簽名驗證和加密。
您的 Java 虛擬機器附帶了 keytool 程式,這是一個金鑰和證書管理實用程式。您可以使用此工具建立新的金鑰庫,向其中新增新的私鑰和證書等。提供 keytool 命令的完整參考超出了本文件的範圍,但您可以在 此處 找到參考,或者在命令列上輸入命令 keytool -help。
為了使用 Spring 配置輕鬆載入金鑰庫,您可以使用 KeyStoreFactoryBean。它有一個資源位置屬性,您可以將其設定為指向要載入的金鑰庫的路徑。可以提供密碼來檢查金鑰庫資料的完整性。如果未提供密碼,則不執行完整性檢查。
<bean id="keyStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="password" value="password"/>
<property name="location" value="classpath:org/springframework/ws/soap/security/xwss/test-keystore.jks"/>
</bean>
如果您未指定位置屬性,將建立一個新的空金鑰庫,這很可能不是您想要的。
要在 XwsSecurityInterceptor 中使用金鑰庫,您需要定義一個 KeyStoreCallbackHandler。此回撥具有三個型別為金鑰庫的屬性(keyStore、trustStore 和 symmetricStore)。處理程式使用的確切儲存取決於此處理程式將執行的加密操作。對於私鑰操作,使用 keyStore;對於對稱金鑰操作,使用 symmetricStore;對於確定信任關係,使用 trustStore。下表對此進行了說明
| 加密操作 | 使用的金鑰庫 |
|---|---|
| 證書驗證 | 首先是 keyStore,然後是 trustStore |
| 基於私鑰的解密 |
keyStore
|
| 基於對稱金鑰的解密 |
symmetricStore
|
| 基於公鑰證書的加密 |
trustStore
|
| 基於對稱金鑰的加密 |
symmetricStore
|
| 簽名 |
keyStore
|
| 簽名驗證 |
trustStore
|
此外,KeyStoreCallbackHandler 具有一個 privateKeyPassword 屬性,該屬性應設定為解鎖 keyStore 中包含的私鑰。
如果未設定 symmetricStore,它將預設為 keyStore。如果未設定金鑰或信任儲存,回撥處理程式將使用標準 Java 機制載入或建立它。請參閱 KeyStoreCallbackHandler 的 JavaDoc,以瞭解此機制的工作原理。
例如,如果您想使用 KeyStoreCallbackHandler 來驗證傳入證書或簽名,您將使用信任儲存,如下所示
<beans>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="trustStore" ref="trustStore"/>
</bean>
<bean id="trustStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:truststore.jks"/>
<property name="password" value="changeit"/>
</bean>
</beans>如果您想使用它來解密傳入證書或簽署傳出訊息,您將使用金鑰儲存,如下所示
<beans>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="keyStore" ref="keyStore"/>
<property name="privateKeyPassword" value="changeit"/>
</bean>
<bean id="keyStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:keystore.jks"/>
<property name="password" value="changeit"/>
</bean>
</beans>
以下部分將指出 KeyStoreCallbackHandler 可用於何處,以及針對特定加密操作要設定哪些屬性。
正如引言中所述,身份驗證是確定主體是否是其聲稱的身份的任務。在 WS-Security 中,身份驗證可以採用兩種形式:使用使用者名稱和密碼令牌(使用純文字密碼或密碼摘要),或使用 X509 證書。
最簡單的使用者名稱認證形式使用純文字密碼。在此場景中,SOAP 訊息將包含一個 UsernameToken 元素,其中包含一個 Username 元素和一個包含純文字密碼的 Password 元素。純文字認證可以與 HTTP 伺服器提供的基本認證進行比較。
請注意,純文字密碼的安全性不高。因此,如果您使用它們,應始終為傳輸層新增額外的安全措施(例如,使用 HTTPS 而不是純 HTTP)。
為了要求每條傳入訊息都包含一個帶有純文字密碼的 UsernameToken,安全策略檔案應包含一個 RequireUsernameToken 元素,其中 passwordDigestRequired 屬性設定為 false。您可以在 此處 找到可能的子元素參考。
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
...
<xwss:RequireUsernameToken passwordDigestRequired="false" nonceRequired="false"/>
...
</xwss:SecurityConfiguration>
如果使用者名稱令牌不存在,XwsSecurityInterceptor 將向傳送方返回一個 SOAP 故障。如果存在,它將向註冊的處理程式觸發一個帶有 PlainTextPasswordRequest 的 PasswordValidationCallback。在 Spring-WS 中,有三個類處理此特定回撥。
最簡單的密碼驗證處理程式是 SimplePasswordValidationCallbackHandler。此處理程式根據記憶體中的 Properties 物件驗證密碼,您可以使用 users 屬性指定該物件,如下所示
<bean id="passwordValidationHandler"
class="org.springframework.ws.soap.security.xwss.callback.SimplePasswordValidationCallbackHandler">
<property name="users">
<props>
<prop key="Bert">Ernie</prop>
</props>
</property>
</bean>
在這種情況下,我們只允許使用者“Bert”使用密碼“Ernie”登入。
SpringPlainTextPasswordValidationCallbackHandler 使用 Spring Security 來認證使用者。本文件不涉及 Spring Security 的詳細描述,但可以肯定地說它是一個功能完備的安全框架。您可以在 Spring Security 參考文件 中閱讀更多相關資訊。
SpringPlainTextPasswordValidationCallbackHandler 需要一個 AuthenticationManager 才能執行。它使用此管理器針對其建立的 UsernamePasswordAuthenticationToken 進行身份驗證。如果身份驗證成功,令牌將儲存在 SecurityContextHolder 中。您可以使用 authenticationManager 屬性設定身份驗證管理器
<beans>
<bean id="springSecurityHandler"
class="org.springframework.ws.soap.security.xwss.callback.SpringPlainTextPasswordValidationCallbackHandler">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
<property name="providers">
<bean class="org.springframework.security.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
</property>
</bean>
<bean id="userDetailsService" class="com.mycompany.app.dao.UserDetailService" />
...
</beans>
JaasPlainTextPasswordValidationCallbackHandler 基於標準 Java 身份驗證和授權服務 。本文件超出了提供 JAAS 完整介紹的範圍,但有一個 不錯的教程 可用。
JaasPlainTextPasswordValidationCallbackHandler 僅需要 loginContextName 即可執行。它使用此名稱建立一個新的 JAAS LoginContext,並使用 SOAP 訊息中提供的使用者名稱和密碼處理標準 JAAS NameCallback 和 PasswordCallback。這意味著此回撥處理程式與任何在 login() 階段觸發這些回撥的 JAAS LoginModule 整合,這是標準行為。
您可以按如下方式連線 JaasPlainTextPasswordValidationCallbackHandler
<bean id="jaasValidationHandler"
class="org.springframework.ws.soap.security.xwss.callback.jaas.JaasPlainTextPasswordValidationCallbackHandler">
<property name="loginContextName" value="MyLoginModule" />
</bean>
在這種情況下,回撥處理程式使用名為“MyLoginModule”的 LoginContext。此模組應在您的 jaas.config 檔案中定義,如上述教程中所述。
使用密碼摘要時,SOAP 訊息也包含一個 UsernameToken 元素,其中包含一個 Username 元素和一個 Password 元素。不同之處在於密碼不是以純文字傳送,而是以摘要形式傳送。接收方將此摘要與它從使用者已知密碼計算出的摘要進行比較,如果它們相同,則使用者透過身份驗證。它可以與 HTTP 伺服器提供的摘要身份驗證進行比較。
為了要求每條傳入訊息都包含一個帶有密碼摘要的 UsernameToken 元素,安全策略檔案應包含一個 RequireUsernameToken 元素,其中 passwordDigestRequired 屬性設定為 true。此外,nonceRequired 應設定為 true:您可以在 此處 找到可能的子元素參考。
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
...
<xwss:RequireUsernameToken passwordDigestRequired="true" nonceRequired="true"/>
...
</xwss:SecurityConfiguration>
如果使用者名稱令牌不存在,XwsSecurityInterceptor 將向傳送方返回一個 SOAP 故障。如果存在,它將向註冊的處理程式觸發一個帶有 DigestPasswordRequest 的 PasswordValidationCallback。在 Spring-WS 中,有兩個類處理此特定回撥。
SimplePasswordValidationCallbackHandler 可以處理純文字密碼和密碼摘要。它在第 7.2.2.1.1 節,“SimplePasswordValidationCallbackHandler”中進行了描述。
SpringDigestPasswordValidationCallbackHandler 需要一個 Spring Security UserDetailService 才能執行。它使用此服務檢索令牌中指定使用者的密碼。然後將此詳細資訊物件中包含的密碼摘要與訊息中的摘要進行比較。如果它們相等,則使用者已成功透過身份驗證,並且 UsernamePasswordAuthenticationToken 將儲存在 SecurityContextHolder 中。您可以使用 userDetailsService 設定服務。此外,您可以設定 userCache 屬性,以快取載入的使用者詳細資訊。
<beans>
<bean class="org.springframework.ws.soap.security.xwss.callback.SpringDigestPasswordValidationCallbackHandler">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
<bean id="userDetailsService" class="com.mycompany.app.dao.UserDetailService" />
...
</beans>
一種更安全的身份驗證方式是使用 X509 證書。在此場景中,SOAP 訊息包含一個 BinarySecurityToken,其中包含 X509 證書的 Base 64 編碼版本。接收方使用該證書進行身份驗證。訊息中儲存的證書也用於簽名訊息(參見第 7.2.3.1 節,“驗證簽名”)。
為確保所有傳入的 SOAP 訊息都帶有 BinarySecurityToken,安全策略檔案應包含一個 RequireSignature 元素。此元素還可以攜帶其他元素,這些元素將在第 7.2.3.1 節,“驗證簽名”中介紹。您可以在 此處 找到可能的子元素參考。
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
...
<xwss:RequireSignature requireTimestamp="false">
...
</xwss:SecurityConfiguration>
當收到不帶證書的訊息時,XwsSecurityInterceptor 將向傳送方返回一個 SOAP 故障。如果存在,它將觸發一個 CertificateValidationCallback。Spring-WS 中有三個處理程式用於身份驗證目的處理此回撥。
在大多數情況下,證書身份驗證之前應進行證書驗證,因為您只希望對有效證書進行身份驗證。無效證書(例如已過期的證書或不在您的受信任證書儲存中的證書)應被忽略。
用 Spring-WS 的術語來說,這意味著 SpringCertificateValidationCallbackHandler 或 JaasCertificateValidationCallbackHandler 之前應是 KeyStoreCallbackHandler。這可以透過在 XwsSecurityInterceptor 的配置中設定 callbackHandlers 屬性的順序來實現。
<bean id="wsSecurityInterceptor"
class="org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor">
<property name="policyConfiguration" value="classpath:securityPolicy.xml"/>
<property name="callbackHandlers">
<list>
<ref bean="keyStoreHandler"/>
<ref bean="springSecurityHandler"/>
</list>
</property>
</bean>
透過這種設定,攔截器將首先使用金鑰庫確定訊息中的證書是否有效,然後對其進行身份驗證。
KeyStoreCallbackHandler 使用標準 Java 金鑰庫來驗證證書。此證書驗證過程包括以下步驟
首先,處理程式將檢查證書是否在私有 keyStore 中。如果存在,則有效。
如果證書不在私鑰庫中,處理程式將檢查當前日期和時間是否在證書中給出的有效期內。如果不在,則證書無效;如果在,它將繼續執行最後一步。
最後,為證書建立認證路徑。這基本上意味著處理程式將確定證書是否由 trustStore 中的任何證書頒發機構頒發。如果成功構建了認證路徑,則證書有效。否則,證書無效。
要將 KeyStoreCallbackHandler 用於證書驗證目的,您最有可能只設置 trustStore 屬性
<beans>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="trustStore" ref="trustStore"/>
</bean>
<bean id="trustStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:truststore.jks"/>
<property name="password" value="changeit"/>
</bean>
</beans>使用此設定,要驗證的證書必須在信任儲存本身中,或者信任儲存必須包含頒發該證書的證書頒發機構。
SpringCertificateValidationCallbackHandler 需要一個 Spring Security AuthenticationManager 才能執行。它使用此管理器來對它建立的 X509AuthenticationToken 進行身份驗證。配置的身份驗證管理器應提供一個可以處理此令牌的提供程式(通常是 X509AuthenticationProvider 的例項)。如果身份驗證成功,令牌將儲存在 SecurityContextHolder 中。您可以使用 authenticationManager 屬性設定身份驗證管理器
<beans>
<bean id="springSecurityCertificateHandler"
class="org.springframework.ws.soap.security.xwss.callback.SpringCertificateValidationCallbackHandler">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<bean id="authenticationManager"
class="org.springframework.security.providers.ProviderManager">
<property name="providers">
<bean class="org.springframework.ws.soap.security.x509.X509AuthenticationProvider">
<property name="x509AuthoritiesPopulator">
<bean class="org.springframework.ws.soap.security.x509.populator.DaoX509AuthoritiesPopulator">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
</property>
</bean>
</property>
</bean>
<bean id="userDetailsService" class="com.mycompany.app.dao.UserDetailService" />
...
</beans>
在這種情況下,我們使用自定義使用者詳細資訊服務根據證書獲取身份驗證詳細資訊。有關基於 X509 證書進行身份驗證的更多資訊,請參閱 Spring Security 參考文件 。
JaasCertificateValidationCallbackHandler 需要 loginContextName 才能執行。它使用此名稱和證書的 X500Principal 建立一個新的 JAAS LoginContext。這意味著此回撥處理程式與任何處理 X500 主體的 JAAS LoginModule 整合。
您可以按如下方式連線 JaasCertificateValidationCallbackHandler
<bean id="jaasValidationHandler"
class="org.springframework.ws.soap.security.xwss.callback.jaas.JaasCertificateValidationCallbackHandler">
<property name="loginContextName">MyLoginModule</property>
</bean>
在這種情況下,回撥處理程式使用名為“MyLoginModule”的 LoginContext。此模組應在您的 jaas.config 檔案中定義,並且應能夠針對 X500 主體進行身份驗證。
訊息的數字簽名是基於文件和簽名者私鑰的資訊。WS-Security 中與簽名相關的任務主要有兩個:驗證簽名和簽名訊息。
就像基於證書的認證一樣,簽名訊息包含一個 BinarySecurityToken,其中包含用於簽名訊息的證書。此外,它還包含一個 SignedInfo 塊,指示訊息的哪個部分已簽名。
為確保所有傳入的 SOAP 訊息都帶有 BinarySecurityToken,安全策略檔案應包含一個 RequireSignature 元素。它還可以包含一個 SignatureTarget 元素,用於指定預期簽名的目標訊息部分,以及各種其他子元素。您還可以定義要使用的私鑰別名、是使用對稱金鑰還是私鑰,以及許多其他屬性。您可以在 此處 找到可能的子元素參考。
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
<xwss:RequireSignature requireTimestamp="false"/>
</xwss:SecurityConfiguration>
如果簽名不存在,XwsSecurityInterceptor 將向傳送方返回一個 SOAP 故障。如果存在,它將向註冊的處理程式觸發一個 SignatureVerificationKeyCallback。在 Spring-WS 中,有一個類處理此特定回撥:KeyStoreCallbackHandler。
如第 7.2.1.3 節,“KeyStoreCallbackHandler”中所述,KeyStoreCallbackHandler 使用 java.security.KeyStore 處理各種加密回撥,包括簽名驗證。對於簽名驗證,處理程式使用 trustStore 屬性
<beans>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="trustStore" ref="trustStore"/>
</bean>
<bean id="trustStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:org/springframework/ws/soap/security/xwss/test-truststore.jks"/>
<property name="password" value="changeit"/>
</bean>
</beans>
簽名訊息時,XwsSecurityInterceptor 將 BinarySecurityToken 和一個 SignedInfo 塊新增到訊息中,該塊指示訊息的哪個部分已簽名。
為了簽名所有傳出的 SOAP 訊息,安全策略檔案應包含一個 Sign 元素。它還可以包含一個 SignatureTarget 元素,用於指定預期簽名的目標訊息部分,以及各種其他子元素。您還可以定義要使用的私鑰別名、是使用對稱金鑰還是私鑰,以及許多其他屬性。您可以在 此處 找到可能的子元素參考。
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config"> <xwss:Sign includeTimestamp="false" /> </xwss:SecurityConfiguration>
XwsSecurityInterceptor 將向註冊的處理程式觸發一個 SignatureKeyCallback。在 Spring-WS 中,有一個類處理此特定回撥:KeyStoreCallbackHandler。
如第 7.2.1.3 節,“KeyStoreCallbackHandler”中所述,KeyStoreCallbackHandler 使用 java.security.KeyStore 處理各種加密回撥,包括簽名訊息。對於添加簽名,處理程式使用 keyStore 屬性。此外,您必須設定 privateKeyPassword 屬性以解鎖用於簽名的私鑰。
<beans>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="keyStore" ref="keyStore"/>
<property name="privateKeyPassword" value="changeit"/>
</bean>
<bean id="keyStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:keystore.jks"/>
<property name="password" value="changeit"/>
</bean>
</beans>
當加密時,訊息被轉換為只能使用適當金鑰讀取的形式。訊息可以解密以揭示原始的、可讀的訊息。
要解密傳入的 SOAP 訊息,安全策略檔案應包含一個 RequireEncryption 元素。此元素還可以包含一個 EncryptionTarget 元素,指示訊息的哪一部分應加密,以及一個 SymmetricKey 指示應使用共享金鑰而不是常規私鑰來解密訊息。您可以在 此處 閱讀其他元素的描述。
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
<xwss:RequireEncryption />
</xwss:SecurityConfiguration>
如果傳入訊息未加密,XwsSecurityInterceptor 將向傳送方返回一個 SOAP 故障。如果已加密,它將向註冊的處理程式觸發一個 DecryptionKeyCallback。在 Spring-WS 中,有一個類處理此特定回撥:KeyStoreCallbackHandler。
如第 7.2.1.3 節,“KeyStoreCallbackHandler”中所述,KeyStoreCallbackHandler 使用 java.security.KeyStore 處理各種加密回撥,包括解密。對於解密,處理程式使用 keyStore 屬性。此外,您必須設定 privateKeyPassword 屬性以解鎖用於解密的私鑰。對於基於對稱金鑰的解密,它將使用 symmetricStore。
<beans>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="keyStore" ref="keyStore"/>
<property name="privateKeyPassword" value="changeit"/>
</bean>
<bean id="keyStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:keystore.jks"/>
<property name="password" value="changeit"/>
</bean>
</beans>
要加密傳出的 SOAP 訊息,安全策略檔案應包含一個 Encrypt 元素。此元素還可以攜帶一個 EncryptionTarget 元素,指示訊息的哪個部分應加密,以及一個 SymmetricKey 指示應使用共享金鑰而不是常規公鑰來加密訊息。您可以在 此處 閱讀其他元素的描述。
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
<xwss:Encrypt />
</xwss:SecurityConfiguration>
XwsSecurityInterceptor 將向註冊的處理程式觸發 EncryptionKeyCallback 以檢索加密資訊。在 Spring-WS 中,有一個類處理此特定回撥:KeyStoreCallbackHandler。
如第 7.2.1.3 節,“KeyStoreCallbackHandler”中所述,KeyStoreCallbackHandler 使用 java.security.KeyStore 處理各種加密回撥,包括加密。對於基於公鑰的加密,處理程式使用 trustStore 屬性。對於基於對稱金鑰的加密,它將使用 symmetricStore。
<beans>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="trustStore" ref="trustStore"/>
</bean>
<bean id="trustStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:truststore.jks"/>
<property name="password" value="changeit"/>
</bean>
</beans>
當安全或驗證操作失敗時,XwsSecurityInterceptor 將分別丟擲 WsSecuritySecurementException 或 WsSecurityValidationException。這些異常繞過標準異常處理機制,但在攔截器本身中處理。
WsSecuritySecurementException 異常在 XwsSecurityInterceptor 的 handleSecurementException 方法中處理。預設情況下,此方法將簡單地記錄一個錯誤,並停止進一步處理訊息。
類似地,WsSecurityValidationException 異常在 XwsSecurityInterceptor 的 handleValidationException 方法中處理。預設情況下,此方法將建立 SOAP 1.1 客戶端或 SOAP 1.2 傳送方故障,並將其作為響應傳送回去。
handleSecurementException 和 handleValidationException 都是受保護的方法,您可以重寫它們以更改其預設行為。
Wss4jSecurityInterceptor 是一個 EndpointInterceptor(參見第 5.5.2 節,“攔截請求 - EndpointInterceptor 介面”),它基於 Apache 的 WSS4J。
WSS4J 實現了以下標準
OASIS Web 服務安全:SOAP 訊息安全 1.0 標準 200401,2004 年 3 月
使用者名稱令牌配置檔案 V1.0
X.509 令牌配置檔案 V1.0
此攔截器支援由 AxiomSoapMessageFactory 和 SaajSoapMessageFactory 建立的訊息。
WSS4J 不使用外部配置檔案;攔截器完全由屬性配置。此攔截器執行的驗證和安全操作分別透過 validationActions 和 securementActions 屬性指定。操作作為空格分隔的字串傳遞。這是一個配置示例
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="UsernameToken Encrypt"/>
...
<property name="securementActions" value="Encrypt"/>
...
</bean>
驗證操作包括
| 驗證操作 | 描述 |
|---|---|
使用者名稱令牌
| 驗證使用者名稱令牌 |
時間戳
| 驗證時間戳 |
加密
| 解密訊息 |
簽名
| 驗證簽名 |
無安全
| 未執行任何操作 |
安全措施是
| 安全操作 | 描述 |
|---|---|
使用者名稱令牌
| 新增使用者名稱令牌 |
使用者名稱令牌簽名
| 新增使用者名稱令牌和簽名使用者名稱令牌金鑰 |
時間戳
| 新增時間戳 |
加密
| 加密響應 |
簽名
| 簽署響應 |
無安全
| 未執行任何操作 |
操作的順序很重要,並由攔截器強制執行。如果傳入 SOAP 訊息的安全操作的執行順序與 validationActions 指定的順序不同,攔截器將拒絕該訊息。
對於需要與金鑰庫或證書處理(簽名、加密和解密操作)互動的加密操作,WSS4J 需要一個 org.apache.ws.security.components.crypto.Crypto 例項。
Crypto 例項可以從 WSS4J 的 CryptoFactory 獲取,或者更方便地使用 Spring-WS 的 CryptoFactoryBean 獲取。
Spring-WS 提供了一個方便的工廠 bean,CryptoFactoryBean,它透過強型別屬性(首選)或透過 Properties 物件構造和配置 Crypto 例項。
預設情況下,CryptoFactoryBean 返回 org.apache.ws.security.components.crypto.Merlin 的例項。這可以透過設定 cryptoProvider 屬性(或其等效的 org.apache.ws.security.crypto.provider 字串屬性)來更改。
這是一個簡單的配置示例
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="mypassword"/>
<property name="keyStoreLocation" value="file:/path_to_keystore/keystore.jks"/>
</bean>
Spring-WS 提供了一組回撥處理程式以與 Spring Security 整合。此外,還提供了一個簡單的回撥處理程式 SimplePasswordValidationCallbackHandler,用於使用記憶體中的 Properties 物件配置使用者和密碼。
回撥處理程式透過 Wss4jSecurityInterceptor 的 validationCallbackHandler 屬性進行配置。
SimplePasswordValidationCallbackHandler 根據記憶體中的 Properties 物件驗證純文字和摘要使用者名稱令牌。其配置如下
<bean id="callbackHandler"
class="org.springframework.ws.soap.security.wss4j.callback.SimplePasswordValidationCallbackHandler">
<property name="users">
<props>
<prop key="Bert">Ernie</prop>
</props>
</property>
</bean>
SpringSecurityPasswordValidationCallbackHandler 使用 Spring Security UserDetailService 來驗證純文字和摘要密碼。它使用此服務檢索令牌中指定使用者的密碼(的摘要)。然後將此詳細資訊物件中包含的密碼(的摘要)與訊息中的摘要進行比較。如果它們相等,則使用者已成功透過身份驗證,並且 UsernamePasswordAuthenticationToken 將儲存在 SecurityContextHolder 中。您可以使用 userDetailsService 設定服務。此外,您可以設定 userCache 屬性,以快取載入的使用者詳細資訊。
<beans>
<bean class="org.springframework.ws.soap.security.wss4j.callback.SpringDigestPasswordValidationCallbackHandler">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
<bean id="userDetailsService" class="com.mycompany.app.dao.UserDetailService" />
...
</beans>
將使用者名稱令牌新增到傳出訊息中非常簡單,只需將 UsernameToken 新增到 Wss4jSecurityInterceptor 的 securementActions 屬性中,並指定 securementUsername 和 securementPassword 即可。
密碼型別可以透過 securementPasswordType 屬性設定。可能的值為用於純文字密碼的 PasswordText 或用於摘要密碼的 PasswordDigest,後者是預設值。
以下示例生成帶有摘要密碼的使用者名稱令牌
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="UsernameToken"/>
<property name="securementUsername" value="Ernie"/>
<property name="securementPassword" value="Bert"/>
</bean>
如果選擇純文字密碼型別,可以指示攔截器使用 securementUsernameTokenElements 屬性新增 Nonce 和/或 Created 元素。該值必須是包含所需元素名稱的列表,以空格分隔(區分大小寫)。
下一個示例生成一個帶有純文字密碼、Nonce 和 Created 元素的使用者名稱令牌
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="UsernameToken"/>
<property name="securementUsername" value="Ernie"/>
<property name="securementPassword" value="Bert"/>
<property name="securementPasswordType" value="PasswordText"/>
<property name="securementUsernameTokenElements" value="Nonce Created"/>
</bean>
由於證書認證類似於數字簽名,WSS4J 將其作為簽名驗證和安全措施的一部分來處理。具體來說,必須將 securementSignatureKeyIdentifier 屬性設定為 DirectReference,以便指示 WSS4J 生成一個包含 X509 證書的 BinarySecurityToken 元素,並將其包含在傳出訊息中。證書的名稱和密碼分別透過 securementUsername 和 securementPassword 屬性傳遞。請參見以下示例
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="Signature"/>
<property name="securementSignatureKeyIdentifier" value="DirectReference"/>
<property name="securementUsername" value="mycert"/>
<property name="securementPassword" value="certpass"/>
<property name="securementSignatureCrypto">
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="123456"/>
<property name="keyStoreLocation" value="classpath:/keystore.jks"/>
</bean>
</property>
</bean>
對於證書驗證,適用常規簽名驗證
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="Signature"/>
<property name="validationSignatureCrypto">
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="123456"/>
<property name="keyStoreLocation" value="classpath:/keystore.jks"/>
</bean>
</property>
</bean>
在驗證結束時,攔截器將透過委託給預設的 WSS4J 實現自動驗證證書的有效性。如果需要,可以透過重新定義 verifyCertificateTrust 方法來更改此行為。
有關更多詳細資訊,請參閱第 7.3.5 節,“數字簽名”。
本節描述了 Wss4jSecurityInterceptor 中可用的各種時間戳選項。
要驗證時間戳,請將 Timestamp 新增到 validationActions 屬性中。可以透過將 timestampStrict 設定為 true 並透過 timeToLive 屬性指定伺服器端生存時間(預設為 300)來覆蓋 SOAP 訊息發起者指定的時間戳語義 [3] 。
在以下示例中,攔截器將時間戳有效期視窗限制為 10 秒,拒絕在該視窗之外的任何有效時間戳令牌
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="Timestamp"/>
<property name="timestampStrict" value="true"/>
<property name="timeToLive" value="10"/>
</bean>
將 Timestamp 新增到 securementActions 屬性會在傳出訊息中生成時間戳頭。 timestampPrecisionInMilliseconds 屬性指定生成的時間戳精度是否為毫秒。預設值為 true。
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="Timestamp"/>
<property name="timestampPrecisionInMilliseconds" value="true"/>
</bean>
本節描述了 Wss4jSecurityInterceptor 中可用的各種簽名選項。
要指示 Wss4jSecurityInterceptor,validationActions 必須包含 Signature 操作。此外,validationSignatureCrypto 屬性必須指向包含發起者公共證書的金鑰庫
<bean id="wsSecurityInterceptor" class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="Signature"/>
<property name="validationSignatureCrypto">
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="123456"/>
<property name="keyStoreLocation" value="classpath:/keystore.jks"/>
</bean>
</property>
</bean>
透過將 Signature 操作新增到 securementActions 中,可以啟用對傳出訊息的簽名。要使用的私鑰的別名和密碼分別由 securementUsername 和 securementPassword 屬性指定。securementSignatureCrypto 必須指向包含私鑰的金鑰庫
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="Signature"/>
<property name="securementUsername" value="mykey"/>
<property name="securementPassword" value="123456"/>
<property name="securementSignatureCrypto">
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="123456"/>
<property name="keyStoreLocation" value="classpath:/keystore.jks"/>
</bean>
</property>
</bean>
此外,可以透過 securementSignatureAlgorithm 定義簽名演算法。
要使用的金鑰識別符號型別可以透過 securementSignatureKeyIdentifier 屬性進行自定義。對於簽名,只有 IssuerSerial 和 DirectReference 有效。
securementSignatureParts 屬性控制訊息的哪些部分應被簽名。此屬性的值是一個以分號分隔的元素名稱列表,用於標識要簽名的元素。簽名部分的一般形式是 {}{namespace}Element [4] 。預設行為是簽名 SOAP 正文。
例如,以下是如何簽署 Spring Web Services 回顯示例中的 echoResponse 元素
<property name="securementSignatureParts"
value="{}{http://www.springframework.org/spring-ws/samples/echo}echoResponse"/>
WS Security 規範定義了幾種格式來傳輸簽名令牌(證書)或對這些令牌的引用。因此,純元素名稱 Token 對令牌進行簽名,並處理不同的格式。要對 SOAP 正文和簽名令牌進行簽名,securementSignatureParts 的值必須包含
<property name="securementSignatureParts">
<value>
{}{http://schemas.xmlsoap.org/soap/envelope/}Body;
Token
</value>
</property>
要指定沒有名稱空間的元素,請使用字串 Null 作為名稱空間名稱(區分大小寫)。
如果在請求中沒有其他區域性名稱為 Body 的元素,則 SOAP 名稱空間識別符號可以為空({})。
透過將 enableSignatureConfirmation 設定為 true 啟用簽名確認。請注意,簽名確認操作涵蓋請求和響應。這意味著 secureResponse 和 validateRequest 必須設定為 true(這是預設值),即使沒有相應的安全操作。
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="Signature"/>
<property name="enableSignatureConfirmation" value="true"/>
<property name="validationSignatureCrypto">
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="123456"/>
<property name="keyStoreLocation" value="file:/keystore.jks"/>
</bean>
</property>
</bean>
本節描述了 Wss4jSecurityInterceptor 中可用的各種加密和解密選項。
解密傳入的 SOAP 訊息需要將 Encrypt 操作新增到 validationActions 屬性中。其餘配置取決於訊息中出現的金鑰資訊 [5] 。
要解密包含嵌入式加密對稱金鑰(xenc:EncryptedKey 元素)的訊息,validationDecryptionCrypto 需要指向包含解密私鑰的金鑰庫。此外,validationCallbackHandler 必須注入一個 org.springframework.ws.soap.security.wss4j.callback.KeyStoreCallbackHandler,並指定金鑰密碼
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="Encrypt"/>
<property name="validationDecryptionCrypto">
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="123456"/>
<property name="keyStoreLocation" value="classpath:/keystore.jks"/>
</bean>
</property>
<property name="validationCallbackHandler">
<bean class="org.springframework.ws.soap.security.wss4j.callback.KeyStoreCallbackHandler">
<property name="privateKeyPassword" value="mykeypass"/>
</bean>
</property>
</bean>
為了支援解密具有嵌入式金鑰名稱(ds:KeyName 元素)的訊息,請配置一個指向包含對稱金鑰的金鑰庫的 KeyStoreCallbackHandler。屬性 symmetricKeyPassword 指示金鑰密碼,金鑰名稱是 ds:KeyName 元素指定的名稱
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="Encrypt"/>
<property name="validationCallbackHandler">
<bean class="org.springframework.ws.soap.security.wss4j.callback.KeyStoreCallbackHandler">
<property name="keyStore">
<bean class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:keystore.jks"/>
<property name="type" value="JCEKS"/>
<property name="password" value="123456"/>
</bean>
</property>
<property name="symmetricKeyPassword" value="mykeypass"/>
</bean>
</property>
</bean>
將 Encrypt 新增到 securementActions 可啟用對傳出訊息的加密。用於加密的證書別名透過 securementEncryptionUser 屬性設定。包含證書的金鑰庫透過 securementEncryptionCrypto 屬性訪問。由於加密依賴於公共證書,因此無需傳遞密碼。
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="Encrypt"/>
<property name="securementEncryptionUser" value="mycert"/>
<property name="securementEncryptionCrypto">
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="123456"/>
<property name="keyStoreLocation" value="file:/keystore.jks"/>
</bean>
</property>
</bean>
加密可以透過多種方式自定義:要使用的金鑰識別符號型別由 securementEncryptionKeyIdentifier 定義。可能的值為 IssuerSerial、X509KeyIdentifier、DirectReference、Thumbprint、SKIKeyIdentifier 或 EmbeddedKeyName。
如果選擇了 EmbeddedKeyName 型別,您需要指定用於加密的金鑰。金鑰的別名透過 securementEncryptionUser 屬性設定,與其他金鑰識別符號型別相同。但是,WSS4J 需要一個回撥處理程式來獲取金鑰。因此,必須為 securementCallbackHandler 提供一個指向相應金鑰庫的 KeyStoreCallbackHandler。預設情況下,結果 WS-Security 頭部中的 ds:KeyName 元素採用 securementEncryptionUser 屬性的值。要指示不同的名稱,請使用所需值設定 securementEncryptionEmbeddedKeyName。在下一個示例中,傳出訊息將使用別名為 secretKey 的金鑰進行加密,而 myKey 將出現在 ds:KeyName 元素中
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="Encrypt"/>
<property name="securementEncryptionKeyIdentifier" value="EmbeddedKeyName"/>
<property name="securementEncryptionUser" value="secretKey"/>
<property name="securementEncryptionEmbeddedKeyName" value="myKey"/>
<property name="securementCallbackHandler">
<bean class="org.springframework.ws.soap.security.wss4j.callback.KeyStoreCallbackHandler">
<property name="symmetricKeyPassword" value="keypass"/>
<property name="keyStore">
<bean class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="file:/keystore.jks"/>
<property name="type" value="jceks"/>
<property name="password" value="123456"/>
</bean>
</property>
</bean>
</property>
</bean>
securementEncryptionKeyTransportAlgorithm 屬性定義用於加密生成的對稱金鑰的演算法。支援的值為 http://www.w3.org/2001/04/xmlenc#rsa-1_5(預設值)和 http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p。
要使用的對稱加密演算法可以透過 securementEncryptionSymAlgorithm 屬性設定。支援的值為 http://www.w3.org/2001/04/xmlenc#aes128-cbc(預設值)、http://www.w3.org/2001/04/xmlenc#tripledes-cbc、http://www.w3.org/2001/04/xmlenc#aes256-cbc、http://www.w3.org/2001/04/xmlenc#aes192-cbc。
最後,securementEncryptionParts 屬性定義訊息的哪些部分將被加密。此屬性的值是一個以分號分隔的元素名稱列表,用於標識要加密的元素。每個元素名稱前面可以加上加密模式指定符和名稱空間識別符號,每個都包含在一對花括號內。加密模式指定符可以是 {Content} 或 {Element} [6] 。以下示例標識了來自 echo 示例的 echoResponse
<property name="securementEncryptionParts"
value="{Content}{http://www.springframework.org/spring-ws/samples/echo}echoResponse"/>
請注意,元素名稱、名稱空間識別符號和加密修飾符區分大小寫。可以省略加密修飾符和名稱空間識別符號。在這種情況下,加密模式預設為 Content,名稱空間設定為 SOAP 名稱空間。
要指定沒有名稱空間的元素,請使用值 Null 作為名稱空間名稱(區分大小寫)。如果未指定列表,則處理程式預設以 Content 模式加密 SOAP 正文。
Wss4jSecurityInterceptor 的異常處理與 XwsSecurityInterceptor 的異常處理相同。有關更多資訊,請參閱第 7.2.5 節,“安全異常處理”。