JAX-WS使用Handler实现简单的WebService权限验证

  WebService如果涉及到安全保密或者使用权限的时候,WS-Security通常是最优选择。WS-Security (Web服务安全)
包含了关于如何在WebService消息上保证完整性和机密性的规约,如何将签名和加密头加入SOAP消息。
不过WS-Security也有一些性能上的损耗,在信息保密要求不是很高的情况下,可以通过在SOAPHeader中添加简单的校验信息实现。
具体思路是客户端调用需要认证的服务时,在SOAPHeader中添加授权信息(如用户名、密码或者序列号等)。
服务端收到请求,在SOAPHeader中校验授权信息,校验通过则执行请求,校验不通过则返回错误提示。

客户端发起请求在SOAPHeader中添加的授权数据格式如下

<auth xmlns="http://schemas.xmlsoap.org/soap/actor/next">  
    <username>admin</username>  
    <password>admin</password>  
</auth>

服务端

服务端授权校验 Handler

import java.util.Iterator;
import java.util.Set;

import javax.xml.namespace.QName;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPConstants;
import javax.xml.soap.SOAPElement;
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 org.apache.cxf.interceptor.Fault;
import org.w3c.dom.NodeList;

/**
 *
 * @author 
 */
public class JaxServerAuthValidateHeader implements SOAPHandler<SOAPMessageContext> {

    @Override
    public void close(MessageContext context) {
    }

    @Override
    public boolean handleFault(SOAPMessageContext context) {
        return true;
    }

    @Override
    public boolean handleMessage(SOAPMessageContext context) {

        // 判断消息是输入还是输出
        boolean isRequest = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        SOAPMessage soapMessage = context.getMessage();

        if (!isRequest) {

            SOAPHeader soapHeader = null;

            try {
                SOAPEnvelope soapEnv = soapMessage.getSOAPPart().getEnvelope();
                soapHeader = soapEnv.getHeader();
            } catch (SOAPException e) {
                throw new Fault(new Exception("服务器异常!"));
            }

            if (soapHeader == null) {
                validateFail(soapMessage, "无   Soap Header 头信息!");
                return false;
            }

            // add an node named "auth"
            QName qname = new QName(SOAPConstants.URI_SOAP_ACTOR_NEXT, "auth");
            Iterator<?> iterator = soapHeader.getChildElements(qname);
            SOAPElement auth = null;
            if (iterator.hasNext()) {
                // 获取auth
                auth = (SOAPElement) iterator.next();
            }

            // 如果授权信息元素不存在,提示错误
            if (auth == null) {
                validateFail(soapMessage, "无授权信息!");
                return false;
            }

            NodeList nameList = auth.getElementsByTagName("username");
            NodeList pwdList = auth.getElementsByTagName("password");
            if (nameList == null || nameList.getLength() <= 0 || pwdList == null || pwdList.getLength() <= 0) {
                validateFail(soapMessage, "授权信息格式错误!");
                return false;
            }

            String username = nameList.item(0).getTextContent();
            String password = pwdList.item(0).getTextContent();
            if (!"admin".equals(username) || !"admin".equals(password)) {
                validateFail(soapMessage, "授权信息格式错误!");
                return false;
            }

        }

        System.out.println(isRequest ? "服务端响应:" : "服务端接收:");
        System.out.println("\r\n");

        return true;

    }

    @Override
    public Set<QName> getHeaders() {
        return null;
    }

    /**
     * 授权校验失败,在SOAPBody中添加SOAPFault
     * 
     * @param message
     */
    private void validateFail(SOAPMessage soapMessage, String faultString) {
        try {

            SOAPEnvelope envelop = soapMessage.getSOAPPart().getEnvelope();

            envelop.getHeader().detachNode();
            envelop.addHeader();

            envelop.getBody().detachNode();
            SOAPBody body = envelop.addBody();

            SOAPFault fault = body.getFault();

            if (fault == null) {
                fault = body.addFault();
            }

            fault.setFaultString(faultString);

            soapMessage.saveChanges();

        } catch (SOAPException e) {
            e.printStackTrace();
        }
    }
    
}

服务端Handler配置文件handler-chain.xml 

<?xml version="1.0" encoding="UTF-8"?>  
<javaee:handler-chains xmlns:javaee="http://java.sun.com/xml/ns/javaee"  
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">  
    <javaee:handler-chain>  
        <javaee:handler>  
            <javaee:handler-class>com.server.handler.JaxServerAuthValidateHeader</javaee:handler-class>  
        </javaee:handler>  
    </javaee:handler-chain>  
</javaee:handler-chains>  

服务端的Service中添加Handler配置文件 

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;


