CXF3.0.2+Spring3.2.14 WebService入门实例六

继续修炼CXF,这次主要讲CXF安全性验证,从网上搜了很多关于ws-security的文章,大体分为三种验证方式。第一,使用UsernameToken方式验证;第二,数字签证方法。使用密钥,生成一对密钥公钥/私钥。 公钥保留在服务端,私钥存放在客户端;第三,其他方式验证,比如SSL,HTTPS等等。

使用UsernameToken方式验证,在server端的回调函数中WSPasswordCallback只能获得用户名(identifier),一般这里的逻辑是使用这个用户名到数据库中查询其密码,然后再设置到password属性,WSS4J 会自动比较客户端传来的值和你设置的这个值。你可能会问为什么这里CXF 不把客户端提交的密码传入让我们在ServerPasswordCallbackHandler中比较呢?这是因为客户端提交过来的密码在SOAP 消息中已经被加密为MD5 的字符串,如果我们要在回调方法中作比较,那么第一步要做的就是把服务端准备好的密码加密为MD5 字符串,由于MD5 算法参数不同结果也会有差别,另外,这样的工作CXF 替我们完成了.

根据上面说的这里就不用自己判断密码了,只要验证用户名后,在设置密码就可以自动验证了。介绍一下开发工具和开发环境,jdk1.6.0_43+Tomcat6.0.29+ MyEclipse10.5,没有使用Maven进行管理!

一、新建web工程,选择Java EE5.0

二、新建接口类HelloWorld.java

package com.test;

import javax.jws.WebService;

@WebService

public interface HelloWorld {

public String sayHello(String name);

}

三、新建接口实现类HelloWorldImpl.java

package com.test;

import javax.jws.WebService;

@WebService(endpointInterface = "com.test.HelloWorld")

public class HelloWorldImpl implements HelloWorld {

public String sayHello(String text) {

System.out.println("sayHello方法被调用!");

return "Hello " + text;

}

}

四、新建测试类Client.java

package com.test;

import java.util.HashMap;

import java.util.Map;

import org.apache.cxf.interceptor.LoggingInInterceptor;

import org.apache.cxf.interceptor.LoggingOutInterceptor;

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;

import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;

import org.apache.wss4j.dom.handler.WSHandlerConstants;

import com.util.AuthAddInterceptor;

import com.util.ClientPassCallbackHandler;

/**

*

* 类名: Client.java

* 作者: 张述飞

* 创建时间: 2016-2-3上午10:25:49

* 版本: V1.0

* 功能描述:

*/

public final class Client {

private Client() {

}

public static void main(String args[]) throws Exception {

JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();

factory.setServiceClass(HelloWorld.class);

factory.setAddress("http://localhost:8080/WbSecurity/HelloWorld");

factory.getInInterceptors().add(new LoggingInInterceptor());

Map<String, Object> outProps = new HashMap<String,Object>();

outProps.put("action", "UsernameToken");

//设置用户名

outProps.put("user", "admin");

outProps.put("passwordType", "PasswordText");

outProps.put("passwordCallbackClass", ClientPassCallbackHandler.class.getName());

factory.getOutInterceptors().add(new WSS4JOutInterceptor(outProps));

factory.getOutInterceptors().add(new AuthAddInterceptor());

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

HelloWorld hw =factory.create(HelloWorld.class);

String str =hw.sayHello("张述飞");

System.out.println(str);

}

}

五、新建AuthAddInterceptor.java

package com.util;

importjava.util.List;

importjavax.xml.namespace.QName;

importorg.apache.cxf.binding.soap.SoapMessage;

importorg.apache.cxf.headers.Header;

importorg.apache.cxf.helpers.DOMUtils;

importorg.apache.cxf.interceptor.Fault;

importorg.apache.cxf.phase.AbstractPhaseInterceptor;

importorg.apache.cxf.phase.Phase;

import org.w3c.dom.Document;

import org.w3c.dom.Element;

public classAuthAddInterceptor extends AbstractPhaseInterceptor<SoapMessage> {

public AuthAddInterceptor() {

super(Phase.PREPARE_SEND);

}

public void handleMessage(SoapMessagemessage) throws Fault {

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

Document doc =DOMUtils.createDocument();

Element auth =doc.createElementNS("http://zhangshufei/auth", "auth");

Element name =doc.createElement("name");

name.setTextContent("admin");

Element password =doc.createElement("password");

password.setTextContent("admin");

auth.appendChild(name);

auth.appendChild(password);

headers.add(new Header(newQName(""),auth));

}

}

