はじめに
最近プロジェクトで問題が発生しました。現在のユーザーが招待コードを友人と共有しています。友人が招待コードに基づいて新しいユーザーとして登録した後、彼は現在のユーザーの部下となり、特定の条件下で、下位レベルのユーザーから一連のリベートを得ることができます。ここで実装されているのは、現在のユーザーの 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( '32' => '0123456789ABCDEFGHJKMNPQRSTVWXYZ',//不含ILOU ); public function __construct($type='32') { $this->type = $type; $this->baseChar = self::$convertList[$type]; } /** * 公用方法,数字进行进制转换 * @param $num * @return string */ private function _idToString($num){ $str = ''; 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,'0',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 = ''; $str1 = $this->_idToString($num1); $str1 = strrev($str1); $str2 = $this->_idToString($num2); $str2 = strrev($str2); //4 补足 3 4位 U L return str_pad($str1,3,'U',STR_PAD_RIGHT).str_pad($str2,4,'L',STR_PAD_RIGHT); } /** * @desc im:三十二进制数转换成十机制数 * @param (string)$char 三十二进制数 * return 返回:十进制数 */ public function stringToId($str){ //1 清除 3 4 位补足位 $str1 = trim(substr($str,0,3),'U'); $str2 = trim(substr($str,3,4),'L'); $num1 = $this->_stringToId($str1); $num2 = $this->_stringToId($str2); //补位拼接 $str1 = str_pad($num1,4,'0',STR_PAD_LEFT); $str2 = str_pad($num2,6,'0',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],'0'); //减去 备用数值 $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 ''; } $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('85U43DM');
結果:
12
概要
もちろん、この最後の方法にも欠点があります。たとえば、暗号化された値を 2 つの num 値に分割する場合、使用される方法は非常に柔軟性がありません。復号化が変更されると、その変更に従います。私はここでアイデアを共有しているだけなので、皆さんは私を批判したり修正したりすることを歓迎します。
以上がPHP は 16 進数を使用して ID を暗号化および復号化しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。