• 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("
    ");
    
            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 注解 

  • 相关阅读:
    Java 中的悲观锁和乐观锁的实现
    乐观锁和悲观锁的区别
    理解RESTful架构
    修复Linux下curl等无法使用 Let's Encrypt 证书
    呕心沥血 AR VR 好资源分享
    linux服务器出现大量TIME_WAIT的解决方法
    Ubuntu系统 无法删除 redis-server
    Python Flask jsonify a Decimal Error
    微信小程序 订阅消息 对接详细记录
    FTP时显示500 Illegal PORT command的解决
  • 原文地址:https://www.cnblogs.com/rinack/p/7943164.html
Copyright © 2020-2023  润新知