ホームページ  >  記事  >  バックエンド開発  >  PHP は 16 進数を使用して ID を暗号化および復号化します

PHP は 16 進数を使用して ID を暗号化および復号化します

藏色散人
藏色散人転載
2019-11-18 14:38:433590ブラウズ

はじめに

最近プロジェクトで問題が発生しました。現在のユーザーが招待コードを友人と共有しています。友人が招待コードに基づいて新しいユーザーとして登録した後、彼は現在のユーザーの部下となり、特定の条件下で、下位レベルのユーザーから一連のリベートを得ることができます。ここで実装されているのは、現在のユーザーの ID に基づいて、逆に復号化できる暗号化された文字列を生成することです。絶え間ないテストと調整を経て、最終的な結果が得られました。例:

id = 12 code = 85U43DM

最初の実装

最初に次のようにコードを入力します:

/**
 * 加密解密用户邀请码,
 * @param unknown $string
 * @param string $action  encode|decode
 * @return string
 */
function endecodeUserId($string, $action = 'encode') {
    $startLen = 13;
    $endLen = 8;
    $coderes = '';
    #TOD 暂设定uid字符长度最大到9
    if ($action=='encode') {
        $uidlen = strlen($string);
        $salt = 'yourself_code';
        $codestr = $string.$salt;
        $encodestr = hash('md4', $codestr);
        $coderes = $uidlen.substr($encodestr, 5,$startLen-$uidlen).$string.substr($encodestr, -12,$endLen);
        $coderes = strtoupper($coderes);
    }elseif($action=='decode'){
        $strlen = strlen($string);
        $uidlen = $string[0];
        $coderes = substr($string, $startLen-$uidlen+1,$uidlen);
    }
    return  $coderes;
}

アイデアの紹介:

ソルト値を設定します。$salt と id が連結されて新しい文字列が形成されます。ソルト値は、後で招待コードのセキュリティ検証に使用できます。文字列に対して md4 暗号化を実行し (md4 は md5 よりも高速で安全性が低いことを考慮して)、 $encodestr を取得し、文字列を 2 つの部分 (最初の部分 $startLen 、13 個の文字列、2 番目の部分 $endLen 、8 個の文字列) に分割します。 $string (ここでは渡された ID) と $uidlen を文字列の前の部分に混ぜます。したがって、現在は id の最大長 9 のみをサポートしているため、$uidlen の長さは 1 となり、最終的に長さ 22 の文字列が得られます。

暗号化処理では、実際に ID の値と ID の長さを暗号化文字列に混合し、保存されている情報に基づいて対応する位置を見つけて ID を取得します。

ここでは、セキュリティに対する高度な要件はありませんが、プログラムの実行速度を上げるために、復号化中に検証は行われません。

テスト、ID の暗号化:

echo endecodeUserId(12);

出力結果:

23471DC2352712F34D6780

テスト、招待コードの復号化

echo endecodeUserId('23471DC2352712F34D6780','decode');

出力結果:

12

得られた結果は問題ないようですが、実際のテストでは問題が発生しました。これは一般ユーザーにも起こり得ることです。友人が WeChat 携帯電話に招待コードを送信し、それを使用して登録したいと考えていますしかし、携帯電話から招待コードをパソコンに転送する方法がわからない、または面倒だと感じています。このとき、招待コードをパソコンに手動で入力する必要があります。なんと、22 桁です。 、まだ大文字と数字が混在しているので、登録を諦めているのでしょう。

そのため調整を行い、7桁の招待コードに変更させていただきました。

もう一度調べてください

メソッドは記事を書く前にカプセル化されていますか、それとも最初にコードを直接書いたほうがよいでしょうか

