Web Service调用轻量级安全解决方案

来源:互联网 发布:ubuntu usr扩容 编辑:程序博客网 时间:2024/06/05 04:04

1       概论

在Web Service标准规范中,安全性主要通过Soap Header来保证Web Service的授权使用,通过SSL(CA认证方式)来保证传输层数据的加密,防止网络侦听(用SSL而不用WS-Security是因为性能,虽然WS-Security有着很多SSL所无法做到的特性:不基于传输层,保障非点对点的安全传输,部分加密等等)。

但以上两种方法还是比较“重”,对计算资源与网络带宽占用比较高,不太适合轻量级应用,特别是我们的应用运行在客户的安全内网,对于授权使用与数据加密要求不高,因此可以在初期采用比较简单的方式来实现授权使用与数据加密。

       在兼顾安全性,同时又不能占用太多系统资源的前提下,决定采用唯一会话ID的方式来解决问题。

       首先,所有调用客户端都要在参数中打包一个调用者对象,并根据其中内容得到唯一会话ID。服务端在收到调用者对象后,根据其中的内容,也计算一个唯一会话ID,如果与客户端发来的ID完全相同,则认为调用者是合法,参数也是完整的。

如有可能,则保存此调用者ID到缓存或者数据库中,以后每次调用都要先判断此调用者ID是否已经存在,如果已经存在,则视同重复调用,则服务端抛弃此调用申请,或者返回“重复调用”出错信息。

对于重要的参数,可以在双方约定一个加解密算法单独进行处理,以保证数据安全性。

2       Soap Header

 

 

一、创建web services 工程(XFire),和平时的一样。

二、加入身份验证功能

1、首先编写服务端验证类,继承AbstractHandler类

package test;

importorg.codehaus.xfire.MessageContext;

importorg.codehaus.xfire.handler.AbstractHandler;

import org.jdom.Element;

 

public classAuthenticationHandler extends AbstractHandler {

 

public voidinvoke(MessageContext cfx) throws Exception {

   if (cfx.getInMessage().getHeader() == null){

    throw neworg.codehaus.xfire.fault.XFireFault("请求必须包含验证信息",

     org.codehaus.xfire.fault.XFireFault.SENDER);

   }

   Element token =cfx.getInMessage().getHeader().getChild(

     "AuthenticationToken");

   if (token == null) {

    throw new org.codehaus.xfire.fault.XFireFault("请求必须包含身份验证信息",

     org.codehaus.xfire.fault.XFireFault.SENDER);

   }

 

   String username =token.getChild("Username").getValue();

   String password =token.getChild("Password").getValue();

   try {

    // 进行身份验证 ,只有abcd@1234的用户为授权用户

    if(username.equals("abcd") &&password.equals("1234"))

     // 这语句不显示

     System.out.println("身份验证通过");

    else

     throw new Exception();

   } catch (Exception e) {

    throw neworg.codehaus.xfire.fault.XFireFault("非法的用户名和密码",

      org.codehaus.xfire.fault.XFireFault.SENDER);

   }

}

}

2、Client构造授权信息

package test;

importorg.codehaus.xfire.MessageContext;

importorg.codehaus.xfire.handler.AbstractHandler;

import org.jdom.Element;

 

public classClientAuthenticationHandler extends AbstractHandler {

 

   private String username = null;

 

    private String password = null;

 

    public ClientAuthenticationHandler() {

    }

 

    public ClientAuthenticationHandler(Stringusername,String password) {

    this.username = username;

        this.password = password;

    }

 

    public void setUsername(String username) {

        this.username = username;

    }

 

    public void setPassword(String password) {

        this.password = password;

    }

 

    public void invoke(MessageContext context)throws Exception {

 

        //为SOAP Header构造验证信息

        Element el = newElement("header");

        context.getOutMessage().setHeader(el);

        Element auth = newElement("AuthenticationToken");

        Element username_el = newElement("Username");

        username_el.addContent(username);

        Element password_el = newElement("Password");

        password_el.addContent(password);

        auth.addContent(username_el);

        auth.addContent(password_el);

        el.addContent(auth);

    }

}

3、修改services.xml为web services绑定Handler

<?xmlversion="1.0" encoding="UTF-8"?>

<beansxmlns="http://xfire.codehaus.org/config/1.0">

<servicexmlns="http://xfire.codehaus.org/config/1.0">

   <name>Hello</name>

  <namespace>http://test/HelloService</namespace>

   <serviceClass>test.IHello</serviceClass>

  <implementationClass>test.HelloImpl</implementationClass>

   <inHandlers>

   <handler handlerClass="test.AuthenticationHandler" ></handler >

   </inHandlers>

   <style>wrapped</style>

   <use>literal</use>

   <scope>application</scope>

