Web Service學習-CXF開發Web Service的權限控制(二)

jopen 8年前發布 | 11K 次閱讀 WEB服務/RPC/SOA


Web Service如何進行權限控制?

 

解決思路:服務器端要求input消息總是攜帶有用戶名,密碼信息,如果沒有用戶名和密碼信息,直接拒絕調用

 

解決方案:攔截器

 

為了讓程序員能訪問,并修改CXF框架所生成的SOAP消息,CXF提供了攔截器

 

CXF(Celtix +XFire)說明:

 

如果不用CXF等框架,SOAP消息的生成,解析都是由程序員負責。無論是添加用戶名,密碼信息還是提取用戶名,密碼信息,都可由程序員代碼完成。

如果使用CXF等框架,SOAP消息的生成,解析都是由CXF等框架來完成。

 

總的來說,CXF發布WebService進行了封裝,簡化了我們的操作。

 

攔截器:


服務端添加攔截器:


1,獲取Endpointpublish的方法返回值。

2,調用該對象的getInInterceptors,getOutInterceptors方法來獲取InOut攔截器列表,接下來就可以添加攔截器了


package com.tgb.client;


import javax.xml.ws.Endpoint;

import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.EndpointImpl;

import com.tgb.service.HelloWorld;
import com.tgb.service.impl.HelloWorldImpl;

public class ServerMain {

    public static void main(String[] args){

        HelloWorld hw=new HelloWorldImpl();
        //調用endpoint的publish方法,來發布web service
//      Endpoint.publish("http://192.168.24.215:8889/hjy",hw);
        EndpointImpl ep=(EndpointImpl)Endpoint.publish("http://192.168.24.215:8899/hjy",hw);

        //添加In攔截器
        ep.getInInterceptors().add(new LoggingInInterceptor());
        //添加Out攔截器
        ep.getOutInterceptors().add(new LoggingOutInterceptor());

        System.out.println("Web Service暴露成功");
    }
}

輸出內容:




客戶端添加攔截器:

 

1,添加相應的CXFjar

2,調用ClientProxy的getClient方法,調用該方法以遠程Web Service的代理為參數

3,調用Client對象的getInInterceptors,getOutInterceptors方法來獲取InOut攔截器列表,接下來就可以添加攔截器了


package hjy;

import java.util.List;

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.tgb.service.Cat;
import com.tgb.service.HelloWorld;
import com.tgb.service.User;
import com.tgb.service.impl.HelloWorldImpl;

public class ClientMain {

    public static void main(String[] args){
        HelloWorldImpl factory=new HelloWorldImpl();
        //此處返回的只是遠程Web Service的代理
        HelloWorld hw=factory.getHelloWorldImplPort();

        Client client=ClientProxy.getClient(hw);
        client.getInInterceptors().add(new LoggingInInterceptor());
        client.getOutInterceptors().add(new LoggingOutInterceptor());

        System.out.println(hw.sayHi("hejingyuan"));

        System.out.println("--------------------------");

        User user=new User();
        user.setId(20);
        user.setName("孫悟空");
        user.setPass("111");
        user.setAddress("花果山");

        List<Cat> cats=hw.getCatsByUser(user);
        for(Cat cat:cats){
            System.out.println(cat.getName());
        }

        System.out.println("--------------------------");

        System.out.println(hw.getAllCats().getEntry().get(0).getKey());
        System.out.println(hw.getAllCats().getEntry().get(0).getValue().getName());



    }
}
 

打印內容為:




自定義攔截器

 

實現效果:當輸入用戶名密碼時,才可以調用我們的服務。即我們需要在服務端添加輸入攔截,在客戶端添加輸出攔截

自定義攔截器,需要實現Interceptor接口,實際上,我們一般會繼承AbstractPhaseInterceptor

 

服務端代碼:


package com.tgb.client;


import javax.xml.ws.Endpoint;

import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.EndpointImpl;

import com.tgb.auth.AuthInterceptor;
import com.tgb.service.HelloWorld;
import com.tgb.service.impl.HelloWorldImpl;

public class ServerMain {

    public static void main(String[] args){

        HelloWorld hw=new HelloWorldImpl();
        //調用endpoint的publish方法,來發布web service
//      Endpoint.publish("http://192.168.24.215:8889/hjy",hw);
        EndpointImpl ep=(EndpointImpl)Endpoint.publish("http://192.168.24.215:8891/hjy",hw);

        //添加In攔截器,該AuthInterceptor就會負責檢查用戶名,密碼是否正確
        ep.getInInterceptors().add(new AuthInterceptor());
        //添加Out攔截器
//      ep.getOutInterceptors().add(new LoggingOutInterceptor());

        System.out.println("Web Service暴露成功");
    }
}

AuthInterceptor :


package com.tgb.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;


//通過PhaseInterceptor,可以指定攔截器在哪個階段起作用
public class AuthInterceptor extends AbstractPhaseInterceptor<SoapMessage>{

