PHP 增强 Token 前置验证可能性

从 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
{
/*
* 固定标识值
*
* @var string
*/
private $identify = 'Hongfs';

/*
* 打乱的 A-Za-z0-9
*
* @var string
*/
private $chars = 'zT7wjmivqRHSUkWcDn1Jd2XfAlK3rBC4apx6bVhNyF5o0eG8QgLZEY9PbtIuOMsg';

/*
* 生成 Token
*
* @return string
*/
public function generate() :string
{
return $this->get(
$this->rand(40)
);
}

/*
* 验证 Token
*
* @param string $value
* @return bool
*/
public function verify($value) :bool
{
return $this->get($value) === $value;
}

/*
* 编码 Token
*
* @param string $token
* @return string
*/
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 秒。

往上