有时候我们的webservice在服务端需要做一个调用方的验证,以保证我们的服务只有指定的客户才能使用。虽然可以使用wss4j的方法来做安全验证,但是考虑到我们的项目会与被.net平台下的项目调用,为了避免跨平台间出现的问题,我们还是决定采用自定义soapheader的形式来添加验证信息。
先来看一下客户端发起请求的soap内容
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:apac="http://apache.org/"> <soapenv:Header/> <soapenv:Body> <apac:CyfTest> <!--Optional:--> <arg0>?</arg0> <!--Optional:--> <arg1>?</arg1> </apac:CyfTest> </soapenv:Body> </soapenv:Envelope>其中红字部分就是我们要添加自定义信息的位置,再来看一下客户端的spring配置文件
<bean id="TsbServiceFactory" class="org.apache.cxf.jaxws.JaxWsProxyfactorybean"> <property name="address" value="http://localhost:8080/TsbWebService/Cyf?wsdl"></property> <property name="serviceClass" value="tsb.ws.tsbinterface.ICyfClient"></property> <property name="outInterceptors"> <list> <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" /> <bean class="tsb.ws.common.authentication.AddPptSoapHeader"></bean> </list> </property> </bean>其中红色部分指定的类就是用来在请求的soap协议中加上自定义头部信息的处理类,处理类内容如下
package tsb.ws.common.authentication; import java.util.List; import javax.xml.namespace.QName; import org.apache.cxf.binding.soap.soapHeader; import org.apache.cxf.binding.soap.soapMessage; import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor; import org.apache.cxf.headers.Header; import org.apache.cxf.helpers.DOMUtils; import org.apache.cxf.interceptor.Fault; import org.apache.cxf.phase.Phase; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * 添加SoapHeader拦截器 * @author cyf * */ public class AddSoapHeader extends AbstractSoapInterceptor { public AddSoapHeader() { super(Phase.WRITE); } /** * 处理soap信息 * @param message soap信息 * <P>作成者:cyf */ public void handleMessage(SoapMessage message) throws Fault { // SoapHeader部分待添加的节点 QName qName = new QName("CertificationProxy"); Document doc = DOMUtils.createDocument(); // 验证用户名 Element id = doc.createElement("userid"); id.setTextContent("xxx"); // 验证密码 Element pwd = doc.createElement("userpwd"); pwd.setTextContent("xxx"); Element root = doc.createElementNS("http://tempuri.org/","CertificationProxy"); root.appendChild(id); root.appendChild(pwd); // 创建SoapHeader内容 SoapHeader header = new SoapHeader(qName,root); // 添加SoapHeader内容 List<Header> headers = message.getHeaders(); headers.add(header); } }
在调用服务的时候客户端就可以在soap请求的头部添加如下的信息其中userid和userpwd就是我们用来验证的用户名和密码
<?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:apac="http://apache.org/"> <soapenv:Header> <CertificationProxy xmlns="http://tempuri.org/"> <userid>xxx</userid> <userpwd>xxx</userpwd> </CertificationProxy> </soapenv:Header> <soapenv:Body> <apac:CyfTest> <!--Optional: --> <arg0>?</arg0> <!--Optional: --> <arg1>?</arg1> </apac:CyfTest> </soapenv:Body> </soapenv:Envelope>
服务端截取请求soap协议时需要在配置文件中添加拦截器
<jaxws:endpoint id="TsbWebService" implementor="tsb.ws.tsbimpl.TsbWebServiceImpl" address="/Tsb"> <jaxws:inInterceptors> <!-- 日志拦截器 --> <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" /> <!-- 自定义拦截器,用于实现认证操作 --> <bean class="tsb.ws.common.authentication.ReadSoapHeader" /> </jaxws:inInterceptors> <jaxws:serviceFactory> <ref bean="jaxWsServicefactorybean" /> </jaxws:serviceFactory> <jaxws:features> <bean class="org.apache.cxf.feature.LoggingFeature" /> </jaxws:features> </jaxws:endpoint>
拦截器内容如下,主要就是从soap协议中获取head部分的节点,然后拿头的值做判断。
package tsb.ws.common.authentication; import javax.xml.soap.soAPException; import javax.xml.soap.soAPHeader; import javax.xml.soap.soAPMessage; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.cxf.binding.soap.soapMessage; import org.apache.cxf.binding.soap.saaj.saajInInterceptor; import org.apache.cxf.interceptor.Fault; import org.apache.cxf.phase.AbstractPhaseInterceptor; import org.apache.cxf.phase.Phase; import org.w3c.dom.NodeList; public class cnm extends AbstractPhaseInterceptor<SoapMessage> { // 取得Log实例 private static Log log = LogFactory.getLog(ReadSoapHeader.class); private saajInInterceptor saa = new saajInInterceptor(); public cnm() { super(Phase.PRE_PROTOCOL); getAfter().add(saajInInterceptor.class.getName()); } @Override public void handleMessage(SoapMessage message) throws Fault { // 获取soap信息 SOAPMessage mess = message.getContent(SOAPMessage.class); if (mess == null) { saa.handleMessage(message); mess = message.getContent(SOAPMessage.class); } SOAPHeader head = null; try { head = mess.getSOAPHeader(); } catch (SOAPException e) { e.printstacktrace(); } if (head == null) { return; } try { // 读取soap头中的userid节点 NodeList nodes = head.getElementsByTagName("userid"); // 读取soap头中的userid节点 NodeList nodepass = head.getElementsByTagName("userpwd"); // 此处可加各种验证 } catch (Exception e) { SOAPException soapExc = new SOAPException("验证失败"); throw new Fault(soapExc); } } }
****************************************************************************************************************************************************
另外也记一下用wss4j的Usernametoken进行验证的配置,基本上和自定义soapheader也差不多同样是采用拦截器的方法
客户端配置文件的endpoint中添加如下拦截器,其中clientPasswordCallback用于指定添加验证信息的处理类;
Usernametoken是指使用用户名令牌;PasswordText指密码加密策略,这里直接文本;xxx 指别名。
<bean id="TsbServiceFactory" class="org.apache.cxf.jaxws.JaxWsProxyfactorybean"> <property name="address" value="http://localhost:8080/TsbWebService/Cyf?wsdl"></property> <property name="serviceClass" value="tsb.ws.tsbinterface.ICyfClient"></property> <property name="outInterceptors"> <list> <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" /> <bean class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"> <constructor-arg> <map> <entry key="action" value="Usernametoken" /> <entry key="passwordType" value="PasswordText" /> <entry key="user" value="xxx" /> <entry key="passwordCallbackRef"> <ref bean="clientPasswordCallback" /> </entry> </map> </constructor-arg> </bean> </list> </property> </bean>添加验证信息
package cxf.client; import java.io.IOException; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import org.apache.ws.security.WSPasswordCallback; public class ClientPasswordCallback implements CallbackHandler { @Override public void handle(Callback[] callbacks) throws IOException,UnsupportedCallbackException { WSPasswordCallback ws = (WSPasswordCallback) callbacks[0]; ws.setPassword("xxxx"); ws.setIdentifier("xxxx"); } }服务端通过拦截器接收验证信息,serverPasswordCallback表示验证处理的类
<jaxws:endpoint id="TsbWebService" implementor="tsb.ws.tsbimpl.TsbWebServiceImpl" address="/Tsb"> <jaxws:inInterceptors> <!-- 日志拦截器 --> <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" /> <!-- 自定义拦截器,用于实现认证操作 --> <bean class="tsb.ws.common.authentication.ReadSoapHeader" /> <bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"> <constructor-arg> <map> <entry key="action" value="Usernametoken" /> <entry key="passwordType" value="PasswordText" /> <entry key="user" value="xxx" /> <entry key="passwordCallbackRef"> <ref bean="serverPasswordCallback" /> </entry> </map> </constructor-arg> </bean> </jaxws:inInterceptors> <jaxws:serviceFactory> <ref bean="jaxWsServicefactorybean" /> </jaxws:serviceFactory> <jaxws:features> <bean class="org.apache.cxf.feature.LoggingFeature" /> </jaxws:features> </jaxws:endpoint>
服务端获取客户端发来的验证信息方法如下
WSPasswordCallback ws = (WSPasswordCallback) callbacks[0]; // 获取用户名
String identifier = ws.getIdentifier(); // 获取用户密码
String password = ws.getpassword();
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。