CXF发布webService(二)

CXF框架中可以自定义拦截器(Interceptor),从而可以做到不改变核心模块的情况下,动态改变请求和响应的一些消息处理。本文介绍如何利用自定义拦截器实现权限验证。

CXF中的拦截器分为in拦截器和out拦截器,又有客户端拦截器和服务端拦截器。拦截器使用流程:客户端(out)-> 服务端(in)->处理业务->服务端(out)->客户端(in)

并不是每一步都需要拦截器。在这里我们用到的是客户端Out拦截器和服务端in拦截器。服务端in拦截器检查用户级权限,客户端out浏览器发送用户信息给服务端。

一、对jetty中发布的服务进行拦截

1、上文中介绍了如何在jetty中发布webservice服务,主要有两种方法(JaxWsServerFactoryBean 或 Endpoint),这两种方法都可以设置拦截器。

1)endpoint方式:

1
2
3
4
5
HelloWorldService service = new HelloWorldServiceImpl();  
String address = "http://localhost:8080/hello";  
  
EndpointImpl endpoint = (EndpointImpl)Endpoint.publish(address, service);  
endpoint.getInInterceptors().add(new AuthInterceptor());

2)JaxWsServerFactoryBean 方式:

1
2
3
4
5
6
7
HelloWorldServiceImpl impl = new HelloWorldServiceImpl();  
JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();  
factory.setAddress("http://localhost:8080/hello");  
factory.setServiceClass(HelloWorldService.class);  
factory.setServiceBean(impl);  
factory.getInInterceptors().add(new AuthInterceptor());  
factory.create();

2、服务器段自定义拦截器:
自定义拦截去需要实现PhaseInterceptor接口,不过一般都是继承自AbstractPhaseInterceptor,下面我们来实现AuthInterceptor的权限控制功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package com.tiamaes.webservice.auth;  
  
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;  
  
/**   
 * <p>类描述:用户权限验证拦截器  </p> 
 * @version   
 */  

public class AuthInterceptor extends AbstractPhaseInterceptor<SoapMessage{  
  
    //在调用之前拦截  
    public AuthInterceptor() {  
        super(Phase.PRE_INVOKE);  
    }  
  
    /** 
     * 自定义拦截器需要实现handleMessage方法,该方法抛出Fault异常,可以自定义异常集成自Fault, 
     * 也可以new Fault(new Throwable()) 
     */  

    public void handleMessage(SoapMessage soap) throws Fault {  
        System.out.println("开始验证用户信息");  
        List<Header> headers = soap.getHeaders();  
          
        //检查headers是否存在  
        if(headers == null | headers.size()<1){  
            throw new Fault(new IllegalArgumentException("找不到Header,无法验证用户信息"));  
        }  
          
        Header header = headers.get(0);  
          
        Element el = (Element)header.getObject();  
        NodeList users = el.getElementsByTagName("username");  
        NodeList passwords = el.getElementsByTagName("password");  
          
        //检查是否有用户名和密码元素  
        if(users.getLength()<1){  
            throw new Fault(new IllegalArgumentException("找不到用户信息"));  
        }  
        String username = users.item(0).getTextContent().trim();  
          
        if(passwords.getLength()<1){  
            throw new Fault(new IllegalArgumentException("找不到密码信息"));  
        }  
        String password = passwords.item(0).getTextContent();  
          
        //检查用户名和密码是否正确  
        if(!"admin".equals(username) || !"admin".equals(password)){  
            throw new Fault(new IllegalArgumentException("用户名或密码不正确"));  
        }else{  
            System.out.println("用户名密码正确允许访问");  
        }  
    }  

}

3、客户端需要添加out拦截器,在out拦截器中加入消息头:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package com.tiamaes.webservice.auth;  
  
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;  
  
/**   
 * 客户端拦截器 
 * @version   
 */  
public class ClientLoginInterceptor extends AbstractPhaseInterceptor<SoapMessage> {  
  
