您的位置:首页 > 其它

CXF 入门:创建一个基于WS-Security标准的安全验证(CXF回调函数使用,)

2016-04-18 08:09 597 查看
http://jyao.iteye.com/blog/1346547

注意:以下客户端调用代码中获取服务端ws实例,都是通过CXF 入门: 远程接口调用方式实现

直入正题!

以下是服务端配置

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

一,web.xml配置,具体不在详述

Xml代码


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

<!DOCTYPE web-app

PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"

"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

<context-param>

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

<!--ws-context.xml(必须)是cxf配置文件, wssec.xml可选,作用可以打印出加密信息类容 -->

<param-value>WEB-INF/ws-context.xml,WEB-INF/wssec.xml</param-value>

</context-param>

<listener>

<listener-class>

org.springframework.web.context.ContextLoaderListener

</listener-class>

</listener>

<servlet>

<servlet-name>CXFServlet</servlet-name>

<display-name>CXF Servlet</display-name>

<servlet-class>

org.apache.cxf.transport.servlet.CXFServlet

</servlet-class>

<load-on-startup>0</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>CXFServlet</servlet-name>

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

</servlet-mapping>

</web-app>

二,ws具体代码
简单的接口

Java代码


import javax.jws.WebService;

@WebService()

public interface WebServiceSample {

String say(String name);

}

接口具体实现类

Java代码


public class WebServiceSampleImpl implements WebServiceSample {

public String say(String name) {

return "你好," + name;

}

}

三,ws回调函数,必须实现javax.security.auth.callback.CallbackHandler

从cxf2.4.x后校验又cxf内部实现校验,所以不必自己校验password是否相同,但客户端必须设置,详情请参考:http://cxf.apache.org/docs/24-migration-guide.html的Runtime Changes片段

回调函数WsAuthHandler代码,校验客户端请求是否合法 ,合法就放行,否则拒绝执行任何操作

Java代码


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.cxf.interceptor.Fault;

import org.apache.ws.security.WSPasswordCallback;

import org.apache.xmlbeans.impl.soap.SOAPException;

public class WsAuthHandler implements CallbackHandler {

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

// username token pwd...

// ▲这里的值必须和客户端设的值相同,从cxf2.4.x后校验方式改为cxf内部实现校验,不必自己比较password是否相同

// 请参考:http://cxf.apache.org/docs/24-migration-guide.html的Runtime

// Changes片段

pc.setPassword("testPassword");// ▲【这里非常重要】▲

// ▲PS 如果和客户端不同将抛出org.apache.ws.security.WSSecurityException:

// The

// security token could not be authenticated or

// authorized异常,服务端会认为客户端为非法调用

} else if (usage == WSPasswordCallback.SIGNATURE) {// 密钥方式SIGNATURE

// 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("testPassword");// //▲【这里非常重要】▲

// ▲PS:如果和客户端不同将抛出org.apache.ws.security.WSSecurityException:The

// security token could not be authenticated or

// authorized异常,服务端会认为客户端为非法调用

}

//不用做其他操作

}

}

}

四,CXF配置ws-context.xml:

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: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" />

<!-- 以上未基本配置,必须,位置在cxf jar中 -->

<jaxws:endpoint id="webServiceSample" address="/WebServiceSample"

implementor="com.service.impl.WebServiceSampleImpl">

<!--inInterceptors表示被外部调用时,调用此拦截器 -->

<jaxws:inInterceptors>

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

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

<constructor-arg>

<map>

<!-- 设置加密类型 -->

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

<!-- 设置密码类型为明文 -->

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

<!--<entry key="action" value="UsernameToken Timestamp" /> 设置密码类型为加密<entry

key="passwordType" value="PasswordDigest" /> -->

<entry key="passwordCallbackClass" value="com.service.handler.WsAuthHandler" />

</map>

</constructor-arg>

</bean>

</jaxws:inInterceptors>

</jaxws:endpoint>

</beans>

CXF配置wssec.xml(可选),用于配置输出校验的具体信息

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:cxf="http://cxf.apache.org/core"