    //由于AbstractPhaseInterceptor無無參數構造器,使用繼承的方式,需要顯示調用父類有參數的構造器
    public AuthInterceptor(){
        //super表示顯示調用父類有參數的構造器
        //顯示調用父類構造器之后,程序將不會隱式調用父類無參數的構造器
        super(Phase.PRE_INVOKE);//該攔截器將會調用之前攔截SOAP消息
    }
    //實現自己的攔截器時,需要實現handleMessage方法。
    //handleMessage方法中的形參就是被攔截到的Soap消息
    //一旦程序獲取了SOAP消息,剩下的事情就可以解析SOAP消息或修改SOAP消息
    @Override
    public void handleMessage(SoapMessage msg) throws Fault {

        System.out.println("-------"+msg);
        //從這里可以看出,我們已經攔截到了SOAP消息

        //得到SOAP消息所有Header
        List<Header> headers=msg.getHeaders();

        //如果沒有Header
        if(headers==null||headers.size()<1){
            throw new Fault(new IllegalArgumentException("根本沒有Header,不能調用"));                       
        }

        //假如要求第一個Header攜帶了用戶名,密碼信息
        Header firstHeader=headers.get(0);
        Element ele=(Element)firstHeader.getObject();

        NodeList userIds=ele.getElementsByTagName("userId");
        NodeList userPasses=ele.getElementsByTagName("userPass");

        if(userIds.getLength()!=1){
            throw new Fault(new IllegalArgumentException("用戶名的格式不正確!"));
        }
        if(userPasses.getLength()!=1){
            throw new Fault(new IllegalArgumentException("密碼的格式不正確!"));
        }

        //得到第一個userId元素里的文本內容,以該內容作為用戶名字
        String userId=userIds.item(0).getTextContent();
        String userPass=userPasses.item(0).getTextContent();
        //實際項目中,應該去查詢數據庫,該用戶名密碼是否被授權調用web service
        if(!userId.equals("hejingyuan") || !userPass.equals("hjy")){
            throw new Fault(new IllegalArgumentException("用戶名密碼不正確!"));
        }
    }

}

客戶端代碼:


package hjy;

import java.util.List;

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;

import com.tgb.auth.AddHeaderInterceptor;
import com.tgb.service.Cat;
import com.tgb.service.HelloWorld;
import com.tgb.service.User;
import com.tgb.service.impl.HelloWorldImpl;

public class ClientMain {

    public static void main(String[] args){
        HelloWorldImpl factory=new HelloWorldImpl();
        //此處返回的只是遠程Web Service的代理
        HelloWorld hw=factory.getHelloWorldImplPort();

        Client client=ClientProxy.getClient(hw);
        //參數為輸入的用戶名,密碼
        client.getOutInterceptors().add(new AddHeaderInterceptor("hejingyuan","hjy"));

        System.out.println(hw.sayHi("hejingyuan"));

        System.out.println("--------------------------");

        User user=new User();
        user.setId(20);
        user.setName("孫悟空");
        user.setPass("111");
        user.setAddress("花果山");

        List<Cat> cats=hw.getCatsByUser(user);
        for(Cat cat:cats){
            System.out.println(cat.getName());
        }

        System.out.println("--------------------------");

        System.out.println(hw.getAllCats().getEntry().get(0).getKey());
        System.out.println(hw.getAllCats().getEntry().get(0).getValue().getName());


    }
}

AddHeaderInterceptor:


package com.tgb.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;


public class AddHeaderInterceptor extends AbstractPhaseInterceptor<SoapMessage>{

    private String userId;
    private String userPass;

    public AddHeaderInterceptor(String userId,String userPass){
        super(Phase.PREPARE_SEND);//在準備發送SOAP消息時啟用該攔截器
        this.userId=userId;
        this.userPass=userPass;
    }
    @Override
    public void handleMessage(SoapMessage msg) throws Fault {
        List<Header> headers=msg.getHeaders();
        //創建Document對象
        Document doc=DOMUtils.createDocument();
        Element ele=doc.createElement("authHeader");

        //此處創建的元素應該按照服務器那邊的要求
        Element idEle=doc.createElement("userId");
        idEle.setTextContent(userId);
        Element passEle=doc.createElement("userPass");
        passEle.setTextContent(userPass);

        ele.appendChild(idEle);
        ele.appendChild(passEle);

        /**
         * 上面代碼生成了一個如下XML文檔片段
         * <authHeader>
         *      <userId>hejingyuan</userId>
         *      <userPass>hjy</userPass>
         * </authHeader>
         */
        //把ele元素包裝成Header,并添加到SOAP消息的Header列表中
        headers.add(new Header(new QName("hejingyuan"),ele));
    }

}


啟動服務端的ServerMain的main函數,將服務發布,然后啟動客戶端ClientMain的main函數去訪問服務端提供的服務。

用戶名密碼錯誤時:




用戶名密碼正確時:




總結:

 

權限控制的實現方式為使用攔截器,對于攔截到的Soap消息進行修改。

SOAP消息:

根元素是Envolope

Header

默認情況下,Header元素不是強制出現的

Header元素由程序員控制添加,主要用戶攜帶一些額外的信息,比如用戶名,密碼信息

Body

如果調用正確,Body元素的內容應該遵守WSDL所要求的格式

如果調用錯誤,Body元素的內容就是Fault子元素



源碼下載



來自: http://blog.csdn.net/hejingyuan6/article/details/47133659

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!