V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
xuanbg
V2EX  ›  程序员

一个基于多次字典迭代的订单号加密方案(含代码)

  •  
  •   xuanbg ·
    xuanbg · 2018-09-22 22:09:58 +08:00 · 1800 次点击
    这是一个创建于 2256 天前的主题,其中的信息可能已经有所发展或是发生改变。

    一般订单号之类的业务编码,大概都由:固定字符、日期和流水号组成。如果第一个订单的流水号是 0001,第二个是 0002 这样子的话,很容易就被别人知道你的订单量。所以,就产生了流水号加密的需求。例如第一个订单是 3765,第二个是 1927,第三个是 0014,这样订单号就毫无规律可循,自然也就看不出来你今天到底有几个订单了。

    但流水号加密有几个问题:

    • 1、唯一性问题 订单号是需要保证唯一的,如果用随机数或者时间戳之类,理论上是不可能保证唯一性的。及时毫秒数加 4 位随机数,碰到就是这么挫,照样重复给你看。

    • 2、长度问题 如果可以不管长度,那么采用 UUID 或者自己搞个雪花算法也可以保证唯一性。但作为一个业务编码,长度超过 15 位就难以记忆和传递了。

    • 3、内含业务信息问题 譬如包含组织机构代码、包含业务发生日期、业务类型等等。 基于以上几个问题,订单号如果要满足 15 位以内(数字 /符号 /字母),并且要包含日期、业务类型等信息,估计流水号也就只能剩下 4-5 位了。5 位也有 10 万的容量,一天 10 万单的企业也是不多的,真的不够,增加一位就是 100 万单了。

    那么问题回到了如何加密这个流水号了。简单交换位置肯定不灵,一眼就看穿。但搞一个从 00000-99999 的集合随机取,取后删除。倒是效率很高,也足够乱序,但空间资源占用太高了,不是什么好方案。搞个大字典也能起到同样作用,但坏处也是一个样,太占空间。 那么,有什么办法可以减少空间的占用,同时也能达到加密数据的目的呢?让我们步步推进,先从一个两位数的加密开始。

    二位数的加密

    对于一个两位数而言,通过一个较小的字典就可以进行加密。譬如 00 转换为 73,01 转换为 36,02 转换为 91,只要一对一就可以实现数据的简单加密。在你不知道字典和输入的情况下,是不可能解密的。因为业务编码是服务端产生的,客户端是只知道输出不知道输入的,当然更没有这个密码本,所以客户端的信息不可能支持由密文反推明文,完美地实现了两位数的加密。

    三位数的加密

    那么三位数怎么办呢?把字典扩展到 1000 位么?显然不行,这样就会导致字典的无限扩张,直到你的内存无法承载为止。 这种情况我们可以通过可逆算法来实现加密而不必扩大字典。我们可以对低位先进行加密,譬如 000 可以用字典转换后两位变成 073,001 转换成 036。当然,止步于此的话,首位都是 0,容易被人一眼看穿。我们可以把首位 0 交换到末位,073 变成 730,然后对 30 再进行一次替换,这样 000 变成 768(000->073->730->768),001 变成 325(001->036->360-325),面目全非了吧。

    加密原理

    划重点:只要这个过程是可逆的,且替换次数一致,源数字和目标数字就永远保持一对一的关系。也就是说,不同的源数字不会被转换成相同的目标数字。简单地说,就是不会导致重复。这样,无论原数字有多少位,我们都可以通过位数减一次迭代,得到完全加密的目标数字。当然,利用更大的字典,我们就可以以更少的迭代次数来完成加密。

    源代码

    C#代码在此: https://github.com/xuanbg/WCF/blob/master/Redis/Generator.cs

    Java 代码虽然也有,但在公司的项目里面,不方便贴出来,大家自己翻译吧。

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3421 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 11:35 · PVG 19:35 · LAX 03:35 · JFK 06:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.