本节内容概述
使用CXF拦截器实现在消息发送阶段添加额外的Soap头信息以及在接收阶段添加自定义的数据有效性校验拦截器。
本案例将实现的功能如下
- 在发送的Soap请求中加入soap:Header(默认情况下cxf并不会加入头信息,而且头信息不会影响到webservice的处理)。添加一套自定义的校验数据规则。
- 数据接收方,也就是Webservice的发布方可以通过添加拦截器在调用Webservice服务前进行头部的校验,从而保证数据的有效性。
以下提供更新安全的Webservice构想(菜鸟的思维)
上面的思路进行数据的加密。如果有人拦截到报文虽然无法伪造(因为我们做了头校验,当然也不是完全不能伪造,假设而已)但是他依然可以看懂报文内容,所以还应该将数据进行加密|解密,这样即使数据被拦截,他也不能理解我们传递的信息是什么。如果在加上一个用户认证功能也许就更perfect,这样我们的服务就可以细分的权限级别。
最终我们应该做到:数据被截获了,但是我们有头部校验啊(你伪造不了),我们数据有加密(你看不懂),我们有用户权限控制(你还要破解用户信息)。
三层防护,哈哈哈!!!
下面是头部校验代码一览
服务端:响应头包装,请求头解析
/**
* 响应头包装器【服务器端--发送】
* @author xbz
*
*/
public class WebserviceResponseHeaderWrapper extends AbstractPhaseInterceptor<SoapMessage> {
public WebserviceResponseHeaderWrapper(){
super(Phase.PREPARE_SEND);
}
@Override
public void handleMessage(SoapMessage soapMsg) throws Fault {
System.out.println("ID:"+getId()+"服务器端--响应头包装器");
List list =soapMsg.getHeaders();
SoapHeader responsetHeader=WebServiceHeaderUtil.createResponsetHeader();
list.add(0,responsetHeader);
}
}
/**
* 请求头解析器【服务器端--接收】
* @author xbz
*
*/
public class WebServiceRequestHeaderParser extends AbstractPhaseInterceptor<SoapMessage> {
public WebServiceRequestHeaderParser(){
super(Phase.PRE_INVOKE);
}
@Override
public void handleMessage(SoapMessage soapMsg) throws Fault {
System.out.println("服务器端--请求头解析器");
List<Header> headers = soapMsg.getHeaders();
Header header=null;
if(headers==null||headers.size()==0){
throw new Fault(new Exception("没有检查到请求头"));
}else{
header=headers.get(0);
}
Element element=(Element) header.getObject();
NodeList idNodeList = element.getElementsByTagName(WebServiceHeaderUtil.REQUEST_HEADER_TAGETNAME_ID);
NodeList timeNodeList = element.getElementsByTagName(WebServiceHeaderUtil.REQUEST_HEADER_TAGETNAME_TIME);
NodeList codeNodeList = element.getElementsByTagName(WebServiceHeaderUtil.REQUEST_HEADER_TAGETNAME_CODE);
if(idNodeList==null||idNodeList.getLength()==0){
throw new Fault(new Exception("没有找到"+WebServiceHeaderUtil.REQUEST_HEADER_TAGETNAME_ID));
}else if(timeNodeList==null||timeNodeList.getLength()==0){
throw new Fault(new Exception("没有找到"+WebServiceHeaderUtil.REQUEST_HEADER_TAGETNAME_TIME));
}else if(codeNodeList==null||codeNodeList.getLength()==0){
throw new Fault(new Exception("没有找到"+WebServiceHeaderUtil.REQUEST_HEADER_TAGETNAME_CODE));
}
String request_id=idNodeList.item(0).getTextContent();
String request_time=timeNodeList.item(0).getTextContent();
String request_code=codeNodeList.item(0).getTextContent();
boolean validateFlag = WebServiceHeaderUtil.validateHeader(request_id,request_time,request_code);
if(!validateFlag ){
throw new Fault(new Exception("请求头信息无效"));
}
System.out.println("=========================================================");
}
}
服务端配置cxf-servlet
<!--服务端发布 -->
<jaxws:endpoint id="helloWorld" implementor="com.ainoties.core.webservice.sample.impl.HelloCXFImpl" address="/HelloWorld">
<jaxws:inInterceptors>
<ref bean="loggingInInterceptor"/>
<ref bean="webserviceRequestHeaderParser"/>
</jaxws:inInterceptors>
<jaxws:outInterceptors>
<ref bean="loggingOutInterceptor"/>
<ref bean="webserviceResponseHeaderWrapper"/>
</jaxws:outInterceptors>
</jaxws:endpoint>
客户端:请求头包装,响应头解析
/**
* 请求头包装器【客户端--发送】
* @author Administrator
*
*/
public class WebServiceRequestHeaderWrapper extends AbstractPhaseInterceptor<SoapMessage> {
public WebServiceRequestHeaderWrapper(){
super(Phase.PREPARE_SEND);
}
@Override
public void handleMessage(SoapMessage soapMsg) throws Fault {
System.out.println("ID:"+getId()+"客户端-请求头包装器");
// QName wsdl_service=(QName) message.getContextualProperty(Message.WSDL_SERVICE);
// Object wsdl_port= message.getContextualProperty(Message.WSDL_PORT);
// Object wsdl_port= message.getContextualProperty(Message.PROTOCOL_HEADERS);
// QName q=new QName(namespaceURI, localPart, prefix);
// Assert.isInstanceOf(SoapMessage.class, message);
System.out.println("==========准备创建SoapHeader====================");
List list =soapMsg.getHeaders();
SoapHeader reuqestHeader=WebServiceHeaderUtil.createRquestHeader();
list.add(0,reuqestHeader);
}
}
/**
* 响应头包解析器【客户端--接收】
* @author xbz
*
*/
public class WebserviceResponseHeaderParser extends AbstractPhaseInterceptor<SoapMessage> {
public WebserviceResponseHeaderParser(){
super(Phase.PRE_INVOKE);
}
@Override
public void handleMessage(SoapMessage soapMsg) throws Fault {
System.out.println("客户端--响应头包解析器");
List<Header> headers = soapMsg.getHeaders();
Header header=null;
if(headers==null||headers.size()==0){
throw new Fault(new Exception("没有检查到头信息"));
}else{
header=headers.get(0);
}
Element element=(Element) header.getObject();
NodeList idNodeList = element.getElementsByTagName(WebServiceHeaderUtil.RESPONSE_HEADER_TAGETNAME_ID);
NodeList timeNodeList = element.getElementsByTagName(WebServiceHeaderUtil.RESPONSE_HEADER_TAGETNAME_TIME);
NodeList codeNodeList = element.getElementsByTagName(WebServiceHeaderUtil.RESPONSE_HEADER_TAGETNAME_CODE);
if(idNodeList==null||idNodeList.getLength()==0){
throw new Fault(new Exception("没有找到"+WebServiceHeaderUtil.RESPONSE_HEADER_TAGETNAME_ID));
}else if(timeNodeList==null||timeNodeList.getLength()==0){
throw new Fault(new Exception("没有找到"+WebServiceHeaderUtil.RESPONSE_HEADER_TAGETNAME_TIME));
}else if(codeNodeList==null||codeNodeList.getLength()==0){
throw new Fault(new Exception("没有找到"+WebServiceHeaderUtil.RESPONSE_HEADER_TAGETNAME_CODE));
}
String response_id=idNodeList.item(0).getTextContent();
String response_time=timeNodeList.item(0).getTextContent();
String response_code=codeNodeList.item(0).getTextContent();
boolean validateFlag = WebServiceHeaderUtil.validateHeader(response_id,response_time,response_code);
if(!validateFlag ){
throw new Fault(new Exception("响应头信息无效"));
}
System.out.println("=========================================================");
}
}
最重要的还有工具类,请使用DOMUtil(cxf旗下)进行xml元素操作
/**
* 创造WebService的SoapHeader,以及加密|解密头信息
* @author xbz
*
*/
public class WebServiceHeaderUtil {
public static DateFormat DATE_FORMAT=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
public static String DEFAULT_ENCODING="UTF-8";
public static int MAX_SALT=100;//最大盐值MAX_SALT-1
public static int DEFALUT_SALT=50;//当盐值为0时,取默认值
public static QName REQUEST_HEADER_QNAME=new QName("RequestHeader", "http://sechemas.xml.ainotest.com/RequestHeader/");
public static QName RESPONSE_HEADER_QNAME=new QName("ResponseHeader", "http://sechemas.xml.ainotest.com/ResponseHeader/");
/*--------------标签名----------------------------*/
public static String REQUEST_HEADER_TAGETNAME_ID="request_id";
public static String REQUEST_HEADER_TAGETNAME_TIME="request_time";
public static String REQUEST_HEADER_TAGETNAME_CODE="request_code";
public static String RESPONSE_HEADER_TAGETNAME_ID="response_id";
public static String RESPONSE_HEADER_TAGETNAME_TIME="response_time";
public static String RESPONSE_HEADER_TAGETNAME_CODE="response_code";
/**
* 创造请求头信息
* @return 返回构建好的xml
*/
public static SoapHeader createRquestHeader(){
SoapHeader header=null;
try{
String reqeustId=UUID.randomUUID().toString();
String requestTime=DATE_FORMAT.format(new Date());
String requestCode=createCode(reqeustId, requestTime);
Document doc = DOMUtils.createDocument();
Element root=doc.createElement("ainotes:RequestHeader");
root.setAttribute("xmlns:ainotes", "http://sechemas.xml.ainotest.com/RequestHeader/");
Element idEle=doc.createElement(REQUEST_HEADER_TAGETNAME_ID);
idEle.setTextContent(reqeustId);
Element timeEle=doc.createElement(REQUEST_HEADER_TAGETNAME_TIME);
timeEle.setTextContent(requestTime);
Element codeEle=doc.createElement(REQUEST_HEADER_TAGETNAME_CODE);
codeEle.setTextContent(requestCode);
root.appendChild(idEle);
root.appendChild(timeEle);
root.appendChild(codeEle);
header=new SoapHeader(REQUEST_HEADER_QNAME,root);
}catch(Exception e){
throw new RuntimeException(e);
}finally{
return header;
}
}
public static SoapHeader createResponsetHeader() {
SoapHeader header=null;
try{
String reqeustId=UUID.randomUUID().toString();
String reponseTime=DATE_FORMAT.format(new Date());
String reponseCode=createCode(reqeustId, reponseTime);
Document doc = DOMUtils.createDocument();
Element root=doc.createElement("ainotes:ReponseHeader");
root.setAttribute("xmlns:ainotes", "http://sechemas.xml.ainotest.com/ReponsetHeader/");
Element idEle=doc.createElement(RESPONSE_HEADER_TAGETNAME_ID);
idEle.setTextContent(reqeustId);
Element timeEle=doc.createElement(RESPONSE_HEADER_TAGETNAME_TIME);
timeEle.setTextContent(reponseTime);
Element codeEle=doc.createElement(RESPONSE_HEADER_TAGETNAME_CODE);
codeEle.setTextContent(reponseCode);
root.appendChild(idEle);
root.appendChild(timeEle);
root.appendChild(codeEle);
header=new SoapHeader(RESPONSE_HEADER_QNAME,root);
}catch(Exception e){
throw new RuntimeException(e);
}finally{
return header;
}
}
/**
* 根据id和time得到一个加密的code
* @param id
* @param time
* @return
*/
public static String createCode(String id,String time){
id=id.replace("-", "");
String requestCode=null;
try{
int salt = getSalt(time);
requestCode=encodeRequestId(id, salt);
}catch(Exception e){
throw new RuntimeException(e);
}finally{
return requestCode;
}
}
public static int getSalt(String requestTime) {
String s =requestTime.replaceAll("[\\.\\-\\s:]+", "");
int ss=Math.abs(s.hashCode());
int salt=ss%MAX_SALT;
if(salt==0){
salt=DEFALUT_SALT;
}
return salt;
}
public static String encodeRequestId(String requestId,int salt) {
String res=null;
try{
byte[] b=requestId.getBytes(DEFAULT_ENCODING);
int end=salt-1;
for (int i = 0; i < salt; i++) {
if(i!=end){
b=DigestUtils.md5Digest(b);
}else{
res=DigestUtils.md5DigestAsHex(b);
}
}
res=res.replaceAll("[\\D]+", "");
}catch(Exception e){
throw new RuntimeException(e);
}finally{
return res;
}
}
public static boolean validateHeader(String id, String time, String code) {
Assert.notNull(id,"id不能为Null");
Assert.notNull(time,"time不能为Null");
Assert.notNull(code,"code不能为Null");
return code.equals(createCode(id, time));
}
测试方法:
@Test
public void testCXFClientWithInterceptor() throws Exception{
JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
URL url= new URL("http://localhost:8080/cxf/webservice/HelloWorld?wsdl");
Client client = dcf.createClient(url);
//消息发送时使用WebServiceRequestHeaderWrapper封装消息头
client.getOutInterceptors().add(new WebServiceRequestHeaderWrapper());
client.getInInterceptors().add(new WebserviceResponseHeaderParser());
// client.getInInterceptors().add(new LoggingInInterceptor());
// client.getOutInterceptors().add(new LoggingOutInterceptor());
Object user= Thread.currentThread().getContextClassLoader().loadClass("com.ainoties.core.webservice.sample.User").newInstance();
Method method = BeanUtils.findDeclaredMethod(user.getClass(), "setUsername", String.class);
method.invoke(user, "xbz");
Object[] res = client.invoke("hiUser", user);
System.out.println(res[0]);
}