CXF-使用interceptor处理Soap Headers

最近需要对外提供 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 可以自动帮我们完成。得到所需数据后,就可以进行校验了:
@Override
public 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值