    private String username;  
    private String password;  
    public void setUsername(String username) {  
        this.username = username;  
    }  
    public void setPassword(String password) {  
        this.password = password;  
    }  
    /** 
     * 创建一个新的实例 ClientLoginInterceptor. 
     * 
     * @param username 
     * @param password 
     */  
    public ClientLoginInterceptor(String username, String password) {  
        super(Phase.PREPARE_SEND);  
        this.username = username;  
        this.password = password;  
    }  
    /* (non-Javadoc) 
     * @see org.apache.cxf.interceptor.Interceptor#handleMessage(org.apache.cxf.message.Message) 
     */  
    public void handleMessage(SoapMessage soap) throws Fault {  
        // TODO Auto-generated method stub  
        List<Header> headers = soap.getHeaders();  
          
        Document doc = DOMUtils.createDocument();  
          
        Element auth = doc.createElement("authrity");  
        Element username = doc.createElement("username");  
        Element password = doc.createElement("password");  
          
        username.setTextContent(this.username);  
        password.setTextContent(this.password);  
          
        auth.appendChild(username);  
        auth.appendChild(password);  
        //doc.appendChild(auth);  
          
        headers.add(0, new Header(new QName("tiamaes"),auth));  
    }  
  
}

4、客户端调用webService服务时,添加out拦截器:

1
2
3
4
5
6
7
8
JaxWsProxyFactoryBean svr = new JaxWsProxyFactoryBean();

svr.setServiceClass(IHelloService.class);
svr.setAddress("http://localhost:9000/helloWorld");
svr.getOutInterceptors().add(new ClientLoginInterceptor("admin","admin"));//设置out拦截器

IHelloService hw = (IHelloService) svr.create();
System.out.println(hw.sayHi("测试1111144444411111。。。。。。。。。"));

二、对web容器中发布的webservice设置拦截器

1、服务端自定义拦截器:
同上

2、在application.xml中配置服务端的In拦截器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 相当于使用endpoint进行服务发布   -->
<jaxws:endpoint id="helloWorld"
implementor="cn.edu.nuc.springMaven.services.impl.HelloService"
address="/helloWorld" >

<!-- 配置IN拦截器 -->
<jaxws:inInterceptors>
<bean class="org.apache.cxf.interceptor.LoggingInInterceptor"></bean>
<bean class="cn.edu.nuc.springMaven.interceptors.AuthInterceptor"></bean>
</jaxws:inInterceptors>

<!-- 配置OUT拦截器
<jaxws:outInterceptors>
<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"></bean>
</jaxws:outInterceptors> -->

</jaxws:endpoint>

然后启动tomcat。。。

3、客户端拦截器:
同上

4、客户端调用webService服务时,添加out拦截器:
1)普通客户端调用

1
2
3
4
5
6
7
8
JaxWsProxyFactoryBean svr = new JaxWsProxyFactoryBean();

svr.setServiceClass(IHelloService.class);
svr.setAddress("http://localhost:9000/helloWorld");
svr.getOutInterceptors().add(new ClientLoginInterceptor("admin","admin"));//设置out拦截器

IHelloService hw = (IHelloService) svr.create();
System.out.println(hw.sayHi("刘晓1111144444411111。。。。。。。。。"));

2)使用spring的依赖注入,通过配置调用webService服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<jaxws:client id="sayHello2"
serviceClass="cn.edu.nuc.springMaven.services.IHelloService"
address="http://localhost:8080/SpringMaven/webservice/helloWorld">


<!-- <jaxws:inInterceptors>
<bean class="org.apache.cxf.interceptor.LoggingInInterceptor"/>
<bean class="com.benben.Interceptor.SampleInterceptor"/>
</jaxws:inInterceptors> -->


<jaxws:outInterceptors>
<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"/>
<!--ClientLoginInterceptor没有默认构造函数,所以这里需要constructor-arg把username和password注入到ClientLoginInterceptor-->
<bean class="cn.edu.nuc.springMaven.interceptors.ClientLoginInterceptor">
<constructor-arg name="username" value="admin"></constructor-arg>
<constructor-arg name="password" value="admin"></constructor-arg>
</bean>
</jaxws:outInterceptors>
</jaxws:client>

代码:

1
2
3
4
5
6
7
8
9
10
11
public class Client {


public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");

IHelloService clietn = (IHelloService)ctx.getBean("sayHello2");

System.out.println(clietn.sayHi("liuxiao"));
}
}