Cxf - 拦截器

本文介绍了如何在Cxf中使用拦截器处理Web Services通信。内容包括 Soap消息的解析,服务端和客户端拦截器的添加。在服务端,通过In拦截器验证用户信息,而在客户端则使用Out拦截器将用户信息插入到Soap消息中。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Soap消息

        Web Services通信的内容。如下

<!-- 根元素: Envelope  -->
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <!-- Header:不强制出现的元素,可用于携带一些wsdl定义之外的信息,如用户名信息等  -->
	<soap:Header>
		<userInfo>
			<userName>CxfName</userName>
			<userPass>CxfPass</userPass>
		</userInfo>
	</soap:Header>
	<!-- Body:Soap的消息体,在正确调用的情况下符合WSDL文档定义的格式,调用错误则为soap:Fault子元素  -->
	<soap:Body>
		<ns2:sayHello xmlns:ns2="http://ws.cxf.xilen.com/">
			<arg0>Admin</arg0>
		</ns2:sayHello>
	</soap:Body>
</soap:Envelope>

二、添加拦截器

    1、服务端

import java.util.List;

import javax.xml.ws.Endpoint;

import org.apache.cxf.interceptor.Interceptor;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.message.Message;

import com.xilen.cxf.ws.UserInfoWs;
import com.xilen.cxf.ws.impl.UserInfoWsImpl;

public class ServerMain {
	public static void main(String[] args) {

		UserInfoWs ui = new UserInfoWsImpl();

		// 得到javax.xml.ws.Endpoint.publish()发布方法的org.apache.cxf.jaxws.EndpointImpl类型返回值
		EndpointImpl ep = (EndpointImpl) Endpoint.publish("http://192.168.1.100/cxf", ui);

		// 通过这个返回值的getInInterceptors()和getOutInterceptors()方法得到现有的对应拦截器集合
		List<Interceptor<? extends Message>> interceptorIn = ep.getInInterceptors();
		List<Interceptor<? extends Message>> interceptorOut = ep.getOutInterceptors();

		// 通过这个集合的添加方法添加拦截器   LoggingInterceptor为Cxf自带拦截器
		interceptorIn.add(new LoggingInInterceptor());
		interceptorOut.add(new LoggingOutInterceptor());

	}
}
    2、客户端

        客户端此时使用了Cxf相关的对象,需要添加Cxf的Jar

public class ClientMain {
	
	public static void main(String[] args) {
	
		//在生成的Java类中继承javax.xml.ws.Service的类可作为工厂来使用
		UserInfoWsImpl factory = new UserInfoWsImpl();
		
		//通过工厂类实例来获取远程Web Service对象的代理
		UserInfoWs uiws = factory.getUserInfoWsImplPort();
		
		//以远程代理对象为参数调用ClientProxy的getClient()方法获取Client对象
		Client client = ClientProxy.getClient(uiws);
		
		//获取Client对象的输入输出拦截器集合并添加新的拦截器
		client.getInInterceptors().add(new LoggingInInterceptor());
		client.getOutInterceptors().add(new LoggingOutInterceptor());
		
		//调用代理对象的方法实现远程调用
		System.out.println(uiws.sayHello("Admin"));
		
	}
}


三、自定义拦截器

    1、说明

        以调用Web Services时需要验证用户为例说明。服务器端需要添加In拦截器验证是否有用户信息并符合要求,而客户端则需要添加Out拦截器把用户信息加入到Soap消息中。

    2、服务器端

        (1)定义服务器端拦截器

import java.util.List;

import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

/**
 * 自定义拦截器继承Interceptor接口,可通过继承AbstractPhaseInterceptor<Message>来实现(Message使用SoapMessage的实现)
 * 通过PhaseInterceptor拦截器可以指定这个拦截器在服务的哪个阶段起作用,具体阶段在实现类的构造器中传入
 */
public class ServerAuthInInterceptor extends AbstractPhaseInterceptor<SoapMessage>{
	
	/**
	 * AbstractPhaseInterceptor没有无参构造器,需要在子类中定义构造器并显示的调用其有参数的构造器
	 */
	public ServerAuthInInterceptor(){
		/*
		 * 显示调用父类的构造器。参数通过Phase类的常量指定,即这个拦截器起作用的阶段
		 * Phase.PRE_INVOKE: 调用之前起作用 
		 */
		super(Phase.PRE_INVOKE);
	}
	
	/**
	 * 重写AbstractPhaseInterceptor的handleMessage()方法 定义拦截业务的逻辑
	 * 参数message就是被拦截到的soap消息
	 * 对符合逻辑的请求Cxf不用多余的操作,只需要在不符合业务逻辑的请求Throw Fault
	 */
	public void handleMessage(SoapMessage message) throws Fault {
		/*
		 * soap body是固定的, 其他信息只能放在Header中
		 * 通过SoapMessage对象的getHeader()方法得到Header的集合
		 */
		List<Header> headers = message.getHeaders();
		
		if (headers.isEmpty()) {
			throw new Fault(new IllegalArgumentException("没有携带额外的信息!"));
		}
		
		// 假设要求第一个Header就携带用户信息
		Header header = headers.get(0);
		// 得到的是DOM的元素
		Element element = (Element) header.getObject();
		
		// DOM解析
		NodeList nodeUserName = element.getElementsByTagName("userName");
		NodeList nodeUserPass = element.getElementsByTagName("userPass");
		if(nodeUserName.getLength() != 1 || nodeUserPass.getLength() != 1){
			throw new Fault(new IllegalArgumentException("没有携带用户信息或者不符合要求!"));
		}
		
		// 得到具体的用户名和密码并验证
		String userName = nodeUserName.item(0).getTextContent();
		String userPass = nodeUserPass.item(0).getTextContent();
		if(!userName.equals("CxfName") || !userPass.equals("CxfPass")){
			throw new Fault(new IllegalArgumentException("用户名或者密码不正确!"));
		}
		
		// 如果验证一路通过,则请求自动继续往下
		
	}
}

        (2)添加服务器端拦截器

public class ServerMain {
	public static void main(String[] args) {

		UserInfoWs ui = new UserInfoWsImpl();

		// 得到javax.xml.ws.Endpoint.publish()发布方法的org.apache.cxf.jaxws.EndpointImpl类型返回值
		EndpointImpl ep = (EndpointImpl) Endpoint.publish("http://192.168.1.100/cxf", ui);

		// 通过这个返回值的getInInterceptors()和getOutInterceptors()方法得到现有的对应拦截器集合
		List<Interceptor<? extends Message>> interceptorIn = ep.getInInterceptors();
		List<Interceptor<? extends Message>> interceptorOut = ep.getOutInterceptors();

		// 通过这个集合的添加方法添加拦截器
		interceptorIn.add(new LoggingInInterceptor());
		interceptorOut.add(new LoggingOutInterceptor());
		
		//添加自定义拦截器
		interceptorIn.add(new ServerAuthInInterceptor());

	}
}

    3、客户端

        (1)定义客户端拦截器

import java.util.List;

import javax.xml.namespace.QName;

import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class ClientAuthOutInterceptor extends AbstractPhaseInterceptor<SoapMessage> {

	private String name;
	private String pwd;

	/**
	 * 除了调用父类构造方法之外,也把用户信息一并添加
	 */
	public ClientAuthOutInterceptor(String name, String pwd) {
		// 在消息发送之前调用 此时Soap消息已经生成
		super(Phase.PREPARE_SEND);  
		this.name = name;
		this.pwd = pwd;
	}

	@Override
	public void handleMessage(SoapMessage message) throws Fault {
		List<Header> headers = message.getHeaders();
		
		// DOM编程
		Document doc = DOMUtils.createDocument();
		Element element = doc.createElement("userInfo");
		
		// 封装用户信息
		Element nameEle = doc.createElement("userName");
		nameEle.setTextContent(name);
		Element passEle = doc.createElement("userPass");
		passEle.setTextContent(pwd);
		
		element.appendChild(nameEle);
		element.appendChild(passEle);
		
		// 把element元素封装成Header并添加到Headers中  QName即命名空间
		Header header = new Header(new QName("xilen"), element);
		headers.add(header);
		
	}
}
        (2)添加客户端拦截器

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;

import com.xilen.cxf.ws.UserInfoWs;
import com.xilen.cxf.ws.impl.UserInfoWsImpl;
import com.xilen.inter.ClientAuthOutInterceptor;

public class ClientMain {
	
	public static void main(String[] args) {
	
		//在生成的Java类中继承javax.xml.ws.Service的类可作为工厂来使用
		UserInfoWsImpl factory = new UserInfoWsImpl();
		
		//通过工厂类实例来获取远程Web Service对象的代理
		UserInfoWs uiws = factory.getUserInfoWsImplPort();
		
		//以远程代理对象为参数调用ClientProxy的getClient()方法获取Client对象
		Client client = ClientProxy.getClient(uiws);
		
		//获取Client对象的输入输出拦截器集合并添加新的拦截器
		client.getInInterceptors().add(new LoggingInInterceptor());
		client.getOutInterceptors().add(new LoggingOutInterceptor());
		
		//自定义用户认证拦截器,存入用户名密码信息
		client.getOutInterceptors().add(new ClientAuthOutInterceptor("CxfName","CxfPass"));
		
		//调用代理对象的方法实现远程调用
		System.out.println(uiws.sayHello("Admin"));
		
	}
}
    4、调用结果
        (1)在用户信息输入正确的情况下,服务端顺利执行了相应的方法并返回

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
	<soap:Body>
		<ns2:sayHelloResponse xmlns:ns2="http://ws.cxf.xilen.com/">
			<return>Admin, Hello!</return>
		</ns2:sayHelloResponse>
	</soap:Body>
</soap:Envelope>
        (2)在用户信息输入错误的情况下,服务器端只返回了错误调用的信息

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
	<soap:Body>
		<soap:Fault>
			<faultcode>soap:Server</faultcode>
			<faultstring>用户名或者密码不正确!</faultstring>
		</soap:Fault>
	</soap:Body>
</soap:Envelope>


四、资源下载  
    客户端和服务端的工程源码已打包上传优快云,地址:http://download.youkuaiyun.com/detail/u013379717/7216357

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值