本章解釋瞭如何為你的 Web 服務新增 WS-Security 方面。我們將重點關注 WS-Security 的三個不同領域,即
認證。 這個過程用於確定主體是否與其聲稱的身份一致。在此上下文中,“主體”通常指使用者、裝置或應用程式中可以執行某種操作的其他系統。
數字簽名。 訊息的數字簽名是基於文件和簽名者私鑰的一段資訊。它透過使用雜湊函式和私有簽名函式(使用簽名者的私鑰加密)建立。
加密和解密。 加密是將資料轉換為一種形式,使其在沒有適當金鑰的情況下無法讀取。它主要用於向未經授權的人隱藏資訊。解密是加密的逆過程;它是將加密資料轉換回可讀形式的過程。
所有這三個領域都透過 XwsSecurityInterceptor
或 Wss4jSecurityInterceptor
實現,我們將在第 7.2 節,“ XwsSecurityInterceptor
”和第 7.3 節,“ Wss4jSecurityInterceptor
”中分別描述它們。
注意 WS-Security(尤其是加密和簽名)需要大量記憶體,並且會降低效能。如果效能對你很重要,你可能需要考慮不使用 WS-Security,或者只使用基於 HTTP 的安全機制。
XwsSecurityInterceptor
是一個 EndpointInterceptor
(參見第 5.5.2 節,“攔截請求 - EndpointInterceptor
介面”),它基於 SUN 的 XML 和 Web 服務安全包 (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
。如果未設定 keyStore 或 trustStore,回撥處理器將使用標準的 Java 機制載入或建立它們。請參閱 KeyStoreCallbackHandler
的 JavaDoc 以瞭解此機制的工作原理。
例如,如果你想使用 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>
如果你想使用它來解密傳入的證書或對傳出訊息進行簽名,你可以使用 keyStore,如下所示
<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 Fault。如果存在,它將向註冊的處理器觸發一個帶有 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 Authentication and Authorization Service 。提供 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 Fault。如果存在,它將向註冊的處理器觸發一個帶有 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 Fault。如果存在,它將觸發一個 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>
使用此設定,攔截器將首先使用金鑰庫確定訊息中的證書是否有效,然後對其進行認證。
如第 7.2.1.3 節,“KeyStoreCallbackHandler”所述,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>
使用此設定,要驗證的證書必須位於 trustStore 本身中,或者 trustStore 必須包含頒發該證書的證書頒發機構。
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 principal 的 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 principal 進行認證。
訊息的數字簽名是基於文件和簽名者私鑰的一段資訊。在 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 Fault。如果存在,它將向註冊的處理器觸發一個 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 Fault。如果已加密,它將向註冊的處理器觸發一個 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 Client 或 SOAP 1.2 Sender Fault,並將其作為響應傳送回去。
handleSecurementException
和 handleValidationException
都是受保護的方法,你可以覆蓋它們以更改其預設行為。
Wss4jSecurityInterceptor
是一個 EndpointInterceptor
(參見第 5.5.2 節,“攔截請求 - EndpointInterceptor
介面”),它基於Apache 的 WSS4J。
WSS4J 實現了以下標準
OASIS Web Services Security: SOAP Message Security 1.0 Standard 200401, March 2004
Username Token profile V1.0
X.509 Token Profile 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>
驗證操作包括
驗證操作 | 描述 |
---|---|
UsernameToken
| 驗證使用者名稱令牌 |
Timestamp
| 驗證時間戳 |
Encrypt
| 解密訊息 |
Signature
| 驗證簽名 |
NoSecurity
| 不執行任何操作 |
安全操作包括
安全操作 | 描述 |
---|---|
UsernameToken
| 新增使用者名稱令牌 |
UsernameTokenSignature
| 新增使用者名稱令牌和簽名使用者名稱令牌秘密金鑰 |
Timestamp
| 新增時間戳 |
Encrypt
| 加密響應 |
Signature
| 對響應進行簽名 |
NoSecurity
| 不執行任何操作 |
操作的順序很重要,並由攔截器強制執行。如果傳入 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 body。
例如,以下是如何在 Spring Web Services 回顯示例中籤名 echoResponse
元素的方法
<property name="securementSignatureParts" value="{}{http://www.springframework.org/spring-ws/samples/echo}echoResponse"/>
WS Security 規範定義了幾種格式來傳輸簽名令牌(證書)或對這些令牌的引用。因此,簡單的元素名稱 Token
會簽署令牌並處理不同的格式。要簽署 SOAP body 和簽名令牌,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] 。以下示例標識了回顯示例中的 echoResponse
<property name="securementEncryptionParts" value="{Content}{http://www.springframework.org/spring-ws/samples/echo}echoResponse"/>
請注意,元素名稱、名稱空間識別符號和加密修飾符是區分大小寫的。加密修飾符和名稱空間識別符號可以省略。在這種情況下,加密模式預設為 Content
,名稱空間設定為 SOAP 名稱空間。
要指定沒有名稱空間的元素,請使用值 Null
作為名稱空間名稱(區分大小寫)。如果未指定列表,處理器預設以 Content
模式加密 SOAP Body。
Wss4jSecurityInterceptor
的異常處理與 XwsSecurityInterceptor
的異常處理相同。有關更多資訊,請參閱 第 7.2.5 節,“安全異常處理”。