1 | <?php |
||||||||||||||
2 | |||||||||||||||
3 | declare(strict_types=1); |
||||||||||||||
4 | |||||||||||||||
5 | namespace xiaodi\JWTAuth; |
||||||||||||||
6 | |||||||||||||||
7 | use Lcobucci\JWT\Builder; |
||||||||||||||
8 | use Lcobucci\JWT\Parser; |
||||||||||||||
9 | use Lcobucci\JWT\Token; |
||||||||||||||
10 | use Lcobucci\JWT\ValidationData; |
||||||||||||||
11 | use think\App; |
||||||||||||||
12 | use think\Model; |
||||||||||||||
13 | use xiaodi\JWTAuth\Exception\JWTException; |
||||||||||||||
14 | use xiaodi\JWTAuth\Exception\JWTInvalidArgumentException; |
||||||||||||||
15 | use xiaodi\JWTAuth\Exception\TokenAlreadyEexpired; |
||||||||||||||
16 | use xiaodi\JWTAuth\Handle\RequestToken; |
||||||||||||||
17 | |||||||||||||||
18 | class Jwt |
||||||||||||||
19 | { |
||||||||||||||
20 | /** |
||||||||||||||
21 | * @var User |
||||||||||||||
22 | */ |
||||||||||||||
23 | private $user; |
||||||||||||||
24 | |||||||||||||||
25 | /** |
||||||||||||||
26 | * @var Token |
||||||||||||||
27 | */ |
||||||||||||||
28 | private $token; |
||||||||||||||
29 | |||||||||||||||
30 | /** |
||||||||||||||
31 | * @var Manager |
||||||||||||||
32 | */ |
||||||||||||||
33 | private $manager; |
||||||||||||||
34 | |||||||||||||||
35 | /** |
||||||||||||||
36 | * @var Builder |
||||||||||||||
37 | 15 | */ |
|||||||||||||
38 | private $builder; |
||||||||||||||
39 | 15 | ||||||||||||||
40 | 15 | use \xiaodi\JWTAuth\Traits\Jwt; |
|||||||||||||
41 | |||||||||||||||
42 | 15 | public function __construct(App $app, Manager $manager, Builder $builder, User $user) |
|||||||||||||
43 | 15 | { |
|||||||||||||
44 | 15 | $this->app = $app; |
|||||||||||||
45 | $this->manager = $manager; |
||||||||||||||
46 | 15 | $this->builder = $builder; |
|||||||||||||
47 | $this->user = $user; |
||||||||||||||
48 | 15 | ||||||||||||||
49 | $config = $this->getConfig(); |
||||||||||||||
50 | 15 | foreach ($config as $key => $v) { |
|||||||||||||
51 | $this->$key = $v; |
||||||||||||||
52 | } |
||||||||||||||
53 | } |
||||||||||||||
54 | |||||||||||||||
55 | /** |
||||||||||||||
56 | * 获取jwt配置. |
||||||||||||||
57 | * |
||||||||||||||
58 | * @return array |
||||||||||||||
59 | */ |
||||||||||||||
60 | 12 | public function getConfig(): array |
|||||||||||||
61 | { |
||||||||||||||
62 | 12 | return $this->app->config->get('jwt.default', []); |
|||||||||||||
63 | 12 | } |
|||||||||||||
64 | |||||||||||||||
65 | /** |
||||||||||||||
66 | 12 | * 生成 Token. |
|||||||||||||
67 | 4 | * |
|||||||||||||
68 | * @param array $claims |
||||||||||||||
69 | 3 | * |
|||||||||||||
70 | 1 | * @return Token |
|||||||||||||
71 | */ |
||||||||||||||
72 | 2 | public function token(array $claims): Token |
|||||||||||||
73 | { |
||||||||||||||
74 | $uniqid = $this->makeTokenId($claims); |
||||||||||||||
75 | 10 | ||||||||||||||
76 | 10 | $this->builder->setIssuer($this->iss()) |
|||||||||||||
0 ignored issues
–
show
The function
Lcobucci\JWT\Builder::setIssuer() has been deprecated: This method will be removed on v4
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() The function
Lcobucci\JWT\Builder::setAudience() has been deprecated: This method will be removed on v4
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() The function
Lcobucci\JWT\Builder::setId() has been deprecated: This method will be removed on v4
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() The function
Lcobucci\JWT\Builder::setNotBefore() has been deprecated: This method will be removed on v4
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() The function
Lcobucci\JWT\Builder::setExpiration() has been deprecated: This method will be removed on v4
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() The function
Lcobucci\JWT\Builder::set() has been deprecated: This method will be removed on v4
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||||||||||||
77 | 10 | ->setAudience($this->aud()) |
|||||||||||||
78 | 10 | ->setId($uniqid, true) |
|||||||||||||
79 | ->setIssuedAt(time()) |
||||||||||||||
80 | 10 | ->setNotBefore(time() + $this->notBefore()) |
|||||||||||||
81 | 10 | ->setExpiration(time() + $this->ttl()) |
|||||||||||||
82 | ->set('refreshAt', time() + $this->refreshTTL()); |
||||||||||||||
83 | |||||||||||||||
84 | 10 | foreach ($claims as $key => $claim) { |
|||||||||||||
85 | $this->builder->set($key, $claim); |
||||||||||||||
0 ignored issues
–
show
The function
Lcobucci\JWT\Builder::set() has been deprecated: This method will be removed on v4
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||||||||||||
86 | 9 | } |
|||||||||||||
87 | 2 | ||||||||||||||
88 | $token = $this->builder->getToken($this->getSigner(), $this->makeSignerKey()); |
||||||||||||||
89 | |||||||||||||||
90 | 9 | $this->manager->login($token); |
|||||||||||||
91 | |||||||||||||||
92 | return $token; |
||||||||||||||
93 | } |
||||||||||||||
94 | |||||||||||||||
95 | /** |
||||||||||||||
96 | * 生成 Token ID. |
||||||||||||||
97 | * |
||||||||||||||
98 | * @param array $claims |
||||||||||||||
99 | * |
||||||||||||||
100 | 6 | * @return string |
|||||||||||||
101 | */ |
||||||||||||||
102 | private function makeTokenId(array $claims): string |
||||||||||||||
103 | 6 | { |
|||||||||||||
104 | 1 | if (empty($claims[$this->getUniqidKey()])) { |
|||||||||||||
105 | 1 | throw new JWTException('用户唯一值·uniqidKey·未配置', 500); |
|||||||||||||
106 | } |
||||||||||||||
107 | |||||||||||||||
108 | 6 | return (string) $claims[$this->getUniqidKey()]; |
|||||||||||||
109 | } |
||||||||||||||
110 | |||||||||||||||
111 | /** |
||||||||||||||
112 | * 获取 当前用户. |
||||||||||||||
113 | * |
||||||||||||||
114 | * @return Model |
||||||||||||||
115 | */ |
||||||||||||||
116 | public function user(): Model |
||||||||||||||
117 | { |
||||||||||||||
118 | return $this->user->get(); |
||||||||||||||
119 | } |
||||||||||||||
120 | |||||||||||||||
121 | /** |
||||||||||||||
122 | * 刷新 Token. |
||||||||||||||
123 | * |
||||||||||||||
124 | * @param Token $token |
||||||||||||||
125 | * |
||||||||||||||
126 | * @return Token |
||||||||||||||
127 | */ |
||||||||||||||
128 | public function refresh(Token $token = null): Token |
||||||||||||||
129 | { |
||||||||||||||
130 | $token = $token ?: $this->getRequestToken(); |
||||||||||||||
131 | |||||||||||||||
132 | $claims = $token->getClaims(); |
||||||||||||||
133 | |||||||||||||||
134 | unset($claims['iat']); |
||||||||||||||
135 | unset($claims['jti']); |
||||||||||||||
136 | unset($claims['nbf']); |
||||||||||||||
137 | unset($claims['exp']); |
||||||||||||||
138 | unset($claims['iss']); |
||||||||||||||
139 | unset($claims['aud']); |
||||||||||||||
140 | |||||||||||||||
141 | // 加入黑名单 |
||||||||||||||
142 | $this->manager->refresh($token); |
||||||||||||||
143 | |||||||||||||||
144 | return $this->token($claims); |
||||||||||||||
145 | } |
||||||||||||||
146 | |||||||||||||||
147 | /** |
||||||||||||||
148 | 5 | * 自动获取请求下的Token. |
|||||||||||||
149 | * |
||||||||||||||
150 | * @return Token |
||||||||||||||
151 | 5 | */ |
|||||||||||||
152 | protected function getRequestToken(): Token |
||||||||||||||
153 | { |
||||||||||||||
154 | $requestToken = new RequestToken($this->app); |
||||||||||||||
155 | |||||||||||||||
156 | 5 | $token = $requestToken->get($this->type()); |
|||||||||||||
157 | |||||||||||||||
158 | try { |
||||||||||||||
159 | 5 | $token = (new Parser())->parse($token); |
|||||||||||||
160 | } catch (\InvalidArgumentException $e) { |
||||||||||||||
161 | 3 | throw new JWTInvalidArgumentException('此 Token 解析失败', 500); |
|||||||||||||
162 | 1 | } |
|||||||||||||
163 | |||||||||||||||
164 | return $token; |
||||||||||||||
165 | } |
||||||||||||||
166 | 2 | ||||||||||||||
167 | 1 | /** |
|||||||||||||
168 | * 解析 Token. |
||||||||||||||
169 | 1 | * |
|||||||||||||
170 | * @return Token |
||||||||||||||
171 | 1 | */ |
|||||||||||||
172 | 1 | public function parseToken(): Token |
|||||||||||||
173 | 2 | { |
|||||||||||||
174 | $token = $this->getRequestToken(); |
||||||||||||||
175 | |||||||||||||||
176 | 3 | return $token; |
|||||||||||||
177 | } |
||||||||||||||
178 | |||||||||||||||
179 | /** |
||||||||||||||
180 | 2 | * 登出. |
|||||||||||||
181 | * |
||||||||||||||
182 | * @param Token $token |
||||||||||||||
183 | 5 | * |
|||||||||||||
184 | * @return void |
||||||||||||||
185 | */ |
||||||||||||||
186 | 5 | public function logout(Token $token = null) |
|||||||||||||
187 | 1 | { |
|||||||||||||
188 | $this->token = $token ?: $this->getRequestToken(); |
||||||||||||||
189 | |||||||||||||||
190 | $this->manager->logout($this->token); |
||||||||||||||
191 | 4 | } |
|||||||||||||
192 | 4 | ||||||||||||||
193 | 1 | /** |
|||||||||||||
194 | * 验证 Token. |
||||||||||||||
195 | 3 | * |
|||||||||||||
196 | * @param Token $token |
||||||||||||||
197 | * |
||||||||||||||
198 | * @return bool |
||||||||||||||
199 | */ |
||||||||||||||
200 | public function verify(Token $token = null) |
||||||||||||||
201 | { |
||||||||||||||
202 | $this->token = $token ?: $this->getRequestToken(); |
||||||||||||||
203 | |||||||||||||||
204 | try { |
||||||||||||||
205 | 2 | $this->validateToken(); |
|||||||||||||
206 | } catch (\BadMethodCallException $e) { |
||||||||||||||
207 | 2 | throw new JWTException('此 Token 未进行签名', 500); |
|||||||||||||
208 | 2 | } |
|||||||||||||
209 | |||||||||||||||
210 | 2 | return true; |
|||||||||||||
211 | 2 | } |
|||||||||||||
212 | |||||||||||||||
213 | /** |
||||||||||||||
214 | * 效验 Token. |
||||||||||||||
215 | * |
||||||||||||||
216 | * @return void |
||||||||||||||
217 | */ |
||||||||||||||
218 | protected function validateToken() |
||||||||||||||
219 | { |
||||||||||||||
220 | 1 | // 是否在黑名单 |
|||||||||||||
221 | if ($this->manager->hasBlacklist($this->token)) { |
||||||||||||||
222 | 1 | throw new TokenAlreadyEexpired('此 Token 已注销,请重新登录', $this->getReloginCode()); |
|||||||||||||
223 | } |
||||||||||||||
224 | |||||||||||||||
225 | // 验证密钥是否与创建签名的密钥一致 |
||||||||||||||
226 | if (false === $this->token->verify($this->getSigner(), $this->makeSignerKey())) { |
||||||||||||||
227 | throw new JWTException('此 Token 与 密钥不匹配', 500); |
||||||||||||||
228 | } |
||||||||||||||
229 | |||||||||||||||
230 | // 是否可用 |
||||||||||||||
231 | $exp = $this->token->getClaim('nbf'); |
||||||||||||||
232 | if (time() < $exp) { |
||||||||||||||
233 | throw new JWTException('此 Token 暂未可用', 500); |
||||||||||||||
234 | } |
||||||||||||||
235 | |||||||||||||||
236 | // 是否已过期 |
||||||||||||||
237 | if (true === $this->token->isExpired()) { |
||||||||||||||
238 | if (time() <= $this->token->getClaim('refreshAt')) { |
||||||||||||||
239 | throw new TokenAlreadyEexpired('Token 已过期,请重新刷新'.time().'-'.$this->token->getClaim('refreshAt'), $this->getAlreadyCode()); |
||||||||||||||
240 | 1 | } |
|||||||||||||
241 | |||||||||||||||
242 | 1 | throw new TokenAlreadyEexpired('Token 刷新时间已过,请重新登录', $this->getReloginCode()); |
|||||||||||||
243 | } |
||||||||||||||
244 | 1 | ||||||||||||||
245 | 1 | $data = new ValidationData(); |
|||||||||||||
246 | 1 | ||||||||||||||
247 | 1 | $jwt_id = $this->token->getHeader('jti'); |
|||||||||||||
248 | 1 | $data->setIssuer($this->iss()); |
|||||||||||||
249 | 1 | $data->setAudience($this->aud()); |
|||||||||||||
250 | $data->setId($jwt_id); |
||||||||||||||
251 | 1 | ||||||||||||||
252 | if (!$this->token->validate($data)) { |
||||||||||||||
253 | throw new JWTException('此 Token 效验不通过', 500); |
||||||||||||||
254 | } |
||||||||||||||
255 | } |
||||||||||||||
256 | } |
||||||||||||||
257 |
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.