CXF的拦截器和以前学过的servlet的拦截器类似的,都是在开始或结束切入一段代码,执行一些逻辑之类的。我们可以在调用ws服务前设置拦截器,也可以在调用ws服务后设置拦截器,当然了,拦截器也可以添加多个,CXF中有自己内置的拦截器,先来写个简单CXF自带的拦截器实例熟悉一下在CXF中如何添加,然后再来自定义CXF拦截器。
1. CXF内置的拦截器设置
还是使用上一节的ws,在原来的基础上添加以下拦截器,如下:
启动之后,客户端访问一下,看服务端控制台的输出(为了清楚点,我就不缩小了):
可以看到,请求的时候会被拦截器拦截,请求结束也会被拦截,从打印的日志消息可以看出,发送的是soap消息,返回的数据由于截屏的范围我就不截取了,这是在服务端添加的拦截器。
那客户端如何添加拦截器呢?由于client端无法直接获取拦截器组,所以我们需要首先获取一个client的代理,然后通过这个代理来获取拦截器组,如下:
客户端访问一下,看下客户端控制台的输出结果(为了清楚点,我就不缩小了):
可以看出,客户端如果设置拦截器的话,也会打印出日志消息,而且客户端和服务端的拦截器执行顺序刚好相反。这就是CXF内置的拦截器,下面我们来自定义CXF的拦截器。
2. 自定义CXF拦截器
自定义拦截器的话,我们来弄个需求,使用拦截器进行权限的认证。自定义拦截器需要继承AbstractPhaseInterceptor<SoapMessage>
,其中SoapMessage是用来封装soap消息的,我们具体来看下如何自定义CXF拦截器,首先看服务端,在上面的代码的定义的两个内置拦截器下面添加一个自定义拦截器即可:
<span style="color:#000000"><code class="language-java">factoryBean.getInInterceptors().add(<span style="color:#000088">new</span> MyInterceptor());</code></span>
然后重点是这个MyInterceptor,如下:
<span style="color:#000000"><code class="language-java"><span style="color:#000088">public</span> <span style="color:#000088">class</span> <span style="color:#4f4f4f">MyInterceptor</span> <span style="color:#000088">extends</span> <span style="color:#4f4f4f">AbstractPhaseInterceptor</span><<span style="color:#4f4f4f">SoapMessage</span>> {
<span style="color:#000088">public</span> <span style="color:#009900">MyInterceptor</span>() {
<span style="color:#000088">super</span>(Phase.PRE_INVOKE); <span style="color:#880000">//在调用方法之前调用自定义拦截器</span>
}
<span style="color:#000088">public</span> <span style="color:#000088">void</span> <span style="color:#009900">handleMessage</span>(SoapMessage message) <span style="color:#000088">throws</span> Fault {
List<Header> headers = message.getHeaders(); <span style="color:#880000">//根据soap消息获取头部</span>
<span style="color:#000088">if</span>(headers == <span style="color:#000088">null</span> && headers.size() == <span style="color:#006666">0</span>) {
<span style="color:#000088">throw</span> <span style="color:#000088">new</span> Fault(<span style="color:#000088">new</span> IllegalArgumentException(<span style="color:#009900">"没有Header,拦截器实施拦截"</span>));
}
Header firstHeader = headers.get(<span style="color:#006666">0</span>); <span style="color:#880000">//我们等会只传一个头部过来</span>
Element elm = (Element) firstHeader.getObject();<span style="color:#880000">//将该头部转成一个Element对象</span>
NodeList userList = elm.getElementsByTagName(<span style="color:#009900">"username"</span>); <span style="color:#880000">//根据标签获取值</span>
NodeList pwdList = elm.getElementsByTagName(<span style="color:#009900">"password"</span>);
<span style="color:#880000">// 进行身份认证</span>
<span style="color:#000088">if</span>(userList.getLength() != <span style="color:#006666">1</span>) {<span style="color:#880000">//只有一个用户</span>
<span style="color:#000088">throw</span> <span style="color:#000088">new</span> Fault(<span style="color:#000088">new</span> IllegalArgumentException(<span style="color:#009900">"用户名格式不对"</span>));
}
<span style="color:#000088">if</span>(pwdList.getLength() != <span style="color:#006666">1</span>) {<span style="color:#880000">//只有一个密码</span>
<span style="color:#000088">throw</span> <span style="color:#000088">new</span> Fault(<span style="color:#000088">new</span> IllegalArgumentException(<span style="color:#009900">"密码格式不对"</span>));
}
String username = userList.item(<span style="color:#006666">0</span>).getTextContent(); <span style="color:#880000">//因为就一个,所以获取第一个即可</span>
String password= pwdList.item(<span style="color:#006666">0</span>).getTextContent();
<span style="color:#000088">if</span>(!username.equals(<span style="color:#009900">"admin"</span>) || !password.equals(<span style="color:#009900">"123"</span>)) {
<span style="color:#000088">throw</span> <span style="color:#000088">new</span> Fault(<span style="color:#000088">new</span> IllegalArgumentException(<span style="color:#009900">"用户名或者密码错误"</span>));
}
}
}</code></span>
上面的代码逻辑很简单,等会儿客户端会传过来一个soap消息,我们会将用户名和密码封装到头部中传过来,那么在这边,通过解析soap消息中头部的数据,来进行身份认证。所以接下来完成客户端那边的拦截器。
客户端这边要自定义一个out拦截器了,因为这边是发送数据,同上,首先在原来客户端定义的两个内置CXF拦截器上面添加一个自定义拦截器,如下:
<span style="color:#000000"><code class="language-java">client.getOutInterceptors().add(<span style="color:#000088">new</span> AddHeaderInterceptor(<span style="color:#009900">"admin"</span>, <span style="color:#009900">"123"</span>));<span style="color:#880000">//添加自定义拦截器</span></code></span>
- 1
在自定义拦截器中,将用户名和密码传进去,重点来看一下这个自定义拦截器,如下:
<span style="color:#000000"><code class="language-java"><span style="color:#000088">public</span> <span style="color:#000088">class</span> <span style="color:#4f4f4f">AddHeaderInterceptor</span> <span style="color:#000088">extends</span> <span style="color:#4f4f4f">AbstractPhaseInterceptor</span><<span style="color:#4f4f4f">SoapMessage</span>> {
<span style="color:#000088">private</span> String username;
<span style="color:#000088">private</span> String password;
<span style="color:#000088">public</span> <span style="color:#009900">AddHeaderInterceptor</span>(String username, String password) {
<span style="color:#000088">super</span>(Phase.PREPARE_SEND); <span style="color:#880000">//准备发送soap消息的时候调用拦截器</span>
<span style="color:#000088">this</span>.username = username;
<span style="color:#000088">this</span>.password = password;
}
<span style="color:#000088">public</span> <span style="color:#000088">void</span> <span style="color:#009900">handleMessage</span>(SoapMessage message) <span style="color:#000088">throws</span> Fault {
List<Header> headerList = message.getHeaders();
Document doc = DOMUtils.createDocument();
<span style="color:#880000">// 定义三个对象</span>
Element elm = doc.createElement(<span style="color:#009900">"authHeader"</span>);
Element userElm = doc.createElement(<span style="color:#009900">"username"</span>);
Element pwdElm = doc.createElement(<span style="color:#009900">"password"</span>);
<span style="color:#880000">// 给用户名和密码对象赋值</span>
userElm.setTextContent(username);
pwdElm.setTextContent(password);
<span style="color:#880000">// 将用户名和密码的对象添加到elm中</span>
elm.appendChild(userElm);
elm.appendChild(pwdElm);
headerList.add(<span style="color:#000088">new</span> Header(<span style="color:#000088">new</span> QName(<span style="color:#009900">"head"</span>), elm));<span style="color:#880000">//往soap消息头部中添加这个elm元素</span>
}
}</code></span>
从上面的代码中可以看出,首先通过构造函数将用户名和密码传进去,然后获取将要发送的soap消息的头部,紧接着认为构造出几个元素,将用户名和密码封装到元素中去,并放到soap消息的头部,这样等会soap消息就会携带这个用户名和密码的消息了,这样就能在上面的服务端取出,进行身份认证了,这样就前后连通了起来。测试结果我就不贴了,可以查看控制台打印的结果,重点看一下soap消息,里面封装好了一个DOM对象,封装了用户名和密码。