`
security
  • 浏览: 372420 次
  • 来自: www.pgp.org.cn
社区版块
存档分类
最新评论

在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.

服务器端就是这么简单,看看客户端的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一下就知道了。
分享到:
评论
1 楼 micropang 2009-08-20  
怎么都是这些,都没有C# 作为客户端的,那要怎么注入法?!!~~~~~

相关推荐

Global site tag (gtag.js) - Google Analytics