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信息)。
服务器端就是这么简单,看看客户端的TestCase
你应该看到上面的client.addOutHandler(new ClientAuthHandler());
没错,它跟服务器端的AuthenticationHandler是一对,一起使用的!
也就是,每个被送往WebService服务的请求都被ClientAuthHandler处理过了。
看看ClientAuthHandler做了些什么:
不就是往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一下就知道了。
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一下就知道了。