CXF+WS-Security+Spring WebService服务器端+客户端及注意问题

项目中要用到webservice,刚听到的时候还挺开心的,因为我之前接触过,想来应该不是很难,

谁料,事实不是这样的....,让我费了个好劲啊。

不说了,下面上代码,这是入门级的,所以会比较详细,仔细看:

服务器端:

1、接口+实现类

//接口
package com.ekservice.service;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;

import com.ek.entry.user.User;

/**
 * @author Administrator
 * 类说明
 */
@WebService(targetNamespace="http://jeeek-dp/", name="ExampleService")
public interface ExampleService {

	@WebResult(name="password")
	@WebMethod(action="http://jeeek-dp/ExampleService/getStr")
	public User getStr(@WebParam(name="name")String userName);
}
//实现类
package com.ekservice.impl;

import javax.jws.WebService;

import com.ek.entry.user.User;
import com.ekservice.service.ExampleService;

/**
 * @author Administrator
 * 类说明
 */
@WebService(targetNamespace="http://jeeek-dp/", serviceName="ExampleService", name="ExampleService")
public class ExampleServiceImpl implements ExampleService {

	public User getStr(String userName) {
		User user = new User();
		user.setAddress("北京市海淀区222号");
		user.setMobilePhone("010-1101111");
		return user;
	}

}

2、WS-Security 安全验证 - 过滤器

package com.ekservice.interceptor;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;

import org.apache.ws.security.WSPasswordCallback;
import org.apache.ws.security.WSSecurityException;

/**
 * @author Administrator
 * 类说明
 */
public class ExampleServiceInterceptor implements CallbackHandler{

	private Map<String, String> passwords = new HashMap<String, String>();

	public ExampleServiceInterceptor() {
		passwords.put("admin", "password");//此处的对应的是验证信息-用户名+密码,必须与客户端一致才可验证通过
	}
	public void handle(Callback[] callbacks) throws IOException,
			UnsupportedCallbackException {

		for (int i = 0; i < callbacks.length; i++) {
			WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
			String identifier = pc.getIdentifier();//用户名
			int usage = pc.getUsage();//验证方式
			if (usage == WSPasswordCallback.USERNAME_TOKEN) {// 密钥方式USERNAME_TOKEN
				if (!passwords.containsKey(identifier)) {
					try {
						throw new WSSecurityException("User not match - "+identifier);
					} catch (WSSecurityException e) {
						e.printStackTrace();
					}
				}
				// username token pwd...
				// ▲这里的值必须和客户端设的值相同,从cxf2.4.x后校验方式改为cxf内部实现校验,不必自己比较password是否相同
				// 请参考:http://cxf.apache.org/docs/24-migration-guide.html的Runtime
				// Changes片段
				pc.setPassword(passwords.get(identifier));// ▲【这里非常重要】▲
				// ▲PS 如果和客户端不同将抛出org.apache.ws.security.WSSecurityException:
				// The
				// security token could not be authenticated or
				// authorized异常,服务端会认为客户端为非法调用
			}else if (usage == WSPasswordCallback.SIGNATURE) {// 密钥方式SIGNATURE
				if (!passwords.containsKey(identifier)) {
					try {
						throw new WSSecurityException("User not match - "+identifier);
					} catch (WSSecurityException e) {
						e.printStackTrace();
					}
				}
				// set the password for client's keystore.keyPassword
				// ▲这里的值必须和客户端设的值相同,从cxf2.4.x后校验方式改为cxf内部实现校验,不必自己比较password是否相同;
				// 请参考:http://cxf.apache.org/docs/24-migration-guide.html的Runtime
				// Changes片段
				pc.setPassword(passwords.get(identifier));// //▲【这里非常重要】▲
				// ▲PS:如果和客户端不同将抛出org.apache.ws.security.WSSecurityException:The
				// security token could not be authenticated or
				// authorized异常,服务端会认为客户端为非法调用
			}
		}
	}

}

3、Spring配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:jaxws="http://cxf.apache.org/jaxws"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://cxf.apache.org/jaxws
		http://cxf.apache.org/schemas/jaxws.xsd">
	<import resource="classpath:/META-INF/cxf/cxf.xml" />
	<import resource="classpath:/META-INF/cxf/cxf-servlet.xml" />
	<import resource="classpath:/META-INF/cxf/cxf-extension-soap.xml" />

        <!-- 安全验证过滤器 -->
	<bean id="exampleServiceInterceptor" class="com.ekservice.interceptor.ExampleServiceInterceptor"></bean>
	<!-- 接口实现类 -->
	<bean id="exampleServiceImpl" class="com.ekservice.impl.ExampleServiceImpl" />
        <!-- 发布接口 spring+cxf -->
        <jaxws:endpoint implementor="#exampleServiceImpl" address="/ExampleService">
        <!-- 为接口添加安全验证过滤器 -->
        <jaxws:inInterceptors>
			<bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
			<!-- 这个地方不知道为什么,浪费了我宝贵的时间,官方文档说CXF2.0.x需要加这个过滤器,2.1.x及以后版本可以不加,
                         但是我用的是CXF2.7.7不加SAAJInInterceptor过滤器报错-->
			<bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor" />
			<bean id="WSS4JInInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
				<constructor-arg>
					<map>
						<entry key="action" value="UsernameToken"></entry>
						<entry key="passwordType" value="PasswordText"></entry>
						<!-- 设置密码类型为加密
						<entry key="passwordType" value="PasswordDigest" />
						-->
						<entry key="passwordCallbackClass" value="com.ekservice.interceptor.ExampleServiceInterceptor"></entry>
					</map>
				</constructor-arg>
			</bean>
		</jaxws:inInterceptors>
		<jaxws:outInterceptors>
			<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
		</jaxws:outInterceptors>
	</jaxws:endpoint>

</beans>

4、User.java

package com.ek.entry.user;

import java.io.Serializable;
import java.util.Date;

/**
 * @包名   ek.entry.user.po
 * @文件名 User.java
 * @版本 V 1.0
 */
public class User implements Serializable{
	private static final long serialVersionUID = 1L;
	private int id;
	private String userName;	//用户名
	private String passWord;	//密码
	private String address;
	private String mobilePhone;
	private Date entryTime;		//录入时间
	private Date updateTime;	//更新时间
	private String entryUserId;	//录入人ID
	private String validFlag;	//有效标识
	private Integer access;
	/**
	 * @return the userName
	 */
	public String getUserName() {
		return userName;
	}
	/**
	 * @param userName the userName to set
	 */
	public void setUserName(String userName) {
		this.userName = userName;
	}
	/**
	 * @return the passWord
	 */
	public String getPassWord() {
		return passWord;
	}
	/**
	 * @param passWord the passWord to set
	 */
	public void setPassWord(String passWord) {
		this.passWord = passWord;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public String getMobilePhone() {
		return mobilePhone;
	}
	public void setMobilePhone(String mobilePhone) {
		this.mobilePhone = mobilePhone;
	}
	public static long getSerialversionuid() {
		return serialVersionUID;
	}
	/**
	 * @return the entryTime
	 */
	public Date getEntryTime() {
		return entryTime;
	}
	/**
	 * @param entryTime the entryTime to set
	 */
	public void setEntryTime(Date entryTime) {
		this.entryTime = entryTime;
	}
	/**
	 * @return the updateTime
	 */
	public Date getUpdateTime() {
		return updateTime;
	}
	/**
	 * @param updateTime the updateTime to set
	 */
	public void setUpdateTime(Date updateTime) {
		this.updateTime = updateTime;
	}
	/**
	 * @return the entryUserId
	 */
	public String getEntryUserId() {
		return entryUserId;
	}
	/**
	 * @param entryUserId the entryUserId to set
	 */
	public void setEntryUserId(String entryUserId) {
		this.entryUserId = entryUserId;
	}
	/**
	 * @return the validFlag
	 */
	public String getValidFlag() {
		return validFlag;
	}
	/**
	 * @param validFlag the validFlag to set
	 */
	public void setValidFlag(String validFlag) {
		this.validFlag = validFlag;
	}

	/**
	 * @return the id
	 */
	public int getId() {
		return id;
	}
	/**
	 * @param id the id to set
	 */
	public void setId(int id) {
		this.id = id;
	}
	public User() {
		super();

		this.entryTime = new Date();
		this.updateTime = new Date();
		this.validFlag = "1";
	}
	/**
	 * @return the access
	 */
	public Integer getAccess() {
		return access;
	}
	/**
	 * @param access the access to set
	 */
	public void setAccess(Integer access) {
		this.access = access;
	}

}

至此,服务器端开发完成,启动tomcat即可访问接口,http://localhost:8080/jeeek-dp/services/ExampleService?wsdl

客户端:

1、使用wsdl2java命令生成客户端代码,只保留生成的javabean跟接口即可,其他的文件可以全部删去,,我留下的文件。

2、编写客户端接口访问验证拦截器,只要确保用户名密码与服务器相同即可(我的理解),代码:

package com.ek.client.interceptor;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;

import org.apache.ws.security.WSPasswordCallback;
import org.apache.ws.security.WSSecurityException;

/**
 * @author
 * 类说明
 */
public class ExampleServiceClientInterceptor implements CallbackHandler{

