作为一个产品君,其实写这种东西心里是没底的。
事情起因是这样,因为项目需求,技术那边需要写一套算法,生成唯一的二维码,用户扫描二维码获得积分,而且只能使用一次。
——————–如果你想知道最终结果,请直接拉到文章末尾—————-
然后技术用了php7的随机算法,加上哈希和时间戳的,但是有一个很重要的问题是:如果在1ms内生成多个,由于随机算法的原因,有可能出现重复。
最近刚好在看rsa算法,于是想到了通过rsa算法来加密,当然,为了生成的二维码具有可辨识度,使用了不怎么安全的512位算法。
而且为了不跟url冲突,需要对加密后的字符串进行urlcode编码,这样大概的长度是95位。
所以这个方案一的执行结果是:
第一步:生成唯一ID并制定好规则
商品ID(4位)+8位纯数字(唯一) 【生成的时候使用sqlite3 数据库进行自增,以保证生成的字符串唯一】
第二步:使用公钥加密并且转化成urlcode编码
第三步:生成网址二维码。
http://aaa.com/code/这里是urlcode编码
第四步:服务端使用私钥解密,并且验证是否符合第一步的规则。
并且在数据库里做记录,防止重复验证。
(这里可以设定失效日期,减少数据库的占用和改善查询性能。定期做清理。数据库使用postgresql,查询性能会比mysql好一些)
方案的好处当然是拥有一对密钥,即使丢失一个,影响也不是很大。
问题也存在:第一就是RSA加密解密性能问题,第二就是数据库查询问题。
这两个基本上决定了性能不可能快。
于是想到了直接用hash值加密,全部存在数据库中,但是这样的话需要一行行比对,而且在字符串比较长的情况下,很耗费时间。能不能有一个明文的ID进行索引呢?
后来突然想到网站的用户名密码方案。
这里先说一下网站用户名密码的加密方案。
密码先通过JS的hash加密,传送给服务端,服务端再进行一次加密,然后比对数据库里面的已加密值,由于整个过程中是密文传输(数据库里也只存储密文),且hash是不可逆的。所以是很安全的算法。
那么现在将用户名看作是唯一ID,密码hash的值看作是密钥,由于hash是不可逆的,所以不存在解密的可能性。
那现在的方案是:
第一步:确定唯一的二维码ID(明文)
商品ID(4位)+8位纯数字(唯一) 【生成的时候使用sqlite3 数据库进行自增,以保证生成的字符串唯一】
第二步:随机生成一个哈希值(当然这个要存在数据库里了)
第三步:生成二维码
http://aaa.com/code/123412345678/这里是哈希值
1234是商品ID,12345678是唯一ID。
第四步:验证,以ID为索引,查找对应的值是否正确。
当然注意,验证过的可以删掉或者标记为已使用。
上述商品ID和8位数字要占用掉12位,而且看起来也不是很安全,那么我们可以通过这个工具,进行加密的同时,缩短它的位数:http://hashids.org/php/
1234,12345678 可以缩成:ekNs4zWJM (9位加密字符串)
当然,其实也可以不用到hash,直接用PHP随机算法生成。