CXF-DOSGI为webservice增加用户名密码权限校验

在OSGI环境中,通常使用CXF-DOSGI作为webservice发布框架,这种资料网上很多,但是如何在CXF-DOSGI下做webservice的权限校验,网上资料几乎为零。
关于CXF(注意不是CXF-DOSGI)做权限校验的资料一堆堆的。本人经过试验,解决了这个问题,说好的代码如下:

一、服务端的

public class Activator implements BundleActivator {
    @SuppressWarnings("rawtypes")
    private ServiceRegistration registration;
    private static BundleContext context;

    static BundleContext getContext() {
        return context;
    }

    @Override
    public void start(BundleContext bundleContext) throws Exception {
          Activator.context = bundleContext;
          //设置服务的属性
        Dictionary props = new Hashtable();

        props.put("service.exported.interfaces","*");  
        props.put("service.exported.intents","SOAP");  
        // 发布服务的类型,有ws类型的,这种是基于SOAP协议的,还有一种是REST类型的
        props.put("service.exported.configs","org.apache.cxf.ws");  
        props.put("org.apache.cxf.ws.address","http://localhost:9000/helloWorld");  // webservice的发布地址

        // 这里是增加权限校验拦截器的关键,这里是个数组可以有多个拦截器
        String[] interceptors = new String[] {"test.AuthInterceptor"};
        props.put("org.apache.cxf.ws.in.interceptors", interceptors);

        //注册服务,这是OSGI同普通项目不同的地方,是以这种方式发布的
        registration = Activator.context.registerService(HelloWorldService.class.getName(), new HelloWorldServiceImpl(), props); 
    }

    @Override
    public void stop(BundleContext bundleContext) throws Exception {
        Activator.context = null;
         registration.unregister();
    }

}

// webservice接口代码
public interface HelloWorldService {
    String sayHello();
}

// webservice实现类
public class HelloWorldServiceImpl implements HelloWorldService {

    @Override
    public String sayHello() {
        System.out.println("HelloWorld");
        return (new Date()).toString();
    }
}

// 权限校验拦截器
package test.service;
import gboat2.base.core.util.SpringContextUtil;
import gboat2.cxf.business.IWebServiceConfigBusiness;
import gboat2.cxf.model.WebServiceConfig;

import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;

import org.apache.commons.lang3.StringUtils;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.saaj.SAAJInInterceptor;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.message.Exchange;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.service.Service;
import org.apache.cxf.service.invoker.BeanInvoker;
import org.w3c.dom.NodeList;

