本文參考文檔如下:
MSDN 官方詳解 : http://www.microsoft.com/china/MSDN/library/WebServices/WebServices/HowASP.NETWebServicesWork.mspx?mfr=true
WS安全規范說明 : https://www.oasis-open.org/committees/download.php/16782/wss-v1.1-spec-os-UsernameTokenProfile.pdf
WS Security 一些歷史信息 : http://zh.wikipedia.org/wiki/WS-Security
如有不理解,請參考上面三個資源。
WS-Security 所涉及的三個方面:身份驗證、簽名和加密
1.身份驗證
常用的的三種認證方法:
1.1用戶名/密碼
在Apache CXF中,可以使用如下定義來使用用戶名密碼認證:
客戶端:
<jaxws:client id="xxx" serviceClass="xxxy" address="xxx"> <jaxws:outInterceptors> <bean class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"> <constructor-arg> <map> <entry key="action" value="UsernameToken"/> <entry key="user" value="xxx"/> <!-- Password_Digest = Base64 ( SHA-1 ( nonce + created + password ) ) --> <entry key="passwordType" value="PasswordDigest"/> <entry key="passwordCallbackRef" value-ref="myPasswordCallback"/> </map> </constructor-arg> </bean> </jaxws:outInterceptors> </jaxws:client>
以上的passwordType值為PasswordText時,則密碼使用明文傳輸;為PasswordDigest時,則Password_Digest = Base64 ( SHA-1 ( nonce + created + password ) ),
比如傳輸的SOAP報文為:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soap:mustUnderstand="1"> <wsse:UsernameToken wsu:Id="UsernameToken-1"> <wsse:Username>xxx</wsse:Username> <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">4t7Q2C0DnV21ie6ngsv6CwZ3vVw=</wsse:Password> <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">R+6n+Z5L6FaG8IqeDrLGXw==</wsse:Nonce> <wsu:Created>2014-04-21T09:56:51.361Z</wsu:Created> </wsse:UsernameToken> </wsse:Security> </soap:Header> <soap:Body> ...... </soap:Body> </soap:Envelope>
則上面計算的Password_Digest的參數則來源於wsse:Security下面的各個節點參數值。
使用加密的密碼傳輸,則客戶端與服務端都需要指定一個passwordCallbackRef,該實現類繼承CallbackHandler,下面做個示例
public class ClientMyPasswordCallback implements CallbackHandler { private static final Log log = LogFactory.getLog(MyPasswordCallback.class); /* (non-Javadoc) * @see javax.security.auth.callback.CallbackHandler#handle(javax.security.auth.callback.Callback[]) */ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { WSPasswordCallback pc = (WSPasswordCallback) callbacks[0]; pc.setPassword("xxx"); } }
服務端則使用:
public class ServerMyPasswordCallback implements CallbackHandler { private static final Log log = LogFactory.getLog(MyPasswordCallback.class); /* (non-Javadoc) * @see javax.security.auth.callback.CallbackHandler#handle(javax.security.auth.callback.Callback[]) */ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { WSPasswordCallback pc = (WSPasswordCallback) callbacks[0]; //通過數據庫得到該用戶名的密碼,這里略去該過程 String password = getPwdByUname(pc.getIdentifier()); pc.setPassword(password );//直接設置從數據庫得到的密碼,WSS4J自動匹配該值與客戶端傳入的值,不需要調用pc.getPassword();因為它總是返回null } }
服務端的攔截器中匹配密碼方式是自動的,不需要從pc.getPassword()得到密碼自己匹配,由於加密算法比較麻煩,所以省去這一步應該是比較好的設計。
X.509 證書與Kerberos則參考:
http://www.microsoft.com/china/MSDN/library/WebServices/WebServices/HowASP.NETWebServicesWork.mspx?mfr=true
文章里說明很清楚,不再重復。
2.簽名
簽名可以防止消息在傳輸中被篡改進行重復攻擊等,可使用私鑰對需要的部分進行簽名,比如在上節的<身份認證>中,如果消息被截獲,可導致重復攻擊,
需要對請求設置過期時間(wsu:Timestamp->wsu:Expires)並簽名。
3.消息包數據加密
若傳輸的消息屬於高安全級別,則需要使用X.509 證書對消息加密,即客戶端使用服務端的公鑰加密,服務端通過私鑰解包,消息包只能在知道服務端私鑰情況下才能解開。
要對全部SOAP包加密,可啟用SSL(通常為HTTPS)。