技术分享 url中把业务id转为5位密文短链的算法

mathews · 2019-12-31 16:23:21 · 热度: 88

使用场景:

买家通过电商app下单后,会受到一条短信,短信内容中包括改订单详情页面的h5地址连接,因为是出现在短信中,所以对连接有要求:

  • 尽量短
  • 安全性考虑,订单在数据库中对应的自增主键id不能暴露出来
  • url中id加密串位数要固定

解决思路:

要满足第2条要求,肯定是要对id进行某种加密后来展现到url中,其实方法有很多,可以通过把10进制id转为高进制(比如36进制)串;也可以直接对id进行md5加密。但是转换高进制的方式会位数不固定,这样不符合第3个条件。直接md5加密太长不满足条件2。

鉴于此,要继续往下深入思考一下了

在搜索引擎如此发达的时代,如果什么事情都想着自己去原创,那样会显得太傻,于是,当你当前所能想出的方案不能解决问题时候,那就去度娘或者谷歌吧

于是去度娘敲下“微博短链”,深深可以借鉴,先粘上微博中url短链算法思路及代码如下:

  • 将长网址md5生成32位签名串,分为4段,每段8个字节;

  • 对这四段循环处理,取8个字节,将他看成16进制串与 0x3fffffff(30位1)与操作,即超过30位的忽略处理;

  • 这30位分成6段,每5位的数字作为字母表的索引取得特定字符,依次进行获得6位字符串;

  • 总的md5串可以获得4个6位串;取里面的任意一个就可作为这个长url的短url地址;

      function shorturl($input) {
      $base32 = array (
      'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
      'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
      'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
      'y', 'z', '0', '1', '2', '3', '4', '5'
      );
      $hex = md5($input);
      $hexLen = strlen($hex);
      $subHexLen = $hexLen / 8;
      $output = array();
      for ($i = 0; $i < $subHexLen; $i++) {
      $subHex = substr ($hex, $i * 8, 8);
      $int = 0x3FFFFFFF & (1 * ('0x'.$subHex));
      $out = '';
      for ($j = 0; $j < 6; $j++) {
          $val = 0x0000001F & $int;
          $out .= $base32[$val];
          $int = $int >> 5;
      }
      $output[] = $out;
      }
      return $output;
      }
    

看了之后,真是啧啧称赞,肯定就用这个思路了,但是需要稍稍改良一下,因为 这个加密后有6位,而我们要求5位,需要注意的事:要满足不同id加密后的唯一性(其实是尽量保持低碰撞率);他的短链可表示的连接数为32的6次方约等于10亿个,我们改成6位后至少也要可以表示这个量级。

废话少说,修改后的算法如下:

function shortUrl($id='', $salt='') {

$base64 = array (

'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
'y', 'z', '0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'A', 'B', 'C', 'D',
'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 
'U', 'V', 'W', 'X', 'Y', 'Z' 
);  
$hex = md5($id.$salt);

$hexLen = strlen($hex);

$subHexLen = $hexLen / 8;

$output = array();
for ($i = 0; $i < $subHexLen; $i++) {

    $subHex = substr ($hex, $i * 8, 8); 
    $int = 0x3FFFFFFF & (1 * ('0x'.$subHex));
    $out = ''; 
    for ($j = 0; $j < 5; $j++) {

        $val = 0x0000003F & $int;
        $val = $val % 62; 
        $out .= $base64[$val];
        $int = $int >> 6;
    }   
    $output[] = $out;
}
$in = 0x3 & (1 * ('0x'.substr($hex, 0, 1)));
return $output[$in];
}
共收到 1 条回复
老猫是我 #1 · 2019-12-31 16:27:15

hashids 了解一下 https://hashids.org/php/

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册