当前位置 : 主页 > 网页制作 > Nodejs >

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

来源:互联网 收集:自由互联 发布时间:2021-06-24
在OSGI环境中,通常使用CXF-DOSGI作为webservice发布框架,这种资料网上很多,但是如何在CXF-DOSGI下做webservice的权限校验,网上资料几乎为零。 关于CXF(注意不是CXF-DOSGI)做权限校验的资料

在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<String, String> props = new Hashtable<String, String>();

        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权限拦截器 * <p> * 根据用户名和密码进行拦截 * </p> * @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;

/** * <p> * 调用webservice时需要传入用户名和密码 * </p> * @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<Header> 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文档 ,我们这里是直接使用用户名和密码 * <authHeader> * <userId>lzw</userId> * <userPass>123456</userPass> * </authHeader> */  
        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

网友评论