/**
 * webservice权限拦截器
 * 

* 根据用户名和密码进行拦截 *

* @author zhaic * @since jdk1.6 * 2016年6月22日 * */
public class AuthInterceptor extends AbstractPhaseInterceptor<SoapMessage> { private SAAJInInterceptor saa = new SAAJInInterceptor(); public AuthInterceptor() { super(Phase.PRE_PROTOCOL); // 注意这个配置是拦截器被触发的阶段不能错 getAfter().add(SAAJInInterceptor.class.getName()); } @Override public void handleMessage(SoapMessage message) throws Fault { // 获取到当前服务的接口名称,这里如果不需要也可以不要,我这里纯属是为了解决我们自己的业务问题 Exchange exchange = message.getExchange(); Service service = exchange.get(Service.class); Class[] interfaces = ((BeanInvoker)service.getInvoker()).getServiceObject(exchange).getClass().getInterfaces(); SOAPMessage mess = message.getContent(SOAPMessage.class); if (mess == null) { saa.handleMessage(message); mess = message.getContent(SOAPMessage.class); } SOAPHeader head = null; try { head = mess.getSOAPHeader(); } catch (Exception e) { e.printStackTrace(); } if (head == null) { return; } NodeList loginNameNodes = head.getElementsByTagName("loginName"); NodeList passwordNodes = head.getElementsByTagName("password"); if((null == loginNameNodes || loginNameNodes.getLength() == 0) || (null == passwordNodes || passwordNodes.getLength() == 0)) { // 以下边这种方式抛出异常可以被客户端接到 SOAPException soapExc = new SOAPException("loginName and password can not null!"); throw new Fault(soapExc); } String loginName = loginNameNodes.item(0).getTextContent(); String password = passwordNodes.item(0).getTextContent(); boolean validateResult = validateAuth(interfaces, loginName, password); if (validateResult) { SOAPException soapExc = new SOAPException("authentication failed!"); throw new Fault(soapExc); } else { SOAPException soapExc = new SOAPException("webservice authentication error!"); throw new Fault(soapExc); } } /** * 校验用户权限 * @param clazzs 用户发布的服务类所实现的接口列表 * @param loginName * @param password * @return */ private boolean validateAuth(Class[] clazzs, String loginName, String password) { // 这里写相关的权限校验代码 } }

二、客户端的代码基本如下:

调用部分代码,这里用的是非动态客户端,至于动态客户端可以参考其他例子,也不过是增加个拦截器而已,这里不再赘述;
public class TestWebServiceResult {
    public staitc void main(String[] args) {
      AuthorityParameter param = new AuthorityParameter("loginName", "anshun", "password", "123456");
      JaxWsProxyFactoryBean  factoryBean=new JaxWsProxyFactoryBean(); 
      factoryBean.getInInterceptors().add(new AuthorityHeaderInterceptor(param)); 
      factoryBean.setServiceClass(HelloWorldService.class); 
      factoryBean.setAddress("http://localhost:9000/helloWorld"); 
      HelloWorldService service = (IHelloWorldService)factoryBean.create(); 
      System.out.println(service.sayHello()); 
    }
}

// 权限代码拦截器
package test.utils;
import gboat2.cxf.utils.AuthorityParameter;
import java.util.List;
import javax.xml.namespace.QName;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * 

* 调用webservice时需要传入用户名和密码 *

* @author zhaic * @since jdk1.6 * 2015年11月17日 * */
public class AuthorityHeaderInterceptor extends AbstractPhaseInterceptor<SoapMessage>{ private AuthorityParameter authorityParameter; public AuthorityHeaderInterceptor(AuthorityParameter authorityParameter) { super(Phase.PREPARE_SEND); this.authorityParameter = authorityParameter; } public void handleMessage(SoapMessage msg) throws Fault { List
headers = msg.getHeaders(); //创建Document对象 Document doc = DOMUtils.createDocument(); //配置服务器端Head信息的用户密码 Element eleId= doc.createElement(this.authorityParameter.getUserNameKey()); eleId.setTextContent(this.authorityParameter.getUserNameValue()); Element elePass = doc.createElement(this.authorityParameter.getPasswordKey()); elePass.setTextContent(this.authorityParameter.getPasswordValue()); /** * 也可以先创建一个父节点,则生成的XML文档 ,我们这里是直接使用用户名和密码 * * lzw * 123456 * */ headers.add(new Header(new QName(""), eleId)); headers.add(new Header(new QName(""), elePass)); } } 密码参数类: public class AuthorityParameter { /** * 用户名字段的名称 */ private String userNameKey; /** * 用户名字段的值 */ private String userNameValue; /** * 密码字段的名称 */ private String passwordKey; /** * 密码字段的值 */ private String passwordValue; public AuthorityParameter() { super(); } /** * AuthorityParameter * @param userNameKey 用户名的字段名称 * @param userNameValue 用户名的字段值 * @param passwordKey 密码的字段名称 * @param passwordValue 密码的字段值 */ public AuthorityParameter(String userNameKey, String userNameValue, String passwordKey, String passwordValue) { super(); this.userNameKey = userNameKey; this.userNameValue = userNameValue; this.passwordKey = passwordKey; this.passwordValue = passwordValue; } public String getUserNameKey() { return userNameKey; } public void setUserNameKey(String userNameKey) { this.userNameKey = userNameKey; } public String getUserNameValue() { return userNameValue; } public void setUserNameValue(String userNameValue) { this.userNameValue = userNameValue; } public String getPasswordKey() { return passwordKey; } public void setPasswordKey(String passwordKey) { this.passwordKey = passwordKey; } public String getPasswordValue() { return passwordValue; } public void setPasswordValue(String passwordValue) { this.passwordValue = passwordValue; } }

好了,以上就是全部的内容,难点在于在CXF-DOSGI如何加入拦截器,这里的资料实在是少之又少。即使是Apache官方也没有直接的例子,不过CXF和CXF-DOSGI毕竟是师出同门,有很多相似之处,本人就是通过参考CXF的例子结合仅有的一页CXF-DOSGI的参数配置说明解决了这个问题。这个问题突破后,还可以写其他功能的拦截器,比如可以取出客户端的IP(注意,由于CXF默认使用的是HTTP协议,所以是可以使用HttpServletRequest等类的)来进行白名单过滤。希望啰嗦了半天能帮上网友。

附重要帮助文档:
CXF-DOSGI配置档,里边有很多配置在其他地方根本找不到,因此尤其重要:
https://cwiki.apache.org/confluence/display/CXF/Distributed+OSGi+Reference
CXF拦截器文档:
http://cxf.apache.org/docs/interceptors.html

你可能感兴趣的:(OSGI,WebService)