从 2022 开始,因为业务开始出现了高并发场景,为了增强业务的可用性,我们把鉴权的处理放到了 Redis ,但我们的令牌一直都是采用随机生成的字符串,每次验证都是要去 Redis 获取才行,如果有大量的假 Token 会对 Redis 进行冲击而引起总体业务的不可用。
为了处理这个痛点,我写了一个算法,我们在 Token 里面加入一个固定的标识值,然后进行一定的打乱,后面请求验证我们可以先验证 Token 是否可以进行还原,如果可以那就说明这个 Token 是我们生成的。
其实 JWT + sha1 ,抛弃掉时间的验证,也是可以实现的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| <?php
class Token {
private $identify = 'Hongfs';
private $chars = 'zT7wjmivqRHSUkWcDn1Jd2XfAlK3rBC4apx6bVhNyF5o0eG8QgLZEY9PbtIuOMsg';
public function generate() :string { return $this->get( $this->rand(40) ); }
public function verify($value) :bool { return $this->get($value) === $value; }
private function get(string $token) :string { $identify = $this->identify;
$salt = ord($token[0]) + ord($token[1]) + 3;
for ($i = 0; $i < strlen($identify); $i++) { $index = $this->toIndex($identify[$i]); $identify[$i] = $this->toStr(($index + $salt) % strlen($this->chars)); }
$index = $salt % (strlen($token) - 2 - strlen($identify));
for ($i = 0; $i < strlen($identify); $i++) { $token[$index + $i + 2] = $identify[$i]; }
return $token; }
private function toIndex($str): int { $value = strpos($this->chars, $str);
return $value === false ? 0 : $value; }
private function toStr($index): string { return $this->chars[$index] ?? ''; }
private function rand(int $length = 64): string { $length = ($length < 4) ? 4 : $length; return bin2hex(random_bytes(($length - ($length % 2)) / 2)); } }
|
最终进行了测试,测试内容为进行一次生成后进行验证,在 4H8G 服务器上,运行 10W 次耗时 0.8 秒。