六、新建ServerPassCallbackHandler.java

package com.util;

import java.io.IOException;

import javax.security.auth.callback.Callback;

import javax.security.auth.callback.CallbackHandler;

import javax.security.auth.callback.UnsupportedCallbackException;

import org.apache.wss4j.common.ext.WSPasswordCallback;

public class ServerPassCallbackHandler implements CallbackHandler {

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

UnsupportedCallbackException {

WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];

if (pc.getIdentifier().equals("admin")) {

/**

* 设置密码

* 这个密码和客户端发送的密码进行比较

* 如果客户端不同将抛出org.apache.ws.sercurity.WSSecurityException

*/

pc.setPassword("123");

}

}

}

七、新建ClientPassCallbackHandler.java

package com.util;

importjava.io.IOException;

importjavax.security.auth.callback.Callback;

importjavax.security.auth.callback.CallbackHandler;

importjavax.security.auth.callback.UnsupportedCallbackException;

importorg.apache.wss4j.common.ext.WSPasswordCallback;

public classClientPassCallbackHandler implements CallbackHandler {

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

UnsupportedCallbackException{

WSPasswordCallback pc =(WSPasswordCallback) callbacks[0];

if(pc.getIdentifier().equals("admin")) {

pc.setPassword("123");

}

}

}

八、使用Spring, applicationContext.xml

<?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:aop="http://www.springframework.org/schema/aop"

xmlns:tx="http://www.springframework.org/schema/tx"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:http-conf="http://cxf.apache.org/transports/http/configuration"

xmlns:jaxws="http://cxf.apache.org/jaxws"

xmlns:jaxrs="http://cxf.apache.org/jaxrs"

xsi:schemaLocation="

http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.2.xsd

http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-3.2.xsd

http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.2.xsd

http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.2.xsd

http://cxf.apache.org/transports/http/configurationhttp://cxf.apache.org/schemas/configuration/http-conf.xsd

http://cxf.apache.org/jaxwshttp://cxf.apache.org/schemas/jaxws.xsd

http://cxf.apache.org/jaxrshttp://cxf.apache.org/schemas/jaxrs.xsd" >

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

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

<bean id="hello" class="com.test.HelloWorldImpl"/>

<bean id="serverPassCallbackHandler" class="com.util.ServerPassCallbackHandler"></bean>

<jaxws:endpoint id="helloWsWorld" implementor="#hello" address="/HelloWorld">

<jaxws:inInterceptors>

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

<!--拦截器的构造函数参数-->

<constructor-arg>

<map>

<entry key="action"value="UsernameToken" />

<!--密码类型,passwordText表示明文-->

<entry key="passwordType"value="PasswordText" />

<entry key="passwordCallbackRef">

<!--回调函数引用-->

<ref bean="serverPassCallbackHandler" />

</entry>

</map>

</constructor-arg>

</bean>

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

</jaxws:inInterceptors>

<jaxws:outInterceptors>

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

</jaxws:outInterceptors>

</jaxws:endpoint>

</beans>

九、Web.xml

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

<web-app version="2.5"

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

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

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

<display-name></display-name>

<context-param>

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

<param-value>

classpath:applicationContext.xml

</param-value>

</context-param>

<listener>

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

</listener>

<!--Spring刷新Introspector防止内在泄露-->

<listener>

<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>

</listener>

<servlet>

<servlet-name>CXFService</servlet-name>

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

</servlet>

<servlet-mapping>

<servlet-name>CXFService</servlet-name>

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

</servlet-mapping>

<welcome-file-list>

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

</welcome-file-list>

</web-app>

十、需导入的jar包

aopalliance-1.0.jar

asm-3.3.1.jar

commons-codec-1.10.jar

commons-logging-1.1.3.jar

cxf-core-3.0.7.jar

cxf-manifest.jar

cxf-rt-bindings-soap-3.0.7.jar

cxf-rt-databinding-jaxb-3.0.7.jar

cxf-rt-frontend-jaxrs-3.0.7.jar

cxf-rt-frontend-jaxws-3.0.7.jar

cxf-rt-frontend-simple-3.0.7.jar

cxf-rt-transports-http-3.0.7.jar

cxf-rt-transports-http-jetty-3.0.7.jar

cxf-rt-ws-addr-3.0.7.jar

cxf-rt-ws-policy-3.0.7.jar

cxf-rt-ws-security-3.0.7.jar

cxf-rt-wsdl-3.0.7.jar