xmlns:wsa="http://cxf.apache.org/ws/addressing" xmlns:http="http://cxf.apache.org/transports/http/configuration"

xmlns:wsrm-policy="http://schemas.xmlsoap.org/ws/2005/02/rm/policy"

xmlns:wsrm-mgr="http://cxf.apache.org/ws/rm/manager"

xsi:schemaLocation="
http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd
http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd
http://schemas.xmlsoap.org/ws/2005/02/rm/policy http://schemas.xmlsoap.org/ws/2005/02/rm/wsrm-policy.xsd
http://cxf.apache.org/ws/rm/manager http://cxf.apache.org/schemas/configuration/wsrm-manager.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<cxf:bus>

<cxf:features>

<cxf:logging />

<wsa:addressing />

</cxf:features>

</cxf:bus>

</beans>

服务端代码及配置到此结束!!!

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

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

以下是客户端配置,主要是回调函数,在客户端调用服务端前被调用,负责安全信息的设置

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

一,先实现回调函数WsClinetAuthHandler,同样必须实现javax.security.auth.callback.CallbackHandler

Java代码


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.ws.security.WSPasswordCallback;

public class WsClinetAuthHandler implements CallbackHandler {

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

UnsupportedCallbackException {

for (int i = 0; i < callbacks.length; i++) {

WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];

System.out.println("identifier: " + pc.getIdentifier());

// 这里必须设置密码,否则会抛出:java.lang.IllegalArgumentException: pwd == null

// but a password is needed

pc.setPassword("testPassword");// ▲【这里必须设置密码】▲

}

}

}

二,客户端调用代码:

Java代码


import java.util.ArrayList;

import java.util.HashMap;

import java.util.Map;

import org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor;

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;

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

import org.apache.ws.security.WSConstants;

import org.apache.ws.security.handler.WSHandlerConstants;

import test.saa.client.WebServiceSample;

import test.saa.handler.WsClinetAuthHandler;

public class TestClient {

public static void main(String[] args) {

// 以下和服务端配置类似,不对,应该说服务端和这里的安全验证配置一致

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

outProps.put(WSHandlerConstants.ACTION,

WSHandlerConstants.USERNAME_TOKEN);

outProps.put(WSHandlerConstants.USER, "admin");

outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);

// 指定在调用远程ws之前触发的回调函数WsClinetAuthHandler,其实类似于一个拦截器

outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS,

WsClinetAuthHandler.class.getName());

ArrayList list = new ArrayList();

// 添加cxf安全验证拦截器,必须

list.add(new SAAJOutInterceptor());

list.add(new WSS4JOutInterceptor(outProps));

JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();

// WebServiceSample服务端接口实现类,这里并不是直接把服务端的类copy过来,具体请参考http://learning.iteye.com/blog/1333223

factory.setServiceClass(WebServiceSample.class);

// 设置ws访问地址

factory.setAddress("http://localhost:8080/cxf-wssec/services/WebServiceSample");

//注入拦截器,用于加密安全验证信息

factory.getOutInterceptors().addAll(list);

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

String response = service.say("2012");

System.out.println(response);

}

}

客户端到此结束!!!!

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

#######################################################################

PS:客户端的另一种调用方式,主要通过配置文件,不过需要spring bean的配置文件(第一种就不用牵扯到spring的配置,比较通用吧!)

一,回调函数WsClinetAuthHandler不变,和上面一样
二,client-beans.xml安全验证配置文件,具体信息看注释,如下:

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

xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schema/jaxws.xsd">

<!--这里无非是通过配置来替代JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean()创建代理并实例化一个ws-->

<bean id="client" class="test.saa.client.WebServiceSample"

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

<!-- 通过代理创建ws实例 -->

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

<property name="serviceClass" value="test.saa.client.WebServiceSample" />

<!-- ws地址,也可以是完整的wsdl地址 -->

<property name="address"

value="http://localhost:8080/cxf-wssec/services/WebServiceSample" />

