1. 程式人生 > >Web Service學習-CXF開發Web Service的許可權控制(二)

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

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子元素

原始碼下載