技术背景:引擎为Axis1.4,接口实现方式为wsdl文件生成服务端、客户端代码,请求消息为SOAP报文。
为了强化SOAP服务器对请求消息合法性的校验,我们采用两种安全性保护措施:1. SOAP请求消息增加Header头域令牌,要求包含用户名和用户密码;2. 实现请求消息预处理拦截机制,对请求消息的令牌进行校验,并验证请求消息IP地址的合法性。
为此,我们要做两件事情:一,如何将Header域加入到请求消息中,使之能够通过Axis引擎的解析;二,如何通过请求消息获得请求IP。
关于第一点,有两种方式实现。比较简单的实现,就是在所有的请求指令中,扩展对象,存储Header对象。这种方式比较笨拙,对原请求消息Body的影响较大。另外一种就是直接在Body之外统一增加节点Header。这种方式较为灵活,对元请求消息格式没有影响。
下面就说明实现请求Header域的操作步骤。
1. 在wsdl中定义Header域。
Header内容元素定义:
<element name="Security">
<complexType>
<sequence>
<element name="UserToken" type="wsse:UserToken" />
</sequence>
</complexType>
</element>
<complexType name="UserToken">
<sequence>
<element name="user" type="xsd:string" />
<element name="pass" type="xsd:string" />
</sequence>
</complexType>
Header定义
<wsdl:message name="AllRequestHeader">
<wsdl:part element="wsse:Security" name="request_header" />
</wsdl:message>
2. 将Header合入到每个请求的Operation定义中。
增加header之前:
<wsdl:operation name="createMyBusiness">
<wsdlsoap:operation soapAction="" />
<wsdl:input name="createMyBusinessRequest">
<wsdlsoap:body use="literal" />
</wsdl:input>
<wsdl:output name="createMyBusinessResponse">
<wsdlsoap:body use="literal" />
</wsdl:output>
</wsdl:operation>
增加header后:
<wsdl:operation name="createMyBusiness">
<wsdlsoap:operation soapAction="" />
<wsdl:input name="createMyBusinessRequest">
<wsdlsoap:header message="impl:AllRequestHeader"
part="request_header" use="literal" />
<wsdlsoap:body use="literal" />
</wsdl:input>
<wsdl:output name="createMyBusinessResponse">
<wsdlsoap:body use="literal" />
</wsdl:output>
</wsdl:operation>
3. 使用Axis自带的WSDL2Java工具,根据修改后的wsdl生成代码。
自此,SOAP请求消息就有了Header域,接下来的工作,就是如何拦截请求,统一校验Header和IP了。
实现请求消息头鉴权拦截器的WSDD配置(Axis1.4):1. 拦截校验类需要继承BasicHandler
2. 在server-config.wsdd下增加配置(请关注RequestValidate的配置):
<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<globalConfiguration>
<parameter name="sendMultiRefs" value="true" />
<parameter name="disablePrettyXML" value="true" />
<parameter name="adminPassword" value="admin" />
<parameter name="attachments.Directory" />
<parameter name="dotNetSoapEncFix" value="true" />
<parameter name="enableNamespacePrefixOptimization"
value="false" />
<parameter name="sendXMLDeclaration" value="true" />
<parameter name="attachments.implementation"
value="org.apache.axis.attachments.AttachmentsImpl" />
<parameter name="sendXsiTypes" value="true" />
<requestFlow>
<handler type="java:org.apache.axis.handlers.JWSHandler">
<parameter name="scope" value="session" />
</handler>
<handler type="java:org.apache.axis.handlers.JWSHandler">
<parameter name="scope" value="request" />
<parameter name="extension" value=".jwr" />
</handler>
</requestFlow>
</globalConfiguration>
<handler name="LocalResponder"
type="java:org.apache.axis.transport.local.LocalResponder" />
<handler name="URLMapper"
type="java:org.apache.axis.handlers.http.URLMapper" />
<handler name="Authenticate"
type="java:org.apache.axis.handlers.SimpleAuthenticationHandler" />
<!--请求消息拦截类定义-->
<handler name="RequestValidate"
type="java:com.validation.RequestValidate" />
<service name="AdminService" provider="java:MSG">
<parameter name="allowedMethods" value="AdminService" />
<parameter name="enableRemoteAdmin" value="false" />
<parameter name="className" value="org.apache.axis.utils.Admin" />
<namespace>http://xml.apache.org/axis/wsdd/</namespace>
</service>
<service name="Version" provider="java:RPC">
<parameter name="allowedMethods" value="getVersion" />
<parameter name="className" value="org.apache.axis.Version" />
</service>
<!-- Services from ImsService WSDL service -->
<service name="SoapService" provider="java:RPC" style="wrapped"use="literal">
<!--拦截请求消息流-->
<requestFlow>
<handler type="RequestValidate" />
</requestFlow>
<!--拦截响应消息流-->
<responseFlow>
<handler type="RequestValidate" />
</responseFlow>
...<!--其它具体接口指令-->
</service>
</deployment>
3. RequestValidate类具体实现。package com.validation;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPException;
import org.apache.axis.AxisFault;
import org.apache.axis.Message;
import org.apache.axis.MessageContext;
import org.apache.axis.handlers.BasicHandler;
import org.apache.axis.message.MessageElement;
import org.apache.axis.message.SOAPHeader;
import org.apache.axis.transport.http.HTTPConstants;
/**
* 头鉴权拦截器,需要继承BasicHandler
*/
public class RequestValidate
extends BasicHandler
{
private static final long serialVersionUID = 1L;
/**invoke
* @param mc mc
*/
public void invoke(MessageContext mc)
throws AxisFault
{
//获取Header
Message msg = mc.getRequestMessage();
SOAPHeader header = null;
try
{
header = (SOAPHeader) msg.getSOAPHeader();
}
catch (SOAPException e)
{
e.printStackTrace();
throw new AxisFault(new QName("20000"), "Header-node error!", null, null);
}
List<?> list = header.getChildren();
MessageElement token = (MessageElement) list.get(0);
list = token.getChildren();
MessageElement authenNode = (MessageElement) list.get(0);
list = authenNode.getChildren();
MessageElement nameNode = (MessageElement) list.get(0);
String userName = nameNode.getValue();//user节点
MessageElement pwdNode = (MessageElement) list.get(1);
String password = pwdNode.getValue();//pass节点
//对user和pass的校验省略
System.out.println("user:"+userName+",pass:"+password);
//获取SOAP请求的IP
HttpServletRequest request =
(HttpServletRequest) mc.getProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST);
String ipAddr = request.getRemoteAddr();
//对IP的校验省略
System.out.println("requestIP:" + ipAddr);
}
}
至此,SOAP请求的拦截器基本实现,我们可以在请求消息的Body之上,增加Header域:
<SOAP-ENV:Header>
<m:Security xmlns:m="http://wsse.email.soapservice.google.com">
<m:UserToken>
<m:user>myname</m:user>
<m:pass>mypass</m:pass>
</m:UserToken>
</m:Security>
</SOAP-ENV:Header>