/**
 * This class was generated by Apache CXF 3.2.1
 * 2017-12-01T14:12:00.085+08:00
 * Generated source version: 3.2.1
 * 
 */
@WebService(targetNamespace = "http://tempuri.org/", name = "AExampleDemoWebService")
@HandlerChain(file="handler-chain.xml")
public interface AExampleDemoWebService {

    @WebMethod
    @WebResult(name = "single", targetNamespace = "")
    public java.lang.String querySingle(
        @WebParam(name = "single", targetNamespace = "http://tempuri.org/")
        java.lang.String single
    );
    
}

服务端的Service 实现类

import java.util.ArrayList;
import java.util.List;

import cn.evun.iwm.receive.soap.service.AExampleDemoWebService;
import cn.evun.iwm.receive.soap.struc.sample.InputParam;
import cn.evun.iwm.receive.soap.struc.sample.OutputParam;

@javax.jws.WebService(serviceName = "aexampleDemoWebService", portName = "Sample", 
    targetNamespace = "http://tempuri.org/", endpointInterface = "cn.soap.service.AExampleDemoWebService")
public class AExampleDemoWebServiceImpl implements AExampleDemoWebService {

    @Override
    public String querySingle(String input) {
        return "success";
    }

}

客户端

客户端添加授权Handler

import java.io.IOException;
import java.util.Set;

import javax.xml.namespace.QName;
import javax.xml.soap.SOAPConstants;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
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;

public class JaxWsClientHandler implements SOAPHandler<SOAPMessageContext> {

    @Override
    public boolean handleMessage(SOAPMessageContext context) {
        
        // 判断消息是请求还是响应
        Boolean isRequest = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        SOAPMessage soapMessage = context.getMessage();

        if (isRequest) {

            try {

                SOAPHeader soapHeader = soapMessage.getSOAPHeader();  
                if (soapHeader == null) {  
                    soapHeader = soapMessage.getSOAPPart().getEnvelope().addHeader();  
                }  

                // add an node named "auth"
                QName qname = new QName(SOAPConstants.URI_SOAP_ACTOR_NEXT, "auth");
                SOAPElement auth = soapHeader.addChildElement(qname);

                SOAPElement name = auth.addChildElement("username");
                name.addTextNode("admin");

                SOAPElement password = auth.addChildElement("password");
                password.addTextNode("admin");

                soapMessage.saveChanges();

                // tracking
                soapMessage.writeTo(System.out);

            } catch (SOAPException e) {
                System.err.println(e);
            } catch (IOException e) {
                System.err.println(e);
            }

        }

        return true;

    }

    @Override
    public boolean handleFault(SOAPMessageContext context) {
        return false;
    }

    @Override
    public void close(MessageContext context) {

    }

    @Override
    public Set<QName> getHeaders() {
        return null;
    }

}

客户端Handler配置文件handler-chain.xml

<?xml version="1.0" encoding="UTF-8"?>  
<javaee:handler-chains xmlns:javaee="http://java.sun.com/xml/ns/javaee"  
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">  
    <javaee:handler-chain>  
        <javaee:handler>  
            <javaee:handler-class>com.client.handler.JaxWsClientHandler</javaee:handler-class>  
        </javaee:handler>  
    </javaee:handler-chain>  
</javaee:handler-chains>  

客户端的Service中添加Handler配置文件

import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.WebEndpoint;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceFeature;

import javax.xml.ws.Service;

/**
 * This class was generated by Apache CXF 3.2.1
 * 2017-12-01T14:12:00.117+08:00
 * Generated source version: 3.2.1
 * 
 */
@WebServiceClient(name = "aexampleDemoWebService", 
                  wsdlLocation = "http://localhost:8090/service/Sample?wsdl",
                  targetNamespace = "http://tempuri.org/") 
@HandlerChain(file="handler-chain.xml")  
public class AexampleDemoWebServiceSoap extends Service {

    public final static URL WSDL_LOCATION;

    public final static QName SERVICE = new QName("http://tempuri.org/", "aexampleDemoWebService");
    public final static QName Sample = new QName("http://tempuri.org/", "Sample");
    static {
        URL url = null;
        try {
            url = new URL("http://localhost:8090/service/Sample?wsdl");
        } catch (MalformedURLException e) {
            java.util.logging.Logger.getLogger(AexampleDemoWebServiceSoap.class.getName())
                .log(java.util.logging.Level.INFO, 
                     "Can not initialize the default wsdl from {0}", "http://localhost:8090/service/Sample?wsdl");
        }
        WSDL_LOCATION = url;
    }

