本文参考文档如下:
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)。