您的位置:首页 > 其它

Cxf+wss4j的WS-Security实现【未验证】

2015-07-18 14:20 323 查看
文章来源:http://blog.csdn.net/wangchsh2008/article/details/6708270

最近一个项目预研,需要使用webservice,进行消息安全传输,客户要求包括加密和认证。我们使用的ws框架是cxf,认证是很容易做的,通过自定义实现一个Interceptor,就可以进行简单的用户和密码认证。但加密从来没有做过,在网上做了一些工作之后,发现cxf是可以通过wss4j实现签名、加密的。

根据网上的资料,做了一个demo,上传位置在:http://download.csdn.net/source/3539244 ,下载即可直接运行。

下面就过程,做一个简单说明。

WSS4J支持如下几种模式:

XML Security

XML Signature

XML Encryption

Tokens

Username Tokens

Timestamps

SAML Tokens

这里将使用Timestamps+Encryption+Signature组合。 关于wss4j的学习,请参考apache文档。

第一步是要生成服务端及客户端密钥文件,这里用到了JDK中的keytool工具,关于Keytool的使用以及参数说明,请读者自行学习相关文档。

本文中为了方便起见,已经将命令编辑为2个bat文件。在同一目录下,执行generateServerKey.bat批处理,即可生成clientStore.jks及serverStore.jks文件(clientKey.rsa和serverKey.rsa文件用不到)。

generateKeyPair.bat

[plain] view
plaincopy

rem @echo off

echo alias %1

echo keypass %2

echo keystoreName %3

echo KeyStorePass %4

echo keyName %5

echo keyName %5

keytool -genkey -alias %1 -keypass %2 -keystore %3 -storepass %4 -dname "cn=%1" -keyalg RSA

keytool -selfcert -alias %1 -keystore %3 -storepass %4 -keypass %2

keytool -export -alias %1 -file %5 -keystore %3 -storepass %4

generateServerKey.bat

[plain] view
plaincopy

call generateKeyPair.bat apmserver apmserverpass serverStore.jks keystorePass serverKey.rsa

call generateKeyPair.bat apmclient apmclientpass clientStore.jks keystorePass clientKey.rsa

keytool -import -alias apmserver -file serverKey.rsa -keystore clientStore.jks -storepass keystorePass -noprompt

keytool -import -alias apmclient -file clientKey.rsa -keystore serverStore.jks -storepass keystorePass -noprompt

生成的密钥文件中包含的信息:

服务端 账户:apmserver / apmserverpass

客户端 账户:apmclient / apmclientpass

如下图所示建立工程:



lib下的jar:



在服务器端,有一个对密码进行处理的类 PasswordHandler.java

[java] view
plaincopy

package com.db.webservice.security;

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;

/**

* desc: comment PasswordHandler.java

* @author Chaisson(chengshengwang)

* @since Aug 19, 2011 5:08:30 PM

* @vision 1.0

*/

public class PasswordHandler implements CallbackHandler {

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

public PasswordHandler() {

passwords.put("apmserver", "apmserverpass");

passwords.put("apmclient", "apmclientpass");

}

public void handle(Callback[] callbacks) throws IOException,

UnsupportedCallbackException {

WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];

String id = pc.getIdentifer();

pc.setPassword((String) passwords.get(id));

}

}

定义一个interface:TestService.java

[java] view
plaincopy

package com.db.webservice;

import javax.jws.WebParam;

import javax.jws.WebService;

/**

* desc: comment TestService.java

* @author Chaisson(chengshengwang)

* @since May 13, 2011 11:41:03 AM

* @vision 1.0

*/

@WebService

public interface TestService {

public String sayHello(@WebParam(name="myName") String name);

public String printMan(User user);

}

实现上面的接口TestServiceImpl.java

[java] view
plaincopy

package com.db.webservice.impl;

import com.db.webservice.TestService;

import com.db.webservice.User;

/**

* desc: comment TestServiceImpl.java

* @author Chaisson(chengshengwang)

* @since May 13, 2011 11:41:38 AM

* @vision 1.0

*/

public class TestServiceImpl implements TestService {

public String sayHello(String myName){

System.out.println("Hello World! "+myName);

return "SUCCESS";

}

public String printMan(User user) {

StringBuffer sb = new StringBuffer();

if(user.getAge()>=18 && user.getAge()<60){

sb.append("He is a young man. ");

}else if(user.getAge()>=60){

sb.append("He is an old man. ");

}else{

sb.append("He is a little boy. ");

}

if(user.getName()!=null){

sb.append(" His name is "+user.getName()+". ");

}

if(user.getDesc()!=null){

sb.append(" His description is that "+user.getDesc()+". ");

}

return sb.toString();

}

}

一个PO: User.java

[java] view
plaincopy

package com.db.webservice;

/**

* desc: comment User.java

* @author Chaisson(chengshengwang)

* @since May 13, 2011 5:20:38 PM

* @vision 1.0

*/