<!--outInterceptors表示调用外部指定ws时,调用此拦截器 -->

<property name="outInterceptors">

<list>

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

<ref bean="wss4jOutConfiguration" />

</list>

</property>

</bean>

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

<property name="properties">

<map>

<!-- 设置加密类型 ,服务端需要和这里的设置保持一致-->

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

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

<!-- 设置密码为明文 ,服务端需要和这里的设置保持一致-->

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

<!-- <entry key="action" value="UsernameToken Timestamp" /> <entry key="user"

value="adminTest" /> 设置密码类型为加密方式,服务端需要和这里的设置保持一致<entry key="passwordType" value="PasswordDigest"

/> -->

<entry key="passwordCallbackRef">

<ref bean="passwordCallback" />

</entry>

</map>

</property>

</bean>

<bean id="passwordCallback" class="test.saa.handler.WsClinetAuthHandler" />

</beans>


三,具体调用服务端代码:

Java代码


import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import test.saa.client.WebServiceSample;

public final class Client {

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

//加载配置

ApplicationContext context = new ClassPathXmlApplicationContext(

new String[] { "test/saa/client-beans.xml" });

//获取ws实例

WebServiceSample client = (WebServiceSample) context.getBean("client");

String response = client.say("2012");

System.out.println("Response: " + response);

}

}


到此客户端第二种实现方式结束
GOOD LUCKY!!!
如有不明,请指教!!!

1

0


分享到:




解决(CXF):The security token could not ... | CXF 入门:创建一个基于SOAPHeader的安全验 ...

2012-01-15 00:37

浏览 4453

评论(6)

收藏

分类:企业架构

相关推荐

参考知识库


Git知识库286 关注 | 304 收录

jQuery知识库445 关注 | 232 收录

大型网站架构知识库1233 关注 | 532 收录

AngularJS知识库594 关注 | 262 收录

评论

6 楼 wentao_tang 2013-12-02 引用
客户端调用,这行代码是必须的吗,有什么作用吗?
outProps.put(WSHandlerConstants.USER, "admin");

5 楼 xiangximingong 2013-09-18 引用
我想问下,就是客户端传入错误的密码,也会抛一个异常,但这个异常我不知道在哪捕获,也不知道怎么通知客户端密码错误!能否指教下……

4 楼 zhenglongfei 2013-07-25 引用
想不通,为什么要在服务端要在set一下呢??

3 楼 Sev7en_jun 2012-12-06 引用

ice.kane 写道

zl759869747 写道


我想问下,为什么在服务端的回调函数里面当还要setPassword呢?我很郁闷的是,我在客户端明明已经设定了密码,可是服务端却拿不到,而且如果有服务端不设定密码就抛异常,我想问下到底是怎么样校验的呢?

我也是遇到这个问题。。

在服务端setPassword,那密码校验还有什么意义

1,为什么不设置密码会抛异常:
--既然要把服务加密,就要设置密码,要不然怎么加密,怎么知道哪些客户端是有权限访问的,就像买了一把锁,连自己(服务端)都不知道钥匙是怎么,那别人(客户端)怎么开这把锁
2,服务端设置密码是为了给自己上一把锁,如果要访问它必须有开这把锁的钥匙,即密码,有钥匙还要保证这个钥匙可以开这把锁,即客户端拿的密码是否和服务端锁的密码一致

2 楼 ice.kane 2012-11-27 引用

zl759869747 写道


我想问下,为什么在服务端的回调函数里面当还要setPassword呢?我很郁闷的是,我在客户端明明已经设定了密码,可是服务端却拿不到,而且如果有服务端不设定密码就抛异常,我想问下到底是怎么样校验的呢?

我也是遇到这个问题。。

在服务端setPassword,那密码校验还有什么意义

1 楼 zl759869747 2012-10-23 引用


我想问下,为什么在服务端的回调函数里面当还要setPassword呢?我很郁闷的是,我在客户端明明已经设定了密码,可是服务端却拿不到,而且如果有服务端不设定密码就抛异常,我想问下到底是怎么样校验的呢?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: