天天看點

在SpringSide實作XFire Webservice認證--原作david.turing

XFire官方網站提供的基于Webservice認證的例子有問題,在新版本的XFire1.1.2中編譯不通過,不過這也是小Case,我後來折騰了一下,為SpringSide提供了一個簡單的Webservice認證功能。

XFire跟Spring的天然融合,讓我們可以少努力10年就能簡單地在Spring中使用Webservice的強大魅力,我從AXIS專向XFire有一些沖動,也吃了不少虧,但受REST一族的強力吹捧,感覺還是值得嘗試的,是以,在公司的系統中也把Axis徹底換了XFire。

回到SpringSide,我大概介紹一下如何配置一個真正實用的XFire驗證服務。

SpringSide中的XFire配置檔案放在:

SpringSide-bookstore\src\org\springside\bookstore\plugins\webservice\applicationContext-webservice-server.xml

我們在裡面定義各個Webservice,該檔案其實對應于XFire官方的XFire-Servlet.xml

看看下面的BookService,這是一個典型的Webservice服務,紅色的inHandlers是我挂上去的。它的意思是所有通路BookService的請求都會被先送到authenticationHandler去處理,我們的驗證邏輯可以在裡面進行。

[code] <!--Web Service 在SpringMVC中的URL 路徑映射-->

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

<property name="mappings">

<value>/BookService=bookWebService</value>

</property>

<property name="inHandlers">

<ref bean="authenticationHandler"/>

</property>

</bean>[/code]

我們接着看看authenticationHandler的代碼:

我們在SpringSide中通過header方式向伺服器提供驗證資訊(另外一種更簡單的方式是建立一個Login的webservice服務,然後在XFire Session中建立Token資訊)。

[code]package org.springside.bookstore.plugins.webservice.authentication;

import org.apache.log4j.Logger;

import org.codehaus.xfire.MessageContext;

import org.codehaus.xfire.exchange.InMessage;

import org.codehaus.xfire.fault.XFireFault;

import org.codehaus.xfire.handler.AbstractHandler;

import org.jdom.Element;

import org.jdom.Namespace;

public class AuthenticationHandler extends AbstractHandler {

private static final Logger log = Logger.getLogger(AuthenticationHandler.class);

public void invoke(MessageContext context) throws Exception {

log.info("#AuthenticationHandler is invoked");

InMessage message=context.getInMessage();

final Namespace TOKEN_NS = Namespace.getNamespace("SpringSide","http://service.webservice.plugins.bookstore.springside.org");

if(message.getHeader()==null)

{

throw new XFireFault("GetRelation Service Should be Authenticated",

XFireFault.SENDER);

}

Element token = message.getHeader().getChild("AuthenticationToken", TOKEN_NS);

if (token == null)

{

throw new XFireFault("Request must include authentication token.",

XFireFault.SENDER);

}

String username = token.getChild("Username", TOKEN_NS).getValue();

String password = token.getChild("Password", TOKEN_NS).getValue();

System.out.println("username="+username);

System.out.println("password="+password);

if(username==null||password==null)

throw new XFireFault("Supplied Username and Password Please",

XFireFault.SENDER);

PasswordAuthenticationManager pamanager=new PasswordAuthenticationManager();

if(!pamanager.authenticate(username,password))

throw new XFireFault("Authentication Fail! Check username/password",

XFireFault.SENDER);

}

}[/code]注意,XFireFault異常是往用戶端抛的,Webservice Client應該學會catch XFireFault.

伺服器端就是這麼簡單,看看用戶端的TestCase

[code]package org.springside.bookstore.plugins.webservice.service;

import java.lang.reflect.Proxy;

import java.net.MalformedURLException;

import java.util.List;

import org.codehaus.xfire.client.Client;

import org.codehaus.xfire.client.XFireProxy;

import org.codehaus.xfire.client.XFireProxyFactory;

import org.codehaus.xfire.service.Service;

import org.codehaus.xfire.service.binding.ObjectServiceFactory;

import org.springside.bookstore.commons.domain.Book;

import org.springside.bookstore.plugins.webservice.authentication.ClientAuthHandler;

import junit.framework.TestCase;

public class BookServiceWithAuthenticationTestCase extends TestCase {

protected void setUp() throws Exception {

super.setUp();

}

protected void tearDown() throws Exception {

super.tearDown();

}

public void getBookFromWebservice() throws Exception{

Service serviceModel = new ObjectServiceFactory()

.create(BookService.class);

BookService service = null;

try {

service=(BookService) new XFireProxyFactory().create(

serviceModel,

"http://localhost:8080/springside/service/BookService");

} catch (MalformedURLException e) {

e.printStackTrace();

}

Client client = ((XFireProxy) Proxy.getInvocationHandler(service)).getClient();

//挂上ClientAuthHandler,提供認證

client.addOutHandler(new ClientAuthHandler());

List list = service.findBooksByCategory(null);

assertNotNull(list);

for(int i=0;i<list.size();i++)

System.out.println(((Book)list.get(i)).getName());

}

}[/code]

你應該看到上面的client.addOutHandler(new ClientAuthHandler());

沒錯,它跟伺服器端的AuthenticationHandler是一對,一起使用的!

也就是,每個被送往WebService服務的請求都被ClientAuthHandler處理過了。

看看ClientAuthHandler做了些什麼:

[code]package org.springside.bookstore.plugins.webservice.authentication;

import org.apache.log4j.Logger;

import org.codehaus.xfire.MessageContext;

import org.codehaus.xfire.handler.AbstractHandler;

import org.jdom.Element;

import org.jdom.Namespace;

public class ClientAuthHandler extends AbstractHandler {

private static final Logger log = Logger.getLogger(ClientAuthHandler.class);

//用戶端自己配置使用者名密碼或者更安全的KeyStore方式

private String username = "admin";

private String password = "admin";

public ClientAuthHandler() {

}

public ClientAuthHandler(String username,String password) {

this.username = username;

this.password = password;

}

public void setUsername(String username) {

this.username = username;

}

public void setPassword(String password) {

this.password = password;

}

public void invoke(MessageContext context) throws Exception {

final Namespace ns = Namespace.getNamespace("SpringSide","http://service.webservice.plugins.bookstore.springside.org");

Element el = new Element("header",ns);

Element auth = new Element("AuthenticationToken", ns);

Element username_el = new Element("Username",ns);

username_el.addContent(username);

Element password_el = new Element("Password",ns);

password_el.addContent(password);

auth.addContent(username_el);

auth.addContent(password_el);

el.addContent(auth);

context.getCurrentMessage().setHeader(el);

log.info("ClientAuthHandler done!");

}

}[/code]

不就是往header裡面注入username,password!

在SpringSide中,所有的Spring配置檔案都被小白分散到各個Module中去了,Wuyu原先是在Plugin中提供Webservice功能,是以,我仍然在Plugin中建立XFire接口。

SpringSide的Spring配置檔案放在:

SpringSide-bookstore\webapp\WEB-INF\springmvc-servlet.xml

該檔案定義了Plugin的xml:

AuthenticationHandler這個Bean需要先定義在Plugins-servlet.xml中,其它很簡單,大家去Try一下就知道了。