xfire官方网站提供的基于webservice认证的例子有问题,在新版本的xfire1.1.2中编译不通过,不过这也是小case,我后来折腾了一下,为springside提供了一个简单的webservice认证功能。
xfire跟spring的天然融合,让我们可以少努力10年就能简单地在spring中使用webservice的强大魅力,我从axis专向xfire有一些冲动,也吃了不少亏,但受rest一族的强力吹捧,感觉还是值得尝试的,因此,在公司的系统中也把axis彻底换了xfire。
回到springside,我大概介绍一下如何配置一个真正实用的xfire验证服务。
springside中的xfire配置文件放在:
-bookstore\src\org\springside\bookstore\plugins\webservice\applicationcontext-webservice-server.xml
我们在里面定义各个webservice,该文件其实对应于xfire官方的xfire-servlet.xml
看看下面的bookservice,这是一个典型的webservice服务,红色的inhandlers是我挂上去的。它的意思是所有访问bookservice的请求都会被先送到
authenticationhandler去处理,我们的验证逻辑可以在里面进行。
/bookservice=bookwebservice
我们接着看看
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("","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("","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配置文件放在:
-bookstore\webapp\web-inf\springmvc-servlet.xml
该文件定义了plugin的xml:
authenticationhandler这个bean需要先定义在plugins-servlet.xml中,其它很简单,大家去try一下就知道了。