</service>

</beans>

4、新建一个类ClientTest,用来测试

package test;

importjava.lang.reflect.Proxy;

importjava.net.MalformedURLException;

importorg.codehaus.xfire.client.*;

importorg.codehaus.xfire.service.Service;

importorg.codehaus.xfire.service.binding.ObjectServiceFactory;

 

public class ClientTest {

 

/**

* @param args

*/

public static voidmain(String[] args) {

   // TODO Auto-generated method stub

   try {

    Service serviceModel = newObjectServiceFactory().create(IHello.class);

    IHello service = (IHello) newXFireProxyFactory().create(serviceModel,

       "http://dracom-d1514b82:8080/web_services3/services/Hello");  

    XFireProxy proxy =(XFireProxy)Proxy.getInvocationHandler(service);

    Client client = proxy.getClient();

    //发送授权信息

    client.addOutHandler(newClientAuthenticationHandler("abcd","1234"));

    //输出调用web services方法的返回信息

   System.out.println(service.getMessage("你好aaa"));

   } catch (MalformedURLException e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

   } catch (IllegalArgumentException e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

   }

}

}

三、这样我们就完成了编码,下面启动web services,运行客户端代码,本文为abcd@1234位授权用户,

使用abcd@1234,可以正常访问web services,如果用错误帐号,则会有以下异常:

Exception in thread"main" org.codehaus.xfire.XFireRuntimeException: Could not invokeservice.. Nested exception is org.codehaus.xfire.fault.XFireFault: 非法的用户名和密码

org.codehaus.xfire.fault.XFireFault:非法的用户名和密码

atorg.codehaus.xfire.fault.Soap11FaultSerializer.readMessage(Soap11FaultSerializer.java:31)

atorg.codehaus.xfire.fault.SoapFaultSerializer.readMessage(SoapFaultSerializer.java:28)

atorg.codehaus.xfire.soap.handler.ReadHeadersHandler.checkForFault(ReadHeadersHandler.java:111)

atorg.codehaus.xfire.soap.handler.ReadHeadersHandler.invoke(ReadHeadersHandler.java:67)

atorg.codehaus.xfire.handler.HandlerPipeline.invoke(HandlerPipeline.java:131)

atorg.codehaus.xfire.client.Client.onReceive(Client.java:406)

atorg.codehaus.xfire.transport.http.HttpChannel.sendViaClient(HttpChannel.java:139)

atorg.codehaus.xfire.transport.http.HttpChannel.send(HttpChannel.java:48)

atorg.codehaus.xfire.handler.OutMessageSender.invoke(OutMessageSender.java:26)

atorg.codehaus.xfire.handler.HandlerPipeline.invoke(HandlerPipeline.java:131)

at org.codehaus.xfire.client.Invocation.invoke(Invocation.java:79)

atorg.codehaus.xfire.client.Invocation.invoke(Invocation.java:114)

atorg.codehaus.xfire.client.Client.invoke(Client.java:336)

atorg.codehaus.xfire.client.XFireProxy.handleRequest(XFireProxy.java:77)

atorg.codehaus.xfire.client.XFireProxy.invoke(XFireProxy.java:57)

at$Proxy0.getMessage(Unknown Source)

attest.ClientTest.main(ClientTest.java:24)

 

如果不在CientTest加以下Heade则会有以下异常:

    XFireProxy proxy =(XFireProxy)Proxy.getInvocationHandler(service);

    Client client = proxy.getClient();

    //发送授权信息

    client.addOutHandler(newClientAuthenticationHandler("abcd1","1234"));

 

信息: Fault occurred!

org.codehaus.xfire.fault.XFireFault:请求必须包含验证信息

attest.AuthenticationHandler.invoke(AuthenticationHandler.java:11)

atorg.codehaus.xfire.handler.HandlerPipeline.invoke(HandlerPipeline.java:131)

atorg.codehaus.xfire.transport.DefaultEndpoint.onReceive(DefaultEndpoint.java:64)

atorg.codehaus.xfire.transport.AbstractChannel.receive(AbstractChannel.java:38)

atorg.codehaus.xfire.transport.http.XFireServletController.invoke(XFireServletController.java:304)

atorg.codehaus.xfire.transport.http.XFireServletController.doService(XFireServletController.java:129)

atorg.codehaus.xfire.transport.http.XFireServlet.doPost(XFireServlet.java:116)

