您的位置:首页 > 编程语言 > Java开发

CXF动态客户端调用JDK自带Webservice安全校验

2015-11-17 18:13 585 查看
项目中有个需求,需要使用CXF动态客户端调用webservice服务端,这个服务端是JDK自带的webservice发布的,而且我们需要在调用时传入用户名和密码。网上CXF客户端和服务端配套使用webservice的方法很多,这里不再赘述,这里主要讲下我上边说的特殊使用方式。有人会把用户名和密码放在webservice的请求参数里,这样污染了webservice接口,本人不喜欢这种方式。

开始的时候,我先试了CXF拦截器传送密码的方式,我自己是参照类似的文章写的:点击打开链接,结果发现出现很多奇怪的错误,比如各种命名空间不识别,JAXB解组错误等等,压根就没有进入服务端的handler,当然了服务端的总入口也是拿到了请求,只是在进入handler之前就报错了。按说CXF也是遵守了webservice的国际规范了应该可以的。后来看了CXF中的传入用户名和密码所涉及的类和参数,分析出CXF应该是对SOAP的head操作做了封装,所以导致SOAP中的具体数据内容和JDK自带的webservice是不同的,怎么办呢?后来想到我可以直接自定义SOAP头,然后到服务端的handler中拿到这个头进行分析不就解决了?经过各种调试,终于解决了。下边公布代码:

1、服务端的handlers.xml

<?xml version="1.0" encoding="UTF-8"?>

<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">

<handler-chain>

<handler>

<handler-name>ServiceSOAPHandler</handler-name>

<handler-class>com.cms.webservice.AuthorityHandler</handler-class>

</handler>

</handler-chain>

</handler-chains>

2、服务端的handler代码:

/**

*

*/

package com.cms.webservice;

import java.io.File;

import java.io.IOException;

import java.util.Iterator;

import java.util.Set;

import javax.xml.namespace.QName;

import javax.xml.soap.Node;

import javax.xml.soap.SOAPBody;

import javax.xml.soap.SOAPConstants;

import javax.xml.soap.SOAPEnvelope;

import javax.xml.soap.SOAPException;

import javax.xml.soap.SOAPFault;

import javax.xml.soap.SOAPHeader;

import javax.xml.soap.SOAPMessage;

import javax.xml.ws.handler.MessageContext;

import javax.xml.ws.handler.soap.SOAPHandler;

import javax.xml.ws.handler.soap.SOAPMessageContext;

import javax.xml.ws.soap.SOAPFaultException;

import com.jeecms.cms.Constants;

import com.jeecms.common.util.PropertyUtils;

/**

* 服务端权限校验类

*

* @author zhaichong

*

* 2014年10月1日

*/

public class AuthorityHandler implements SOAPHandler<SOAPMessageContext> {

private static String PROPERTIE_NAME="webservice.properties";

private static String USER_NAME_PWD="webservice.userNamePwd";

public boolean handleMessage(SOAPMessageContext context) {

return auth(context);

}

/**

* 校验用户名密码是否合法

*

* @param context

* @return

*/

private boolean auth(SOAPMessageContext context) {

Boolean out = (Boolean) context.get(SOAPMessageContext.MESSAGE_OUTBOUND_PROPERTY);

if (!out) {

SOAPMessage message = context.getMessage();

try {

SOAPHeader header = message.getSOAPPart().getEnvelope().getHeader();

if (header != null) {

// 获取SOAP头登录校验节点

// org.w3c.dom.Node node =

// header.getElementsByTagName("authHeader").item(0);

// 获取用户名

org.w3c.dom.Node userNameNode = header.getElementsByTagName("userName").item(0);

String userName = userNameNode.getTextContent();

// 获取密码

org.w3c.dom.Node passwordNode = header.getElementsByTagName("password").item(0);

String password = passwordNode.getTextContent();

// 获取本地配置文件用户名密码 与传过来的用户名密码比对

PropertiesUtil propertiesUtil = new PropertiesUtil(PROPERTIE_NAME);

String userNamePWD = propertiesUtil.getKeyValue(USER_NAME_PWD);

if (userNamePWD != null) {

String namePWDs[] = userNamePWD.split(",");

for (int i = 0; i < namePWDs.length; i++) {

if (namePWDs[i] != null) {

String uName = namePWDs[i].split("\\|")[0];

String uPassword = namePWDs[i].split("\\|")[1];

if (uPassword.equals(password) && uName.equals(userName)) {

return true;

}

}

}

}

//System.out.println("client send userName:" + userName);

//System.out.println("client send password:" + password);

return false;

} else {

return false;

}

} catch (SOAPException e) {

e.printStackTrace();

}

}

return true;

}

public boolean handleFault(SOAPMessageContext context) {

// TODO Auto-generated method stub

return false;

}

public void close(MessageContext context) {

// TODO Auto-generated method stub

}

public Set<QName> getHeaders() {

// TODO Auto-generated method stub

return null;

}

}