<?php
class convert
{
    /**
     * 初始数字,自定义
     */
    const INIT_NUM = 123456789;
    /**
     * @var 进制的基本字符串
     */
    private $baseChar;
    /**
     * @var 进制类型
     */
    private $type;
    /**
     * @var array 各进制字符串列表
     */
    private static $convertList = array(
        &#39;32&#39; => &#39;0123456789ABCDEFGHJKMNPQRSTVWXYZ&#39;,//不含ILOU
    );
    public function __construct($type=&#39;32&#39;)
    {
        $this->type = $type;
        $this->baseChar = self::$convertList[$type];
    }
    /**
     * 公用方法,数字进行进制转换
     * @param $num
     * @return string
     */
    private function _idToString($num){
        $str = &#39;&#39;;
        while ($num!=0){
            $tmp = $num % $this->type;
            $str .= $this->baseChar[$tmp];
            $num = intval($num/$this->type);
        }
        return $str;
    }
    /**
     * @desc  im:十机制数转换成三十二进制数
     * @param (string)$char 三十二进制数
     * return 返回:十进制数
     */
    public function idToString($id){//10位内id 返回7位字母数字
        //数组 增加备用数值
        $id += self::INIT_NUM;
        //左补0 补齐10位
        $str = str_pad($id,10,&#39;0&#39;,STR_PAD_LEFT);
        //按位 拆分 4 6位(32进制 4 6位划分)
        $num1 = intval($str[0].$str[2].$str[6].$str[9]);
        $num2 = intval($str[1].$str[3].$str[4].$str[5].$str[7].$str[8]);
        $str1 = $str2 = &#39;&#39;;
        $str1 = $this->_idToString($num1);
        $str1 = strrev($str1);
        $str2 = $this->_idToString($num2);
        $str2 = strrev($str2);
        //4 补足 3 4位 U L
        return str_pad($str1,3,&#39;U&#39;,STR_PAD_RIGHT).str_pad($str2,4,&#39;L&#39;,STR_PAD_RIGHT);
    }
    /**
     * @desc  im:三十二进制数转换成十机制数
     * @param (string)$char 三十二进制数
     * return 返回:十进制数
     */
    public function stringToId($str){
        //1 清除 3 4 位补足位
        $str1 = trim(substr($str,0,3),&#39;U&#39;);
        $str2 = trim(substr($str,3,4),&#39;L&#39;);
        $num1 = $this->_stringToId($str1);
        $num2 = $this->_stringToId($str2);
        //补位拼接
        $str1 = str_pad($num1,4,&#39;0&#39;,STR_PAD_LEFT);
        $str2 = str_pad($num2,6,&#39;0&#39;,STR_PAD_LEFT);
        $id = ltrim($str1[0].$str2[0].$str1[1].$str2[1].$str2[2].$str2[3].$str1[2].$str2[4].$str2[5].$str1[3],&#39;0&#39;);
        //减去 备用数值
        $id -= self::INIT_NUM;
        return $id;
    }
    /**
     * 公用方法字符串转数字
     * @param $str
     * @return float|int|string
     */
    private function _stringToId($str){
        //转换为数组
        $charArr = array_flip(str_split($this->baseChar));
        $num = 0;
        for ($i=0;$i<=strlen($str)-1;$i++)
        {
            $linshi = substr($str,$i,1);
            if(!isset($charArr[$linshi])){
                return &#39;&#39;;
            }
            $num += $charArr[$linshi]*pow($this->type,strlen($str)-$i-1);
        }
        return $num;
    }
}

はじめにto idea

この手法は、長年のマスターの指導のもとに採用されました。 ID を固定長の 32 桁の文字列に変換し、独自のアルゴリズムを追加します。ここでは他の基数ではなく基数 32 が使用されるのはなぜですか? 32 桁システムには十分な英語文字を含めることができ、生成される暗号化文字列はより標準化されたように見えますが、一方で、認識しにくい一部の英語文字 (ここでは ILOU は除外します) が除外されるため、32 桁システムは36 ベースの代わりに使用されます。

暗号化処理、メソッド idToString() では、最初の id が比較的小さい場合、16 進数 32 に変換するときに 0 が多くなり、非常に不規則に見えることを考慮して、初期値 INIT_NUM が設定されます。 、これはカスタマイズできます。渡されたidに従って初期値を加算すると10桁の値が得られ、この値のインターバルビットが4桁の$num1と6桁の$num2に分割されます。 2 つの値は別々に変換されます。16 進数です。変換後、$num1 は長さ 3 の文字列を取得します。不足を補うために U を使用します。$num2 は長さ 4 の文字列を取得します。不足分を補うL。

復号化は逆の操作です。操作を逆にするだけです。

テスト: 生成

$obj = new convert(32);
$res1 = $obj->idToString(12);

結果:

85U43DM

復号化:

$obj = new convert(32);
$res1 = $obj->stringToId(&#39;85U43DM&#39;);

結果:

12

概要

もちろん、この最後の方法にも欠点があります。たとえば、暗号化された値を 2 つの num 値に分割する場合、使用される方法は非常に柔軟性がありません。復号化が変更されると、その変更に従います。私はここでアイデアを共有しているだけなので、皆さんは私を批判したり修正したりすることを歓迎します。

以上がPHP は 16 進数を使用して ID を暗号化および復号化しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。