atjavax.servlet.http.HttpServlet.service(HttpServlet.java:710)

atjavax.servlet.http.HttpServlet.service(HttpServlet.java:803)

atorg.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)

atorg.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)

atorg.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230)

atorg.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)

atorg.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)

atorg.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104)

atorg.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)

atorg.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:261)

atorg.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)

atorg.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:581)

atorg.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)

atjava.lang.Thread.run(Thread.java:619)

3      SSL

SSL(SecureSocket Layer)是一种通信交互协议,由Netscape公司在1994年制定,主要目的就是确保在web 服务器和浏览器之间数据传输安全。通过简单设置应用服务器与客户端即可实现。 

 

4       唯一会话ID方式

4.1  接口调用者对象:Invoker

Invoker对象唯一标识一个客户端对财政非税征管系统服务端接口方法的一次调用。

名称

类型

说明

是否可空

sessionId

String

唯一会话ID, MD5(时间戳+年度+区划编码+单位编码+用户编码+用户密码)

timestamp

String

时间戳,为18位数字组成的字符串,前13位为发起请求时间的13位long类型值(1970年1月1日起直至当前时间的以毫秒为单位的时间间隔的值),后5位为随机数。

sysYear

String

年度

districtCode

String

区划编码

districtName

String

区划名称

unitCode

String

单位编码

unitName

String

单位名称

userCode

String

用户编码

userName

String

用户名称

4.2   唯一会话ID:sessionId

客户端调用Web Service接口时动态生成,只使用一次,用以唯一标识调用者身份,并防止由于网络或者软硬件故障造成的重复调用。

sessionId=MD5(时间戳+年度+区划编码+单位编码+用户编码+用户密码)。

参数说明:

4.2.1 时间戳

为18位数字组成的字符串,前13位为发起请求或应答时间的13位long类型值(1970年1月1日起直至当前时间的以毫秒为单位的时间间隔的值),后5位为随机数。

4.2.2 年度

为客户端登录时选择的系统年度。

4.2.3 区划编码

为客户端登录时选择的区划编码值。

4.2.4 单位编码

为客户端登录时选择的收费单位值。

4.2.5 用户编码

为客户端登录时选择的用户编码值。

4.2.6 用户密码

为客户端登录时选择的用户密码值。

4.2.7 其他参数

    如果要进一步加强安全性,双方可以约定在唯一会话ID中加入调用参数完整性、安全性的校验,即在sessionId的MD5中加入此接口要求的其他参数值进行一并计算。

4.3  MD5算法

    MD5算法如下(包括Java与C#):

4.3.1 Java

//Java MD5算法

private staticString[] HexCode ={ "0", "1", "2", "3","4", "5", "6", "7", "8","9", "A", "B", "C", "D","E", "F" }; 

    

     public static String MD5(String s){ 

        MessageDigest  md = null; 

        try { 

            md =MessageDigest.getInstance("MD5"); 

        } catch (Exception e) { 

           e.printStackTrace();

           return null;

        } 

        md.update(s.getBytes()); 

        returnbyteArrayToHexString(md.digest()); 

     } 

    

private  static StringbyteToHexString(byte b) { 

            int n = b; 

            if (n < 0) { 

                n = 256 + n; 

            } 

            int d1 = n / 16; 

             int d2 = n % 16; 

            return HexCode[d1] + HexCode[d2]; 

     } 

      

     private  static String byteArrayToHexString(byte[] b){ 

            String result = ""; 

            for (int i = 0; i < b.length; i++) { 

                result = result + byteToHexString(b[i]); 

            } 

            return result; 

     } 

 

4.3.2 C#

//C# MD5算法

private staticstring[] HexCode ={ "0", "1", "2", "3","4", "5", "6", "7", "8","9", "A", "B", "C", "D","E", "F" }; 

 

public staticstring MD5(string s) 

            MD5 md = newMD5CryptoServiceProvider(); 

            byte[] ss =md.ComputeHash(UnicodeEncoding.UTF8.GetBytes(s)); 

            return byteArrayToHexString(ss); 

 

private  static string byteToHexString(byte b) { 

        int n = b; 

        if (n < 0) { 

            n = 256 + n; 

        } 

        int d1 = n / 16; 

        int d2 = n % 16; 

        return HexCode[d1] + HexCode[d2]; 

 

private  static String byteArrayToHexString(byte[] b){ 

        String result = ""; 

        for (int i = 0; i < b.Length; i++){ 

            result = result +byteToHexString(b[i]); 

        } 

        return result; 

 

原创粉丝点击