CXF+WS-Security+Spring WebService服务器端+客户端及注意问题
时间:2015-05-15 17:42:25
收藏:0
阅读:178
项目中要用到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目录下。
因为整这个东西费了很多时间,坐下记录。
共勉!
评论(0)