代码改变世界

在线支付平台设计之Order.do (中)

2009-01-10 01:53  Kevin Zhou  阅读(806)  评论(1编辑  收藏  举报

上节“在线支付平台设计之Order.do (上)”中,我对Order.do的设计思想进行了初步分析,我也觉得对于Order.do来讲我谈到的内容还是很少,毕竟我的最终的目标还是希望大家能够通过我谈到到这些内容能设计一个简单的支付接口。

因为上节已经谈到Order.do的设计思想,所以这节我主要谈怎么实现,原理部分我就不在这里赘述了。

一:为什么是Order.do?

谈到Order.do,其实asp.net程序员都知道再.net下实现.do不是什么困难的事情,只需要再项目里引用URLRewriter就可以了,这里主要基于两方面的考虑:

1)Order.do掩盖了真实的.aspx扩展名,在安全性上加了一层“套”(当然这样并不能掩盖完全aspx,经过稍微检测还是可以发现真正的脚本语言~~)

2)由于URLRewriter再url重写方面对效率的影响几乎可以忽略不计,又能在安全性再加一层套,不用白不用(~~)。

3)附加谈一下URLRewriter的用法:

首先下载dll: download.microsoft.com/download/0/4/6/0463611e-a3f9-490d-a08c-877a83b797cf/MSDNURLRewriting.msi

安装并在所需要的项目里引用URLRewriter.dll

然后在web.config里配置如下

  <configSections>
    <section name="RewriterConfig" type="URLRewriter.Config.RewriterConfigSerializerSectionHandler, URLRewriter"/>    
  </configSections>

 

<RewriterConfig>
  <Rules>
    <RewriterRule>
      <LookFor>Order.do</LookFor>
      <SendTo>Order.aspx</SendTo>
    </RewriterRule>   
  </Rules>
</RewriterConfig>

 

    <httpHandlers>
      <add verb="*" path="*.do" type="URLRewriter.RewriterFactoryHandler, URLRewriter" />
    </httpHandlers>

 

然后在网站打开后访问Order.do,执行的即是Order.aspx的代码

二:接受参数是用Get、Post或者双兼容?

首先我们看一下他们之间的区别:

  1. get是从服务器上获取数据,post是向服务器传送数据。
  2. get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到。post是通过HTTP post机制,将表单内各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到这个过程。
  3. 对于get方式,服务器端用Request.QueryString获取变量的值,对于post方式,服务器端用Request.Form获取提交的数据。
  4. get传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。但理论上,IIS4中最大量为80KB,IIS5中为100KB。
  5. get安全性非常低,post安全性较高。

看到get与post的区别,最后还是决定用双兼容的方式来接受数据, asp.net提供的方式是:

object obj = Request["partner_no"];

这种方式,不管对方是htm,还是jsp,抑或asp,即使php,我们都是可以兼容get与post两种方式,完善代码如下:

这里添加一个函数SaftRequest,其作用是当Request的结果为NULL的时候,返回String.Empty,否则我们直接obj.ToString()可能会报异常.

private string SafeRequest(object obj)
{
    try
    {
        return obj.ToString();
    }
    catch (Exception ex)
    {   //Log.Write(ex.GetType + ex.Message);
        return String.Empty;
    }
}

 

那么我们的Request就可以改为

string partnerNo = SafeRequest(Request["partner_no"]);

 

 三:加密需要多兼容?
 
不得不说,现在很多支付网关也好,银行也好,有些会在接口开发文档有这样一句话:“本接口暂时只支持MD5加密方式”。我想既然做支付平台,
这里我们假设是支付终端(对于支付网关多一个跳转到支付终端,然后接受支付终端的结果,最后再返回给支付方的过程),当然要再数据安全的基础上,
还要考虑平台兼容性,现在主流平台也就无非asp,asp.net,jsp,php,所以再斟酌加密类型要考虑是否各个平台是否都能实现。这里我设计的时候,
要是以MD5为主,其他的为预留。
加密:
using System.Security.Cryptography;
public abstract class Encryption
{
    public abstract string Encrypt(string oriData);
}
public class MD5Encryption : Encryption
{
    //MD5 Hash
    public override string Encrypt(string oriData)
    {
        MD5 md5 = MD5.Create();
        byte[] data = md5.ComputeHash(Encoding.Default.GetBytes(oriData));

        StringBuilder sBuilder = new StringBuilder();
        foreach (byte pd in data)
        {
            sBuilder.Append(pd.ToString("x2"));
        }
        return sBuilder.ToString();
    }
}
public class RSAEncryption : Encryption
{

    public override string Encrypt(string oriData)
    {
        //reference: ms-help://MS.MSDNQTR.v90.chs/fxref_mscorlib
        //                   /html/dcb50ce1-fd47-b47f-be48-d716093c1359.htm            
        return String.Empty;
    }
}
 

实例化

public class EncryptFactory
    {
        static Encryption encryption;

        public EncryptFactory() { }

        //Instance Factory
        public static Encryption GetEncryptInstance(string encryptType)
        {
            if (String.Compare(encryptType, "MD5", true) == 0)
            { encryption = new MD5Encryption(); }
            else if (String.Compare(encryptType, "RSA", true) == 0)
            { encryption = new RSAEncryption(); }
            else
            { encryption = null; }

            return encryption;
        }
    }

 

用法:

 protected void Page_Load(object sender, EventArgs e)
 {
     string orderNo = SafeRequest(Request["order_no"]);
     string money = SafeRequest(Request["money"]);
     string orderDate = SafeRequest(Request["order_date"]);
      string signType = SafeRequest(Request["sign_type"]);
    //...
     //参数验证(参看:四,参数合法性该如何验证?)
    string oriData = ParamsHandle(orderNo, money, orderDate, signType
        //,这里我们只需要其Bubble Sort部分
        )
        //ParamsHandle 函数在: http://www.cnblogs.com/enquan/archive/2008/12/26/1363168.html

    Encryption encryption = EncryptFactory.GetEncryptInstance(signType);
    string encryData = encryption.Encrypt(orderDate);
    
    if(String.Compare(encryData, validateKey) == 0)
    {
        //进行下一步操作
    }
}

 

四:参数合法性该如何验证?

谈到参数验证,我们都知道微软再.net framework都提供了一系列的验证方法,比如 转整形 int.TryParse(), 字符串长度验证 string.Length > X,字符串是否包含某个特殊字符,等等 string.IndexOf(“<”) >= 0 等等,但是试想一下我们如果这样验证也许就会掉入十分复杂的逻辑验证深渊,还可能出现验证漏洞,那么这里推荐大家用正则表达。

验证之前,先来细看接收参数都有什么:

1. 首先有html标签要么转化,要么直接kill掉

2. 长度是否符合我们约定的标准

3. 价格、数量等参数是否可以转整形、日期型是否为合法日期

加入我们约定在接受字符串时,仅仅允许字符、数字、下划线。示例代码如下:

 

System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(@"^\w+$");
if (regex.IsMatch(currencyType))
{
    //do next...
}

 

本节先到这里~~