CXF-使用interceptor处理Soap Headers

来源:互联网 发布:网络配线架说明书 编辑:程序博客网 时间:2024/06/05 15:28

最近需要对外提供 web service 服务,初步调研后决定采用 CXF 进行开发。CXF 是 apache 提供的开源框架,对 JAX-WS 标准的实现较为完整,并提供了与 spring 框架的集成,功能较为丰富。由于涉及到在 SOAP 头中传输身份验证信息,本文主要讲解如何通过 CXF 提供的 interceptor 来处理 SOAP 头中的数据。实际上, JAX-WS 规范的 Handler 也可以进行类似消息的处理,由于是标准中的定义,因此具有较强的通用性。但是在大部分情况下开发者其实并不太关注程序的可移植性,而且 CXF 的 interceptor 提供了更为丰富的功能,因此使用上较为广泛。相关 jar 包可以在这里下载,里面包含了 CXF 及其所依赖的各 jar 包。

服务端


下面是一个简单的 SEI(Service Endpoint Inteface),已经使用 JAX-WS 定义的标注将其暴露为一个 web service 接口:
@WebService(targetNamespace = "http://services.devsumo.com/cxfMessenger/v001")@XmlSeeAlso(AuthHeader.class)public interface CXFMessenger {    @WebMethod    String sendMessage(         @WebParam(name="recipient") String recipient,         @WebParam(name="messageContent") String messageContent);}

这里每一个请求都需要传入用户的用户名、密码进行校验,我们新建一个 AuthHeader 类:
@XmlAccessorType(XmlAccessType.FIELD)@XmlType(name = "AuthHeader")public class AuthHeader {    protected String username;    protected String password;    public String getusername() {        return username;    }    public void setusername(String value) {        this.username = value;    }    public String getpassword() {        return password;    }    public void setpassword(String value) {        this.password = value;    }}

CXF 自己已经提供了许多功能强大的 Interceptor 以便开发者使用,不过这里我们使用 AbstracSoapInterceptor 类作为我们的基础类。AbstractSoapInterceptor 是其他 Interceptor 类的基类,为我们提供了基本的代码模板:
public class AuthInterceptor extends AbstractSoapInterceptor {     public AuthInterceptor() {        super(Phase.PRE_INVOKE);    }     @Override    public void handleMessage(SoapMessage message) throws Fault {        // TODO: Implement our header handling    }}

这里我们有一个简单的 handlerMessage 函数需要实现。但在此之前,我们首先来关注填一下 Phase。在 CXF 中,各个 Interceptor 组成了 Interceptor Chains,CXF 将它们划分为几个阶段,用于精细化的控制各 interceptor 的执行时间。一个 interceptor 可以在请求刚刚被接收时执行,也就是说早于任何其他的解析或者编解码,这种情况比较适合端对端的性能监控。也可以让它在服务方法即将被调用时执行。后一种情况就是我们要使用的,在这里将 Phase 定义为 Phase.PRE_INVOKE。
handleMessage 的参数 SoapMessage 类有一个十分便捷的方法: getHeader ,其参数是一个 QName 对象,如果存在匹配此 QName 对象的 Header 的话则将其返回:
public class AuthInterceptor extends AbstractSoapInterceptor {     private static final QName HEADER_TYPE =        new QName("http://services.devsumo.com/cxfMessenger/v005",                  "AuthHeader");     public AuthInterceptor() {        super(Phase.PRE_INVOKE);    }     @Override    public void handleMessage(SoapMessage message) throws Fault {        Header header = message.getHeader(HEADER_TYPE);        if(header != null) {          // TODO: process our header        }    }}

通过 Header 得到我们需要的数据不需要我们自己进行编解码,通过相关的 DataBinding,CXF 可以自动帮我们完成。得到所需数据后,就可以进行校验了:
@Overridepublic void handleMessage(SoapMessage message) throws Fault {    Header header = message.getHeader(HEADER_TYPE);     if (header == null)          throws new Exception("无身份信息");     Service service =          ServiceModelUtil.getService(message.getExchange());     DataReader<Node> dataReader =          service.getDataBinding().createReader(Node.class);     AuthHeader AuthHeader =          (AuthHeader)dataReader.read(HEADER_TYPE,               (Node)header.getObject(), AuthHeader.class);     if (!AuthHeader.getUsername.equals("hello")) {          throws new Exception("用户无权限");     }     if (!AuthHeader.getPassword.equals("world")) {          throws new Exception("密码错误");     }}


最后我们需要在 CXF 中对 interceptor 进行注册。Bus 为 CXF 的骨架类,提供了 CXF 运行时的资源共享,下面的配置使得此 Interceptor 对所有的服务接口生效:
<cxf:bus>    <cxf:inInterceptors>        <bean id="authInterceptor"              class="com.devsumo.cxfmessenger.v005.AuthInterceptor"/>    </cxf:inInterceptors></cxf:bus>

客户端


客户端可以使用如下代码将在请求信息中添加 Soap Header:
ApplicationContext cxt = new ClassPathXmlApplicationContext("classpath:applicationContext-client.xml");         CustomerService client = (CustomerService)cxt.getBean("customerServiceClient");AuthHeader authHeader = new AuthHeader();authHeader.setUsername("hello");authHeader.setPassword("world");Header header = new Header(new QName("http://services.devsumo.com/cxfMessenger/v001", "AuthHeader"),                                             authHeader, new JAXBDataBinding(AuthHeader.class));List<Header> headers = new ArrayList<Header>();headers.add(header);((BindingProvider)client).getRequestContext().put(Header.HEADER_LIST, headers);

其中 Spring 配置文件内容如下。注意 serviceClass 属性的值为 SEI 接口的全限定名,address 属性的值为上述 WS 服务对外开放的 url。
<!-- 在客户端中,此接口类所在package可按需要放置 --><bean id="messenger" class="api.client.CXFMessenger"                   factory-bean="messengerClientFactory" factory-method="create"/>   <bean id="messengerClientFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">     <property name="serviceClass" value="api.client.CXFMessenger"/>     <property name="address" value="http://services.devsumo.com/cxfMessenger/v001/messenger"/></bean>


0 0
原创粉丝点击