	private Map<String, String> passwords = new HashMap<String, String>();

	public ExampleServiceClientInterceptor() {
		passwords.put("admin", "password");
	}
	public void handle(Callback[] callbacks) throws IOException,
			UnsupportedCallbackException {

		for (int i = 0; i < callbacks.length; i++) {
			WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
			String identifier = pc.getIdentifier();
			int usage = pc.getUsage();

			if (usage == WSPasswordCallback.USERNAME_TOKEN) {// 密钥方式USERNAME_TOKEN
				System.out.println(passwords.containsKey(identifier)+"="+passwords.get(identifier)+"-"+identifier);
				if (!passwords.containsKey(identifier)) {
					try {
						throw new WSSecurityException("User not match - "+identifier);
					} catch (WSSecurityException e) {
						e.printStackTrace();
					}
				}
				pc.setPassword(passwords.get(identifier));// //▲【这里非常重要】▲
			}else if (usage == WSPasswordCallback.SIGNATURE) {// 密钥方式SIGNATURE
				if (!passwords.containsKey(identifier)) {
					try {
						throw new WSSecurityException("User not match - "+identifier);
					} catch (WSSecurityException e) {
						e.printStackTrace();
					}
				}
				pc.setPassword(passwords.get(identifier));// //▲【这里非常重要】▲
			}
		}
	}

}

3、客户端配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:jaxws="http://cxf.apache.org/jaxws"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://cxf.apache.org/jaxws
		http://cxf.apache.org/schemas/jaxws.xsd">

	<bean id="exampleServiceClientInterceptor"
		class="com.ek.client.interceptor.ExampleServiceClientInterceptor"></bean>

	<jaxws:client id="service"
		address="http://localhost:8080/jeeek-dp/services/ExampleService"
		serviceClass="com.ek.client.services.example.ExampleService">
		<jaxws:outInterceptors>
			<bean class="org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor"></bean>
			<bean id="WSS4JOutInterceptor" 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="admin" />
						<!-- <entry key="mustUnderstand" value="false"></entry> -->
						<entry key="passwordCallbackRef">
							<ref bean="exampleServiceClientInterceptor" />
						</entry>
					</map>
				</constructor-arg>
			</bean>
		</jaxws:outInterceptors>
	</jaxws:client>

</beans>

4、客户端调用接口:

package com.ek.client.call;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.ek.client.services.example.ExampleService;
import com.ek.client.services.example.User;

/**
 * @author
 * 类说明
 */
public class ExampleServiceClient {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-cxf-client.xml");
		ExampleService es = (ExampleService) ctx.getBean("service");

		User u = es.getStr("ssss");

		System.out.println(u.getAddress);
	}
}

整个项目的包目录结构就不截图了,配置文件新建个src 文件放到里面即可,也可以直接放到src目录下。

因为整这个东西费了很多时间,坐下记录。

共勉!

时间: 2024-08-03 03:28:02

CXF+WS-Security+Spring WebService服务器端+客户端及注意问题的相关文章

使用CXF开发WebService服务器端接口

CXF作为java领域主流的WebService实现框架,Java程序员有必要掌握它. CXF主页:http://cxf.apache.org/ 简介:百度百科 今天的话,主要是用CXF来开发下WebService服务器端接口,明天写下开发客户端接口: 这里用Maven. 首先建一个Maven的j2se项目: 项目的jre用1.8,因为1.8有webservice的默认实现.不要用1.5 不然下面你用我的代码会有问题,用1.5的话,还需要另外加jar包,这里为了大家省事,要换成1.8; 根据规范

webService总结(一)——使用CXF公布和调用webService(不使用Spring)

CXF和Axis2是两个比較流行的webService框架,接下来我会写几篇博客简介怎样使用这两种框架. 首先,先简介一下CXF的使用. CXF公布webService有多种方法.这里我介绍三种: 1.不使用Spring.CXF自己主动公布webService 2.不使用Spring,CXF手动公布webService 3.使用Spring + CXF公布webService 这篇博客以实例介绍第一种方法--不使用Spring,CXF自己主动公布webService. 服务端: 文件夹结构例如以

使用CXF框架,发布webservice服务,并使用客户端远程访问webservice

使用CXF框架,发布webservice服务,并使用客户端远程访问webservice  1. CXF介绍 :soa的框架    * cxf 是 Celtrix (ESB框架)和 XFire(webserivice) 合并而成,并且捐给了apache      * CxF的核心是org.apache.cxf.Bus(总线),类似于Spring的 ApplicationContext    * CXF默认是依赖于Spring的    * Apache CXF 发行包中的jar,如果全部放到lib中

axis2+spring开发webservice服务器端

需求:开发VAC与SP间订购通知接口服务器端(SP端),给定VacSyncService_SPClient.wsdl文件 首先,官网下载axis2-1.6.2-bin.zip和axis2-1.6.2-war.zipaxis2-1.6.2-bin.zip包含axis2的jar包,工具和例子axis2-1.6.2-war.zip包含了axis2的web应用,发布web服务时,将自己的程序以特定文件结构发布到axis2的web应用的service目录中 1.根据wsdl生成服务器端代码解压axis2-

WebService -- Java 实现之 CXF ( 使用:Spring+CXF+Tomcat发布webService)

1. 新建一个Maven项目,选择webapp模板,命名为WS_Spring_CXF_Tomcat 2. 在POM.xml中添加Spring和CXF的依赖 <!-- 添加 Spring dependency --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.1.7.R

构建基于CXF的WebService服务(2)-- 利用CXF提供的wsdl2java工具创建客户端

1.环境配置 将CXF_HOME/bin加入到环境变量path中,如我的是D:\Java\Jar\apache-cxf-2.7.7\bin 打开cmd输入 wsdl2java -v 出现如下信息表示配置成功 2.wsdl2java的使用 (1)创建一个"Java Project"项目,暂且命名为client,将CXF用到的jar包引入进来,去掉jetty相关包,加入geronimo-jaxws_2.2_spec-1.1.jar包 (2)打开命令行工具,将目录切换到client项目中的s

webservice 之 使用cxf开发WebService服务器端接口

摘要:webservice 不是一种新技术,它是一种跨平台,跨语言的规范,用于不同平台,不同语言开发应用之间的交互. cxf:是java主流的WebService实现框架. 接下来开始 使用cxf开发WebService服务器端接口 1.新建一个maven项目: 2. 项目用jdk1.7(因为1.7有webservice的默认实现)项目结构如: 3. 首先新建接口,HelloWorld.java: package com.wh.webservice; import javax.jws.WebSe

使用Apache CXF开发WebServices服务端、客户端

在前一篇的博客中,我使用Xfire1.x来开发了WebServies的服务端. 但是如果你访问Apache的官网,可以看到xfire已经被合并了. 最新的框架叫做CXF. Apache CXF = Celtix + XFire. CXF 继承了 Celtix 和 XFire 两大开源项目的精华, 提供了对 JAX-WS 全面的支持,并且提供了多种 Binding .DataBinding.Transport 以及各种 Format 的支持,并且可以根据实际项目的需要,采用代码优先(Code Fi

性能优化之——.NET(C#)调用webService获取客户端IP地址所属区域(异步回调)(二)

朋友们这次分享的是异步回调不是异步调用哦! 请注意喽! 功能描述,接口地址,方法名称以及参数说明,同上篇:.NET(C#)调用webService获取客户端IP地址所属区域(非异步)(一)(LZ比较懒,不想写太多哦!(⊙0⊙)) 实现代码如下: 1 namespace main 2 { 3 class Program 4 { 5 public static string Result = string.Empty; 6 7 static void Main(string[] args) 8 {