本人在做xfire認證時,在網上找一篇文件介紹springside實現的認證,覺得不錯,所以摘錄了。多謝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去處理,我們的驗證邏輯可以在裏面進行。 <!--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. 服務器端就是這麼簡單,看看客戶端的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()); } } 你應該看到上面的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!"); } } 不就是往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一下就知道了。 |