geronimo-javamail_1.4_spec-1.7.1.jar

geronimo-jaxws_2.2_spec-1.2.jar

javax.servlet-3.0.jar

javax.ws.rs-api-2.0.1.jar

jaxb-api-2.2.11.jar

jaxb-core-2.2.11.jar

jaxb-impl-2.2.11.jar

jetty-continuation-8.1.15.v20140411.jar

jetty-http-8.1.15.v20140411.jar

jetty-io-8.1.15.v20140411.jar

jetty-security-8.1.15.v20140411.jar

jetty-server-8.1.15.v20140411.jar

jetty-util-8.1.15.v20140411.jar

neethi-3.0.3.jar

slf4j-api-1.7.9.jar

slf4j-jdk14-1.7.9.jar

spring-aop-3.2.14.RELEASE.jar

spring-beans-3.2.14.RELEASE.jar

spring-context-3.2.14.RELEASE.jar

spring-core-3.2.14.RELEASE.jar

spring-expression-3.2.14.RELEASE.jar

spring-web-3.2.14.RELEASE.jar

stax2-api-3.1.4.jar

woodstox-core-asl-4.4.1.jar

wsdl4j-1.6.3.jar

wss4j-ws-security-common-2.0.6.jar

wss4j-ws-security-dom-2.0.6.jar

xmlschema-core-2.2.1.jar

xmlsec-2.0.5.jar

十一、 测试

启动Tomcat服务器,再运行Client.java,

服务器端会显示

2016-2-3 10:33:36org.apache.cxf.services.HelloWorldImplService.HelloWorldImplPort.HelloWorld

信息: Inbound Message

----------------------------

ID: 1

Address:http://localhost:8080/WbSecurity/HelloWorld?wsdl

Http-Method: GET

Content-Type:

Headers: {Accept=[image/gif,image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash,application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword,application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument,application/xaml+xml, */*], accept-encoding=[gzip, deflate],accept-language=[zh-CN,en-US;q=0.5], connection=[Keep-Alive],Content-Type=[null], host=[localhost:8080],referer=[http://localhost:8080/WbSecurity], ua-cpu=[x86], user-agent=[Mozilla/4.0(compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; .NET CLR3.0.04506.648; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET4.0C; .NET4.0E)]}

--------------------------------------

2016-2-3 10:33:54org.apache.cxf.services.HelloWorldImplService.HelloWorldImplPort.HelloWorld

信息: Inbound Message

----------------------------

ID: 2

Address:http://localhost:8080/WbSecurity/HelloWorld

Encoding: UTF-8

Http-Method: POST

Content-Type: text/xml;charset=UTF-8

Headers: {Accept=[*/*],cache-control=[no-cache], connection=[keep-alive], Content-Length=[824],content-type=[text/xml; charset=UTF-8], host=[localhost:8080],pragma=[no-cache], SOAPAction=[""], user-agent=[Apache CXF 3.0.7]}

Payload: <soap:Envelopexmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Header><wsse:Securityxmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"soap:mustUnderstand="1"><wsse:UsernameTokenwsu:Id="UsernameToken-40011b26-dd56-40ea-95de-9daa124d414b"><wsse:Username>admin</wsse:Username><wsse:PasswordType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">123</wsse:Password></wsse:UsernameToken></wsse:Security><authxmlns="http://zhangshufei/auth"><name>admin</name><password>admin</password></auth></soap:Header><soap:Body><ns2:sayHelloxmlns:ns2="http://test.com/"><arg0>张述飞</arg0></ns2:sayHello></soap:Body></soap:Envelope>

--------------------------------------

sayHello方法被调用!

2016-2-3 10:33:55org.apache.cxf.services.HelloWorldImplService.HelloWorldImplPort.HelloWorld

信息: Outbound Message

---------------------------

ID: 2

Response-Code: 200

Encoding: UTF-8

Content-Type: text/xml

Headers: {}

Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:sayHelloResponsexmlns:ns2="http://test.com/"><return>Hello 张述飞</return></ns2:sayHelloResponse></soap:Body></soap:Envelope>

--------------------------------------

在客户端会显示:

2016-2-3 10:33:54org.apache.cxf.services.HelloWorldService.HelloWorldPort.HelloWorld

信息: Outbound Message

---------------------------

ID: 1

Address:http://localhost:8080/WbSecurity/HelloWorld

Encoding: UTF-8

Http-Method: POST

Content-Type: text/xml