    public AexampleDemoWebServiceSoap(URL wsdlLocation) {
        super(wsdlLocation, SERVICE);
    }

    public AexampleDemoWebServiceSoap(URL wsdlLocation, QName serviceName) {
        super(wsdlLocation, serviceName);
    }

    public AexampleDemoWebServiceSoap() {
        super(WSDL_LOCATION, SERVICE);
    }
    
    public AexampleDemoWebServiceSoap(WebServiceFeature ... features) {
        super(WSDL_LOCATION, SERVICE, features);
    }

    public AexampleDemoWebServiceSoap(URL wsdlLocation, WebServiceFeature ... features) {
        super(wsdlLocation, SERVICE, features);
    }

    public AexampleDemoWebServiceSoap(URL wsdlLocation, QName serviceName, WebServiceFeature ... features) {
        super(wsdlLocation, serviceName, features);
    }    

    /**
     *
     * @return
     *     returns AExampleDemoWebService
     */
    @WebEndpoint(name = "Sample")
    public AExampleDemoWebService getSample() {
        return super.getPort(Sample, AExampleDemoWebService.class);
    }

    /**
     * 
     * @param features
     *     A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy.  
   *   Supported features not in the <code>features</code> parameter will have their default values. *
@return * returns AExampleDemoWebService */ @WebEndpoint(name = "Sample") public AExampleDemoWebService getSample(WebServiceFeature... features) { return super.getPort(Sample, AExampleDemoWebService.class, features); } }

客户端发起请求

QName SERVICE_NAME = new QName("http://tempuri.org/", "aexampleDemoWebService");
URL url = new URL("http://localhost:8090/service/Sample?wsdl");
AexampleDemoWebServiceSoap ss = new AexampleDemoWebServiceSoap(wsdlURL, SERVICE_NAME);
AExampleDemoWebService port = ss.getSample();
port.querySingle("1111");

@HandlerChain 注解 替代方式

  客户端  通过 HandlerReolver 代替 @HandlerChain 注解 导入 Handler 配置文件

  handler-chain配置文件对所有的请求都添加授权验证信息,有些时候不是所有的请求都需要添加授权验证,HandlerResolver提供了在编程时添加Handler的方法,可以用HandlerResolver给需要授权的接口添加Handler。

QName SERVICE_NAME = new QName("http://tempuri.org/", "aexampleDemoWebService");
URL url = new URL("http://localhost:8090/service/Sample?wsdl");
AexampleDemoWebServiceSoap ss = new AexampleDemoWebServiceSoap(wsdlURL, SERVICE_NAME);
//通过HandlerResolver添加Handler  
ss.setHandlerResolver(new HandlerResolver(){  
  
    @Override  
    @SuppressWarnings("rawtypes")             
    public List<Handler> getHandlerChain(PortInfo portInfo) {  
        List<Handler> handlerChain = new ArrayList<Handler>();  
        handlerChain.add(new JaxWsClientHandler());  
        return handlerChain;  
    }  
    
});
AExampleDemoWebService port = ss.getSample();
port.querySingle("2222");

服务端 @HandlerChain 注解替代 

  服务发布时服务端通过继承至 Endpoint 实现类 EndpointImpl 的 setHandlers 方法添加头部信息 代替 @HandlerChain 注解 导入 Handler 配置文件

import java.util.ArrayList;
import java.util.List;

import javax.xml.ws.Endpoint;
import javax.xml.ws.handler.Handler;

import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.interceptor.Interceptor;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.message.Message;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class CXFConfig {

    @Bean
    public ServletRegistrationBean dispatcherServlet() {
        return new ServletRegistrationBean(new CXFServlet(), "/service/*");
    }

    @Bean(name = Bus.DEFAULT_BUS_ID)
    public SpringBus springBus() {
        return new SpringBus();
    }

    @Bean
    public AExampleDemoWebServiceImpl aExampleDemoWebServiceImpl() {
        return new AExampleDemoWebServiceImpl();
    }

    @Bean
    public Endpoint endpointWebServiceSampleImpl() {
        
        EndpointImpl endpoint = new EndpointImpl(springBus(), aExampleDemoWebServiceImpl());

        // SOAPHandler 方式 
        @SuppressWarnings("rawtypes")
        List<Handler> handlers = new ArrayList<>();
        handlers.add(new JaxServerAuthValidateHeader());
        endpoint.setHandlers(handlers);
        
        endpoint.publish("/Sample");
        
        return endpoint;
        
    }

}

当使用替代方式添加头部信息的时候就不需要使用 @HandlerChain 注解 

posted @ 2017-12-01 16:34  如.若  阅读(1023)  评论(0编辑  收藏  举报