public class User {

private int age;

private String name;

private String desc;

//省去setter/getter

}

还有3个配置文件:

server_insecurity_enc.properties

org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin

org.apache.ws.security.crypto.merlin.keystore.type=jks

org.apache.ws.security.crypto.merlin.keystore.password=keystorePass

org.apache.ws.security.crypto.merlin.alias.password=apmserverpass

org.apache.ws.security.crypto.merlin.keystore.alias=apmserver

org.apache.ws.security.crypto.merlin.file=serverStore.jks

server_insecurity_sign.properties

org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin

org.apache.ws.security.crypto.merlin.keystore.type=jks

org.apache.ws.security.crypto.merlin.keystore.password=keystorePass

#org.apache.ws.security.crypto.merlin.alias.password=apmserverpass

org.apache.ws.security.crypto.merlin.keystore.alias=apmserver

org.apache.ws.security.crypto.merlin.file=serverStore.jks

server_outsecurity_enc.properties

org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin

org.apache.ws.security.crypto.merlin.keystore.type=jks

org.apache.ws.security.crypto.merlin.keystore.password=keystorePass

#org.apache.ws.security.crypto.merlin.alias.password=apmserverpass

#org.apache.ws.security.crypto.merlin.keystore.alias=apmserver

org.apache.ws.security.crypto.merlin.file=serverStore.jks

如果我没有看错的话,上面3个配置是一摸一样的,呵呵,仅仅是后面的有部分被注释了。

在客户端,我们首先需要一个Test类,TestServiceClient.java

[java] view
plaincopy

package com.db.webservice.test;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.FileSystemXmlApplicationContext;

import com.db.webservice.TestService;

import com.db.webservice.User;

/**

* desc: comment TestServiceClient.java

* @author Chaisson(chengshengwang)

* @since May 13, 2011 2:17:04 PM

* @vision 1.0

*/

public class TestServiceClient {

/**

* desc:

* @Chaisson(chengshengwang)

* @since May 13, 2011

* @version 1.0

* @param args

*/

public static void main(String[] args) {

/*JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean ();

factory.setServiceClass(TestService.class);

factory.setAddress("http://localhost:8080/TestCXF/services/MyService");

TestService service =(TestService)factory.create();

service.sayHello("Chaisson");

User user = new User();

user.setAge(10);

user.setName("Chaisson");

user.setDesc("He is a good man");

System.out.println(service.printMan(user));*/

ApplicationContext ctx = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");

TestService client = (TestService)ctx.getBean("client");

User user = new User();

user.setAge(10);

user.setName("Chaisson");

user.setDesc("He is a good man");

System.out.println(client.printMan(user));

}

}

当然也需要3个properties配置文件

insecurity_enc.properties

org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin

org.apache.ws.security.crypto.merlin.keystore.type=jks

org.apache.ws.security.crypto.merlin.keystore.password=keystorePass

org.apache.ws.security.crypto.merlin.alias.password=apmclientpass

org.apache.ws.security.crypto.merlin.keystore.alias=apmclient

org.apache.ws.security.crypto.merlin.file=clientStore.jks

outsecurity_enc.properties

org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin

org.apache.ws.security.crypto.merlin.keystore.type=jks

org.apache.ws.security.crypto.merlin.keystore.password=keystorePass

org.apache.ws.security.crypto.merlin.alias.password=apmclientpass

org.apache.ws.security.crypto.merlin.keystore.alias=apmclient

org.apache.ws.security.crypto.merlin.file=clientStore.jks

outsecurity_sign.properties

org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin

org.apache.ws.security.crypto.merlin.keystore.type=jks

org.apache.ws.security.crypto.merlin.keystore.password=keystorePass

org.apache.ws.security.crypto.merlin.alias.password=apmclientpass

org.apache.ws.security.crypto.merlin.keystore.alias=apmclient

org.apache.ws.security.crypto.merlin.file=clientStore.jks

如果我没有看错的话,这回3个配置文件真的一摸一样了。

在客户端还需要加一个服务发布地址的配置文件

serverhost.properties

host.url=http://127.0.0.1:8080/TestCXF/services

最后整个工程少不了Spring配置文件:

applicationContext.xml

[html] view
plaincopy

<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.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

<bean id="client" class="com.db.webservice.TestService"

factory-bean="clientFactory" factory-method="create">

</bean>

<bean id="clientFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">

<property name="serviceClass" value="com.db.webservice.TestService" />

<property name="address" value="${host.url}/MyService" />

<property name="outInterceptors">

<list>

<bean class="org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor" />

<ref bean="wss4jOutConfiguration" />

</list>

</property>

<property name="inInterceptors">

<list>

<bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor" />

<ref bean="wss4jInConfiguration" />

</list>

</property>

</bean>

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

<property name="locations">

<list>

<value>classpath:serverhost.properties</value>

