在SpringSide實現XFire Webservice認證
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去處理,我們的驗證邏輯可以在里面進行。
????<!--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>
我們接著看看authenticationHandler的代碼:
我們在SpringSide中通過header方式向服務器提供驗證信息(另外一種更簡單的方式是創建一個Login的webservice服務,然后在XFire Session中建立Token信息)。
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;
/**
?*?XFire的回調的Handler,在XFire配置文件中配置
?*?Server端的認證模塊,回調處理模塊
?*?
?*?ClientAuthHandler跟AuthenticationHandler要一起用,或者都不用
?*?
?*?@author??david.turing
?*?@blog??openssl.blogjava.net
?*
?*/
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);
?
????????
????}
}
注意,XFireFault異常是往客戶端拋的,Webservice Client應該學會catch XFireFault.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;
/**
?*?XFire的回調的Handler,在XFire配置文件中配置
?*?Server端的認證模塊,回調處理模塊
?*?
?*?ClientAuthHandler跟AuthenticationHandler要一起用,或者都不用
?*?
?*?@author??david.turing
?*?@blog??openssl.blogjava.net
?*
?*/
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);
?
????????
????}
}
服務器端就是這么簡單,看看客戶端的TestCase
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());
????}
}
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());
????}
}
你應該看到上面的client.addOutHandler(new ClientAuthHandler());
沒錯,它跟服務器端的AuthenticationHandler是一對,一起使用的!
也就是,每個被送往WebService服務的請求都被ClientAuthHandler處理過了。
看看ClientAuthHandler做了些什么:
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;
/**
?*?客戶端端的認證模塊,回調處理模塊
?*?每個需要認證的WebService方法都可以掛這個Handler
?*?
?*?僅用于Demo,從解耦和易用性出發,
?*?沒有跟Acegi結合,你可以任意擴展
?*?默認用戶名/密碼是admin/admin
?*?
?*?ClientAuthHandler跟AuthenticationHandler要一起用,或者都不用
?*?
?*?@author??david.turing
?*
?*?@blog?openssl.blogjava.net
?*/????
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?{
????????????????????????
????????????/*******************************************
?????????????*?Soap?Header方式
?????????????*?從Soap?Header中獲取用戶名密碼
?????????????*******************************************/
????????????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!");
????????}
????}
import?org.apache.log4j.Logger;
import?org.codehaus.xfire.MessageContext;
import?org.codehaus.xfire.handler.AbstractHandler;
import?org.jdom.Element;
import?org.jdom.Namespace;
/**
?*?客戶端端的認證模塊,回調處理模塊
?*?每個需要認證的WebService方法都可以掛這個Handler
?*?
?*?僅用于Demo,從解耦和易用性出發,
?*?沒有跟Acegi結合,你可以任意擴展
?*?默認用戶名/密碼是admin/admin
?*?
?*?ClientAuthHandler跟AuthenticationHandler要一起用,或者都不用
?*?
?*?@author??david.turing
?*
?*?@blog?openssl.blogjava.net
?*/????
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?{
????????????????????????
????????????/*******************************************
?????????????*?Soap?Header方式
?????????????*?從Soap?Header中獲取用戶名密碼
?????????????*******************************************/
????????????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!");
????????}
????}
不就是往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一下就知道了。
posted on 2006-07-25 23:48 david.turing 閱讀(8778) 評論(4) 編輯 收藏 所屬分類: Security領域