注意: 这里的handler.xml文件在war包里是放在classes下的,webservice的实现类要用@HandlerChain(file="handlers.xml")进行注解,上边代码的用户名和密码是放在properties文件中的,各位网友可以自己修改。

3、客户端的拦截器:

package gboat2.cxf.test;

import gboat2.cxf.utils.AuthorityParameter;

import java.util.List;

import javax.xml.namespace.QName;

import org.apache.cxf.binding.soap.SoapMessage;

import org.apache.cxf.headers.Header;

import org.apache.cxf.helpers.DOMUtils;

import org.apache.cxf.interceptor.Fault;

import org.apache.cxf.phase.AbstractPhaseInterceptor;

import org.apache.cxf.phase.Phase;

import org.w3c.dom.Document;

import org.w3c.dom.Element;

/**

* <p>

* 调用webservice时需要传入用户名和密码

* </p>

*

*/

public class AuthorityHeaderInterceptor extends AbstractPhaseInterceptor<SoapMessage>{

private AuthorityParameter authorityParameter;

public AuthorityHeaderInterceptor(AuthorityParameter authorityParameter) {

super(Phase.PREPARE_SEND);

this.authorityParameter = authorityParameter;

}

public void handleMessage(SoapMessage msg) throws Fault {

List<Header> headers = msg.getHeaders();

//创建Document对象

Document doc = DOMUtils.createDocument();

//配置服务器端Head信息的用户密码

Element eleId= doc.createElement(this.authorityParameter.getUserNameKey());

eleId.setTextContent(this.authorityParameter.getUserNameValue());

Element elePass = doc.createElement(this.authorityParameter.getPasswordKey());

elePass.setTextContent(this.authorityParameter.getPasswordValue());

/**

* 也可以先创建一个父节点,则生成的XML文档 ,我们这里是直接使用用户名和密码

* <authHeader>

* <userId>lzw</userId>

* <userPass>123456</userPass>

* </authHeader>

*/

headers.add(new Header(new QName(""), eleId));

headers.add(new Header(new QName(""), elePass));

}

}

4、用户名和密码类:

package gboat2.cxf.utils;

public class AuthorityParameter {

/**

* 用户名字段的名称

*/

private String userNameKey;

/**

* 用户名字段的值

*/

private String userNameValue;

/**

* 密码字段的名称

*/

private String passwordKey;

/**

* 密码字段的值

*/

private String passwordValue;

public AuthorityParameter() {

super();

}

/**

* AuthorityParameter

* @param userNameKey 用户名的字段名称

* @param userNameValue 用户名的字段值

* @param passwordKey 密码的字段名称

* @param passwordValue 密码的字段值

*/

public AuthorityParameter(String userNameKey, String userNameValue, String passwordKey, String passwordValue) {

super();

this.userNameKey = userNameKey;

this.userNameValue = userNameValue;

this.passwordKey = passwordKey;

this.passwordValue = passwordValue;

}

public String getUserNameKey() {

return userNameKey;

}

public void setUserNameKey(String userNameKey) {

this.userNameKey = userNameKey;

}

public String getUserNameValue() {

return userNameValue;

}

public void setUserNameValue(String userNameValue) {

this.userNameValue = userNameValue;

}

public String getPasswordKey() {

return passwordKey;

}

public void setPasswordKey(String passwordKey) {

this.passwordKey = passwordKey;

}

public String getPasswordValue() {

return passwordValue;

}

public void setPasswordValue(String passwordValue) {

this.passwordValue = passwordValue;

}

}

5、调用代码

JaxWsDynamicClientFactory factory = JaxWsDynamicClientFactory.newInstance();

Client client = null;

// 创建客户端连接

client = factory.createClient("http://localhost:9090/cms/saveCms?wsdl");

AuthorityParameter param = new AuthorityParameter("userName", "anshun", "password", "123456");

client.getOutInterceptors().add(new AuthorityHeaderInterceptor(authorityParameter));

client.getOutInterceptors().add(new LoggingOutInterceptor());

// 客户端设置

HTTPClientPolicy policy = ((HTTPConduit) client.getConduit()).getClient();

policy.setConnectionTimeout(30000);

policy.setReceiveTimeout(180000);

// 以下就是具体调用方法,这里不写了,网上很多。

......

好了,这样在服务端就能获取到用户名和密码,又不会污染webservice的方法参数。记下来防止遗忘,也为各位网友解决这种问题提供个思路,网上这种资料太少了。其中的部分代码是从项目代码里摘录出来的,可能有的地方有纰漏,但是思路脉络已经清除了,希望能帮到各位网友。另外,如果有需要了解JDK自带webservice如何使用用户名和密码来进行调用前的校验,可以参考我的一个例子:点击打开链接
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: