CXF3.0.3和Spring3.24进行整合

由于整合过程困难重重,特此记录,希望能帮到遇到同样问题的大家。

假设有一个需求:前台程序需要调用后台程序的“检测IP地址重复”服务,需要进行用户认证。

服务器端(后台)

一、定义提供服务的接口以及实现类

接口:

import java.util.List;

import javax.jws.WebService;

@WebService
public interface DataCenter {
	List<String> getIptvprofileMacListByIpAddress(String ip);
}

实现类:

import java.util.List;

import javax.annotation.PostConstruct;
import javax.jws.WebService;

import org.apache.commons.lang3.StringUtils;
import org.apache.cxf.staxutils.StaxUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

import com.google.common.collect.Lists;
import com.smt.iptv.portal.entity.Iptvprofile;
import com.smt.iptv.portal.service.cmp.IptvprofileService;

@Controller
@WebService
public class DataCenterImpl implements DataCenter{
	@Autowired
	private IptvprofileService iptvprofileService;
	
	// CXF 报java.lang.RuntimeException: Cannot create a secure XMLInputFactory的解决方案
	@PostConstruct
	public void init() {
		System.setProperty(StaxUtils.ALLOW_INSECURE_PARSER, "true");
	}
	
	/**
	 * 
	 * @title: getIptvprofile
	 * @description: 根据ipaddress查询Iptvprofile,如果查询到数据,返回Iptvprofile的macaddress组成的集合
	 * @author: Jack
	 * @version: V1.00
	 * @date: 2019年2月19日 下午10:40:39
	 * @param ip
	 * @return
	 */
	@Override
	public List<String> getIptvprofileMacListByIpAddress(String ip) {
		List<Iptvprofile> profileList = this.iptvprofileService.getByIpaddress(ip);
		List<String> macList = Lists.newArrayList();
		if (profileList != null && profileList.size() > 0) {
			for(Iptvprofile profile : profileList) {
				String mac = profile.getMacaddress();
				if (StringUtils.isNotBlank(mac)) {
					macList.add(mac);
				}
			}
		}
		
		return macList;
	}
}

这里说一下,后面在调用webService 时,遇到了一个安全性异常,解决思路来源于:https://blog.csdn.net/Swear_fling/article/details/45232875

二、定义CXF输入拦截器,进行用户认证

拦截器代码:

import javax.xml.namespace.QName;

import org.apache.commons.lang3.StringUtils;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.ContextLoader;
import org.springside.modules.security.utils.Digests;
import org.springside.modules.utils.Encodes;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import com.smt.iptv.portal.entity.role.User;
import com.smt.iptv.portal.service.account.UserService;

/**
 * 
 * Title: LoginVerifyInterceptor
 * Description: webService拦截器,进行登录验证,验证不通过则不进行接口方法调用。另外,shiro要开放相应url权限,否则会导向到登录界面的jsp。
 * @author Jack
 * @date   2019年2月21日 下午7:07:04
 *
 */
public class LoginVerifyInterceptor extends AbstractPhaseInterceptor<SoapMessage>{

	public LoginVerifyInterceptor() {
		super(Phase.PRE_INVOKE);		// 在调用接口方法之前进行拦截
	}

	@Override
	public void handleMessage(SoapMessage message) throws Fault {
		// TODO 从客户端发送的message中获取header,header中存放着要进行验证的用户名、密码
		Header header = message.getHeader(new QName("authcUser"));
		if (header == null) {
			throw new Fault(new IllegalArgumentException("没有进行登录验证,不允许调用"));
		}
		
		Element element = (Element) header.getObject();
		NodeList usernameNodeList = element.getElementsByTagName("username");
		NodeList passwordNodeList = element.getElementsByTagName("password");
		if (usernameNodeList.getLength() != 1 || passwordNodeList.getLength() != 1) {
			throw new Fault(new IllegalArgumentException("用户名或密码格式不对"));
		}
		
		String username = usernameNodeList.item(0).getTextContent();
		String password = passwordNodeList.item(0).getTextContent();
		if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
			throw new Fault(new IllegalArgumentException("用户名或密码不能为空"));
		}
		
		// TODO 根据用户名、密码,查询数据库,是否有对应的后台用户
		ApplicationContext ac = ContextLoader.getCurrentWebApplicationContext();
		UserService userService = (UserService) ac.getBean("userService");
		
		User user = userService.findUserByLoginName(username);
		if (user == null) {
			// 根据用户名查不到
			throw new Fault(new IllegalArgumentException("用户不存在"));
		}else {
			// 将客户端发送的密码经过相同的加密算法进行加密后,和数据库的密码进行比较
			byte[] input = Digests.sha1(password.getBytes(), Encodes.decodeHex(user.getSalt()), 1024);
			String encryptPassword = Encodes.encodeHex(input);
			if (!encryptPassword.equals(user.getPassword())) {
				throw new Fault(new IllegalArgumentException("密码不正确"));
			}
		}
	}

}

三、web.xml文件中注册CXF所需servlet

<servlet>
		<description>配置CXF所需servlet</description>
		<servlet-name>CXFServlet</servlet-name>
		<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
		<load-on-startup>10</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>CXFServlet</servlet-name>
		<url-pattern>/webservice/*</url-pattern>
	</servlet-mapping>

四、spring配置文件中,配置一个JAX-WS server

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:jaxws="http://cxf.apache.org/jaxws"
	xsi:schemaLocation="http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"
	default-lazy-init="true">
	
	<description>Spring公共配置</description>
	
	<!-- 引入cxf的xml文件,该文件在cxf core.jar包下 -->
	<import resource="classpath:META-INF/cxf/cxf.xml"/>
	<bean id="LoginVerifyInterceptor" class="com.smt.iptv.portal.web.cxf.LoginVerifyInterceptor"/>
	<!-- 发布webService -->
	<jaxws:endpoint id="DataCenterImpl" implementor="com.smt.iptv.portal.web.cxf.DataCenterImpl" 
		address="datacenter">
		<!-- 设置登录验证拦截器 -->
		<jaxws:inInterceptors>
			<ref bean="LoginVerifyInterceptor"/>
		</jaxws:inInterceptors>	
	</jaxws:endpoint>
</beans>

到这里,服务器端的代码及配置就结束了。

客户端(前台)

一、首先利用CXF提供的工具,生成客户端代码

下载并解压cxf后,进入bin目录(当然,可以通过配置环境变量完成)运行命令:wsdl2java -p com.jack.webservice.datacenter -d d:\src -client  http://www.webxml.com.cn/WebServices/WeatherWS.asmx?wsdl

-p后面是你要生成的包结构,-d后面是你生成文件的保存路劲,-client后面是你要使用的服务端访问地址.

 

生成代码之后将代码拷贝到你的项目中,也可以打成jar包后导入

二、定义CXF输出拦截器,发送用户名、密码

拦截器代码:

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;

/**
 * 
 * Title: LoginVerifyInterceptor
 * Description: webService拦截器,远程调用别人的接口方法,人家要求进行登录验证。
 * @author Jack
 * @date   2019年2月21日 下午7:40:18
 *
 */
public class LoginVerifyInterceptor extends AbstractPhaseInterceptor<SoapMessage> {

	private String username;
	private String password;
	
	public LoginVerifyInterceptor(String username, String password) {
		super(Phase.PREPARE_SEND);		// 准备发送调用请求前拦截	
		this.username = username;
		this.password = password;
	}

	@Override
	public void handleMessage(SoapMessage message) throws Fault {
		
		// TODO 将用户名、密码生成xml,往message的Header集合中添加
		Document doc = DOMUtils.createDocument();
		// 生成用户名元素节点
		Element usernameElement = doc.createElement("username");
		usernameElement.setTextContent(this.username);
		// 生成密码元素节点
		Element passwordElement = doc.createElement("password");
		passwordElement.setTextContent(this.password);
		
		Element element = doc.createElement("authcUser");
		element.appendChild(usernameElement);
		element.appendChild(passwordElement);
		
		List<Header> headerList = message.getHeaders();
		headerList.add(new Header(new QName("authcUser"), element));
	}

}

三、客户端调用webService发布的接口方法

		/*DataCenterImplService dataCenterImpl = new DataCenterImplService();
		DataCenter dc = dataCenterImpl.getDataCenterImplPort();
		// 在调用远程方法前,设置输出拦截器,进行登录验证(别人说验证不过不给调用)
		Client client = ClientProxy.getClient(dc);
		client.getOutInterceptors().add(new LoginVerifyInterceptor("admin", "123"));*/
		
		
		/**
		 * 用上面的方法,调远程方法会报异常:com.sun.xml.ws.client.sei.SEIStub cannot be cast to org.apache.cxf.frontend.ClientProxy
		 * 原因可能是因为CXF的JAX-WS实现没有装载,而是装载的SUN对JAX-WS的实现。通过配置文件修改classloader装载时不委派依旧无效!!!
		 */
		JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
		factory.getOutInterceptors().add(new LoginVerifyInterceptor("admin", "123456"));
		factory.setServiceClass(DataCenter.class);
		factory.setAddress("http://localhost:9999/iptvmanager/webservice/datacenter?wsdl");
		DataCenter dc = (DataCenter) factory.create();
		// 调用远程方法
		List<String> result = dc.getIptvprofileMacListByIpAddress(request.getRemoteHost());

这里又遇到一个异常:com.sun.xml.ws.client.sei.SEIStub cannot be cast to org.apache.cxf.frontend.ClientProxy。

解决思路来源于:https://stackoverflow.com/questions/2064068/how-to-pick-cxf-over-metro-on-glassfish

至此,整合完成,需求也实现了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值