CXF动态客户端调用JDK自带Webservice安全校验

        项目中有个需求,需要使用CXF动态客户端调用webservice服务端,这个服务端是JDK自带的webservice发布的,而且我们需要在调用时传入用户名和密码。网上CXF客户端和服务端配套使用webservice的方法很多,这里不再赘述,这里主要讲下我上边说的特殊使用方式。有人会把用户名和密码放在webservice的请求参数里,这样污染了webservice接口,本人不喜欢这种方式。

        开始的时候,我先试了CXF拦截器传送密码的方式,我自己是参照类似的文章写的:点击打开链接,结果发现出现很多奇怪的错误,比如各种命名空间不识别,JAXB解组错误等等,压根就没有进入服务端的handler,当然了服务端的总入口也是拿到了请求,只是在进入handler之前就报错了。按说CXF也是遵守了webservice的国际规范了应该可以的。后来看了CXF中的传入用户名和密码所涉及的类和参数,分析出CXF应该是对SOAP的head操作做了封装,所以导致SOAP中的具体数据内容和JDK自带的webservice是不同的,怎么办呢?后来想到我可以直接自定义SOAP头,然后到服务端的handler中拿到这个头进行分析不就解决了?经过各种调试,终于解决了。下边公布代码:

        1、服务端的handlers.xml

          
         
             
               
                  ServiceSOAPHandler
                  com.cms.webservice.AuthorityHandler
             

           

           

       2、服务端的handler代码:

    /**
     * 
     */
    package com.cms.webservice;
    
    import java.io.File;
    import java.io.IOException;
    import java.util.Iterator;
    import java.util.Set;
    
    import javax.xml.namespace.QName;
    import javax.xml.soap.Node;
    import javax.xml.soap.SOAPBody;
    import javax.xml.soap.SOAPConstants;
    import javax.xml.soap.SOAPEnvelope;
    import javax.xml.soap.SOAPException;
    import javax.xml.soap.SOAPFault;
    import javax.xml.soap.SOAPHeader;
    import javax.xml.soap.SOAPMessage;
    import javax.xml.ws.handler.MessageContext;
    import javax.xml.ws.handler.soap.SOAPHandler;
    import javax.xml.ws.handler.soap.SOAPMessageContext;
    import javax.xml.ws.soap.SOAPFaultException;
    
    import com.jeecms.cms.Constants;
    import com.jeecms.common.util.PropertyUtils;
    
    /**
     * 服务端权限校验类
     * 
     * @author zhaichong
     *
     *         2014年10月1日
     */
    public class AuthorityHandler implements SOAPHandler {
    
    private static String PROPERTIE_NAME="webservice.properties";
    private static String USER_NAME_PWD="webservice.userNamePwd";
   
    public boolean handleMessage(SOAPMessageContext context) {
    return auth(context);
    }
    
    /**
    * 校验用户名密码是否合法
   
    * @param context
    * @return
    */
    private boolean auth(SOAPMessageContext context) {
    Boolean out = (Boolean) context.get(SOAPMessageContext.MESSAGE_OUTBOUND_PROPERTY);
    if (!out) {
    SOAPMessage message = context.getMessage();
    try {
    SOAPHeader header = message.getSOAPPart().getEnvelope().getHeader();
    if (header != null) {
    // 获取SOAP头登录校验节点
    // org.w3c.dom.Node node =
    // header.getElementsByTagName("authHeader").item(0);
    // 获取用户名
    org.w3c.dom.Node userNameNode = header.getElementsByTagName("userName").item(0);
    String userName = userNameNode.getTextContent();
    // 获取密码
    org.w3c.dom.Node passwordNode = header.getElementsByTagName("password").item(0);
    String password = passwordNode.getTextContent();
    // 获取本地配置文件用户名密码 与传过来的用户名密码比对
    PropertiesUtil propertiesUtil = new PropertiesUtil(PROPERTIE_NAME);
    String userNamePWD = propertiesUtil.getKeyValue(USER_NAME_PWD);
    if (userNamePWD != null) {
    String namePWDs[] = userNamePWD.split(",");
    for (int i = 0; i < namePWDs.length; i++) {
    if (namePWDs[i] != null) {
    String uName = namePWDs[i].split("\\|")[0];
    String uPassword = namePWDs[i].split("\\|")[1];
   
    if (uPassword.equals(password) && uName.equals(userName)) {
    return true;
    }
    }
    }
    }
    //System.out.println("client send userName:" + userName);
    //System.out.println("client send password:" + password);
    return false;
    } else {
    return false;
    }
    } catch (SOAPException e) {
    e.printStackTrace();
    }
    
    }
    
    return true;
    }
    
    public boolean handleFault(SOAPMessageContext context) {
    // TODO Auto-generated method stub
    return false;
    }
    
    public void close(MessageContext context) {
    // TODO Auto-generated method stub
    
    }
    
    public Set getHeaders() {
    // TODO Auto-generated method stub
    return null;
    }
    
    }

   注意: 这里的handler.xml文件在war包里是放在classes下的,webservice的实现类要用@HandlerChain(file="handlers.xml")进行注解,上边代码的用户名和密码是放在properties文件中的,各位网友可以自己修改。

3、客户端的拦截器:

    package gboat2.cxf.test;


    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时需要传入用户名和密码
     *


     *
     */
    public class AuthorityHeaderInterceptor extends AbstractPhaseInterceptor{  
        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)); 
        }   
    }  

    4、用户名和密码类:

        
    package gboat2.cxf.utils;
    
    
    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;
}
   }

    5、调用代码

   JaxWsDynamicClientFactory factory = JaxWsDynamicClientFactory.newInstance(); 
   Client client = null; 
   // 创建客户端连接 
   client = factory.createClient("http://localhost:9090/cms/saveCms?wsdl"); 

   AuthorityParameter param = new AuthorityParameter("userName", "anshun", "password", "123456");
   client.getOutInterceptors().add(new AuthorityHeaderInterceptor(authorityParameter)); 
   client.getOutInterceptors().add(new LoggingOutInterceptor()); 

   // 客户端设置

   HTTPClientPolicy policy = ((HTTPConduit) client.getConduit()).getClient();
   policy.setConnectionTimeout(30000);
   policy.setReceiveTimeout(180000);

   // 以下就是具体调用方法,这里不写了,网上很多。

   ......

   好了,这样在服务端就能获取到用户名和密码,又不会污染webservice的方法参数。记下来防止遗忘,也为各位网友解决这种问题提供个思路,网上这种资料太少了。其中的部分代码是从项目代码里摘录出来的,可能有的地方有纰漏,但是思路脉络已经清除了,希望能帮到各位网友。另外,如果有需要了解JDK自带webservice如何使用用户名和密码来进行调用前的校验,可以参考我的一个例子:点击打开链接

                

你可能感兴趣的:(WebService)