Headers: {Accept=[*/*],SOAPAction=[""]}

Payload: <soap:Envelopexmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Header><wsse:Securityxmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"soap:mustUnderstand="1"><wsse:UsernameTokenwsu:Id="UsernameToken-40011b26-dd56-40ea-95de-9daa124d414b"><wsse:Username>admin</wsse:Username><wsse:PasswordType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">123</wsse:Password></wsse:UsernameToken></wsse:Security><authxmlns="http://zhangshufei/auth"><name>admin</name><password>admin</password></auth></soap:Header><soap:Body><ns2:sayHelloxmlns:ns2="http://test.com/"><arg0>张述飞</arg0></ns2:sayHello></soap:Body></soap:Envelope>

--------------------------------------

2016-2-3 10:33:55org.apache.cxf.services.HelloWorldService.HelloWorldPort.HelloWorld

信息: Inbound Message

----------------------------

ID: 1

Response-Code: 200

Encoding: UTF-8

Content-Type:text/xml;charset=UTF-8

Headers:{content-type=[text/xml;charset=UTF-8], Date=[Wed, 03 Feb 2016 02:33:55 GMT],Server=[Apache-Coyote/1.1], transfer-encoding=[chunked]}

Payload: <soap:Envelopexmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:sayHelloResponsexmlns:ns2="http://test.com/"><return>Hello 张述飞</return></ns2:sayHelloResponse></soap:Body></soap:Envelope>

--------------------------------------

Hello 张述飞

十二、 这是我在做项目时遇到的几个错误,写下来以备再查!

Exception encountered duringcontext initialization - cancelling refresh attempt

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'helloWSEndpoint': Cannotcreate inner bean 'org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor#da0c91' of type[org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor] while setting beanproperty 'inInterceptors' with key [0]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor#da0c91' defined in class path resource[applicationContext.xml]: Resolution of declared constructors on bean Class[org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor] from ClassLoader[WebappClassLoader

context: /WbSecurity

delegate: false

repositories:

/WEB-INF/classes/

----------> ParentClassloader:

需导入xmlsec-2.0.5.jar

=============================================================

Invocation without a bindingoperation.

2016-2-1 16:41:03 org.apache.cxf.phase.PhaseInterceptorChaindoDefaultLogging

警告: Interceptor for {http://test.com/}HelloWorldImplServicehas thrown exception, unwinding now

org.apache.cxf.interceptor.Fault:No binding operation info while invoking unknown method with params unknown.

at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:59)

在applicationContext.xml中没有设置address后面的名字

<jaxws:endpointid="helloWsWorld" implementor="#hello"address="/HelloWorld">

=============================================================

org.apache.cxf.binding.soap.SoapFault:Empty username for specified action.

atorg.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor$WSS4JOutInterceptorInternal.handleMessageInternal(WSS4JOutInterceptor.java:238)

atorg.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor$WSS4JOutInterceptorInternal.handleMessage(WSS4JOutInterceptor.java:135)

客户端和服务器端通讯时,没有设置用户名,也就是user的属性

outProps.put("user","admin");

====================================================================

警告: Interceptor for {http://test.com/}HelloWorldService#{http://test.com/}sayHellohas thrown exception, unwinding now

java.lang.ClassCastException: java.lang.String cannot be cast tojavax.security.auth.callback.CallbackHandler

atorg.apache.wss4j.dom.handler.WSHandler.getCallbackHandler(WSHandler.java:1147)

outProps.put("passwordCallbackRef", ClientPassCallbackHandler.class.getName());

改为

outProps.put("passwordCallbackClass", ClientPassCallbackHandler.class.getName());

=====================================================================

java.lang.NoClassDefFoundError:org/apache/commons/codec/binary/Base64

缺少commons-codec-1.10.jar包

 

Exception in thread "main" javax.xml.ws.soap.SOAPFaultException: A security error was encountered when verifying themessage

可能是客户端和服务器端的密码不相同

到这里为止,关于CXF WbService的学习我们基本上要告一段落了,其实使用WbService,无非是发布服务,然后暴露一个方法,方便别的项目调用,再好一点的无非再就是加一个安全验证之类的,大家可以活学活用,真到了项目用到的时候,边学边用,带着问题学!随后我们将学习Maven,Activity工作流之类的东东,有兴趣的朋友请继续访问我的博客!我们年后见!祝大家新年好运!

代码下载地址:

http://download.csdn.net/detail/zhangshufei8001/9426474

猜你喜欢

转载自zhangshufei8001.iteye.com/blog/2377299
今日推荐