2017年1月22日 星期日

CXF 攔截器 ( WebService 三)

JDK沒有攔截器的功能,但CXF有



※內鍵攔截器

以logging為例,如下:


※伺服器端

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;
    
String url = "http://localhost:8888/aaa/bbb/ccc";
EndpointImpl publish = (EndpointImpl) Endpoint.publish(url, new HelloWS());
    
System.out.println("增加server in log");
List<Interceptor<? extends Message>> in = publish.getInInterceptors();
in.add(new LoggingInInterceptor());
    
System.out.println("增加server out log");
List<Interceptor<? extends Message>> out = publish.getOutInterceptors();
out.add(new LoggingOutInterceptor());
    
System.out.println("server is start!");

※注意import的包,CXF有很多同名的class,但package不同

※在網址打上xxx.wsdl就會執行伺服器端in的部分,在console會顯示

※out的部分可用第一篇的JavaClientCallWebService.java


※用戶端

import java.util.List;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.interceptor.Interceptor;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.message.Message;
    
HelloWSService hws = new HelloWSService();
IHelloWS hw = hws.getHelloWSPort();
    
Client client = ClientProxy.getClient(hw);
System.out.println("增加client out log");
List<Interceptor<? extends Message>> out = client.getOutInterceptors();
out.add(new LoggingOutInterceptor());
    
System.out.println("增加client in log");
List<Interceptor<? extends Message>> in = client.getInInterceptors();
in.add(new LoggingInInterceptor());
    
System.out.println(hw.add(1, 2));

※使用wsimport或wsdl2java產生程式碼後,使用如上的程式碼,這前幾篇也有做過,只是多個中間攔截器的部分

※首先先向server端丟出訊息,因為現在寫的是用戶端,所以對用戶端來說是out,然後伺服器端的in會接收,在來是伺服器端的out,最後再丟回給用戶端的out,攔截器完成後,再印出程式碼



※自定攔截器


※用戶端

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Body>
        <ns2:add xmlns:ns2="http://test.hello/">
            <arg0>1</arg0>
            <arg1>2</arg1>
        </ns2:add>
    </soap:Body>
</soap:Envelope>

※內鍵攔截器的伺服器端的console有Inbound Message,裡面的Payload就是長這個樣子


※AddLoginInfo.java 

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 AddLoginInfo extends AbstractPhaseInterceptor<SoapMessage> {
    private String account;
    private String password;
    
    public AddLoginInfo(String account, String password) {
        super(Phase.PRE_PROTOCOL); // 表示在轉換成SOAP之前
        this.account = account;
        this.password = password;
    }
    
    @Override
    public void handleMessage(SoapMessage msg) throws Fault {
        /**
         * <login> <acc></acc> <pass></pass> </login>
         */
        Document doc = DOMUtils.createDocument();
        Element loginEle = doc.createElement("login");
    
        Element accEle = doc.createElement("acc");
        accEle.setTextContent(account);
    
        Element passEle = doc.createElement("pass");
        passEle.setTextContent(password);
    
        loginEle.appendChild(accEle);
        loginEle.appendChild(passEle);
    
        List<Header> headers = msg.getHeaders();
        headers.add(new Header(new QName("login"), loginEle));
    
        System.out.println("客戶端的 handleMessage執行成功!");
    }
}

※繼承 AbstractPhaseInterceptor 並增加 SOAP 的 head

※QName裡的字串必需與createElement裡的字串一樣,表示要攔截login標籤,否則攔截不到


※測試

HelloWSService hws = new HelloWSService();
IHelloWS hw = hws.getHelloWSPort();
    
Client client = ClientProxy.getClient(hw);
System.out.println("增加client out log");
List<Interceptor<? extends Message>> out = client.getOutInterceptors();
out.add(new AddLoginInfo("bruce", "12345"));
    
System.out.println(hw.add(1, 2));

※將out的攔截器指向新增的AddLoginInfo


※伺服器端的console

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Header>
        <login>
            <acc>bruce</acc>
            <pass>12345</pass>
        </login>
    </soap:Header>
    <soap:Body>
        <ns2:add xmlns:ns2="http://test.hello/">
            <arg0>1</arg0>
            <arg1>2</arg1>
        </ns2:add>
    </soap:Body>
</soap:Envelope>

※一樣是 Inbound Message 的 Payload,此時會印出新增的login


※伺服器端

import javax.xml.namespace.QName;
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;
    
public class CheckLoginInfo extends AbstractPhaseInterceptor<SoapMessage> {
    public CheckLoginInfo() {
        super(Phase.PRE_PROTOCOL);
    }
    
    @Override
    public void handleMessage(SoapMessage msg) throws Fault {
        Header header = msg.getHeader(new QName("login"));
        if (header != null) {
            Element ele = (Element) header.getObject();
    
            NodeList acc = ele.getElementsByTagName("acc");
            String account = acc.item(0).getTextContent();
    
            NodeList pass = ele.getElementsByTagName("pass");
            String password = pass.item(0).getTextContent();
    
            if ("bruce".equals(account) && "12345".equals(password)) {
                System.out.println("驗證通過!");
                return;
            }
        }
    
        System.out.println("驗證未通過!");
        throw new Fault(new RuntimeException("帳號和密碼不正確"));
    }
}

※同樣繼承 AbstractPhaseInterceptor 並從head取出帳號和密碼


※server.java

String url = "http://localhost:8888/aaa/bbb/ccc";
EndpointImpl publish = (EndpointImpl) Endpoint.publish(url, new HelloWS());
    
List<Interceptor<? extends Message>> in = publish.getInInterceptors();
in.add(new CheckLoginInfo());
    
System.out.println("server is start!");

※因為客戶端丟過來,所以當然是用in去接

※此時輸入正確的帳密才會印出「call add method!」,否則就會被攔截器攔截並拋出例外

沒有留言:

張貼留言