</list>

</property>

</bean>

<bean id="wss4jOutConfiguration" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">

<property name="properties">

<map>

<entry key="action" value="Timestamp Encrypt Signature" />

<entry key="user" value="apmclient" />

<entry key="encryptionUser" value="apmserver" />

<entry key="signaturePropFile" value="outsecurity_sign.properties" />

<entry key="signatureKeyIdentifier" value="IssuerSerial" />

<entry key="encryptionPropFile" value="outsecurity_enc.properties" />

<entry>

<key>

<value>passwordCallbackRef</value>

</key>

<ref bean="passwordCallback" />

</entry>

</map>

</property>

</bean>

<bean id="wss4jInConfiguration" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">

<property name="properties">

<map>

<entry key="action" value="Timestamp Encrypt Signature" />

<entry key="user" value="apmclient" />

<entry key="decryptionPropFile" value="insecurity_enc.properties" />

<entry key="enableSignatureConfirmation" value="true" />

<entry key="signaturePropFile" value="outsecurity_sign.properties" />

<entry key="signatureKeyIdentifier" value="IssuerSerial" />

<entry>

<key>

<value>passwordCallbackRef</value>

</key>

<ref bean="passwordCallback" />

</entry>

</map>

</property>

</bean>

<bean id="passwordCallback" class="com.db.webservice.security.PasswordHandler"/>

</beans>

cxf的配置文件:
cxf-servlet.xml

[html] view
plaincopy

<?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.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-extension-soap.xml" />

<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

<bean id="testService" class="com.db.webservice.impl.TestServiceImpl" />

<jaxws:endpoint id="myService" implementor="#testService" address="/MyService" >

<jaxws:inInterceptors>

<bean class="org.apache.cxf.interceptor.LoggingInInterceptor"/>

<bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor"/>

<ref bean="wss4jInConfiguration"/>

</jaxws:inInterceptors>

<jaxws:outInterceptors>

<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"/>

<bean class="org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor"/>

<ref bean="wss4jOutConfiguration"/>

</jaxws:outInterceptors>

</jaxws:endpoint>

<!--

<bean id="client" class="com.db.webservice.TestService"

factory-bean="clientFactory" factory-method="create"/>

<bean id="clientFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">

<property name="serviceClass" value="com.db.webservice.TestService"/>

<property name="address" value="http://localhost:8080/TestCXF/services/MyService"/>

</bean> -->

<bean id="wss4jInConfiguration" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">

<property name="properties">

<map>

<entry key="action" value="Timestamp Encrypt Signature"/>

<entry key="decryptionPropFile" value="server_insecurity_enc.properties"/>

<entry key="signaturePropFile" value="server_insecurity_sign.properties"/>

<entry>

<key>

<value>passwordCallbackRef</value>

</key>

<ref bean="passwordCallback"/>

</entry>

</map>

</property>

</bean>

<bean id="wss4jOutConfiguration" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">

<property name="properties">

<map>

<entry key="action" value="Timestamp Encrypt Signature"/>

<entry key="user" value="apmserver" />

<entry key="encryptionUser" value="apmclient" />

<entry key="encryptionPropFile" value="server_outsecurity_enc.properties"/>

<entry key="signaturePropFile" value="server_insecurity_sign.properties"/>

<entry>

<key>

<value>passwordCallbackRef</value>

</key>

<ref bean="passwordCallback"/>

</entry>

</map>

</property>

</bean>

<bean id="passwordCallback" class="com.db.webservice.security.PasswordHandler"/>

</beans>

还有一个web.xml,也简单提一下吧

[html] view
plaincopy

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

<web-app version="2.4"

xmlns="http://java.sun.com/xml/ns/j2ee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>

<description>cxfServlet</description>

<display-name>cxfServlet</display-name>

<servlet-name>cxfServlet</servlet-name>

<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>

<load-on-startup>2</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>cxfServlet</servlet-name>

<url-pattern>/services/*</url-pattern>

</servlet-mapping>

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>WEB-INF/cxf-servlet.xml</param-value>

</context-param>

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

<welcome-file-list>

<welcome-file>index.jsp</welcome-file>

</welcome-file-list>

</web-app>

好了,project搭建完毕,在最后进行测试的时候,发生了很多问题。

如果你从http://download.csdn.net/source/3539244位置下载了我的demo,直接run的话,也是会报异常的。因为某些国家对加密算法有一定限制,所以SUN公司在发布JDK的时候里面的策略文件是限制版的。需要到SUN公司网站上下载非限制版的策略文件进行替换。

解决的办法,请参考我的另一篇文章http://blog.csdn.net/wangchsh2008/article/details/6708718

本文参考了以下一些文章:
http://blog.csdn.net/kunshan_shenbin/article/details/3813000 http://blog.myspace.cn/e/403951535.htm http://cxf.apache.org/docs/ws-security.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: