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; |
|||||||||||||||||
0 ignored issues
–
show
introduced
by
![]() |
|||||||||||||||||||
41 | |||||||||||||||||||
42 | 15 | public function __construct(App $app, Manager $manager, Builder $builder, User $user) |
|||||||||||||||||
43 | 15 | { |
|||||||||||||||||
44 | 15 | $this->app = $app; |
|||||||||||||||||
0 ignored issues
–
show
|
|||||||||||||||||||
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::setIssuedAt() 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::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. ![]() Are you sure the usage of
$this->iss() targeting xiaodi\JWTAuth\Jwt::iss() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() $this->iss() of type void is incompatible with the type string expected by parameter $issuer of Lcobucci\JWT\Builder::setIssuer() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() 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()) |
|||||||||||||||||
0 ignored issues
–
show
Are you sure the usage of
$this->aud() targeting xiaodi\JWTAuth\Jwt::aud() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() $this->aud() of type void is incompatible with the type string expected by parameter $audience of Lcobucci\JWT\Builder::setAudience() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||||||||||||||
78 | 10 | ->setId($uniqid, true) |
|||||||||||||||||
79 | ->setIssuedAt(time()) |
||||||||||||||||||
80 | 10 | ->setNotBefore(time() + $this->notBefore()) |
|||||||||||||||||
0 ignored issues
–
show
Are you sure the usage of
$this->notBefore() targeting xiaodi\JWTAuth\Jwt::notBefore() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||||||||||||||||||
81 | 10 | ->setExpiration(time() + $this->ttl()) |
|||||||||||||||||
0 ignored issues
–
show
Are you sure the usage of
$this->ttl() targeting xiaodi\JWTAuth\Jwt::ttl() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||||||||||||||||||
82 | ->set('refreshAt', time() + $this->refreshTTL()); |
||||||||||||||||||
0 ignored issues
–
show
Are you sure the usage of
$this->refreshTTL() targeting xiaodi\JWTAuth\Jwt::refreshTTL() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||||||||||||||||||
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()])) { |
|||||||||||||||||
0 ignored issues
–
show
Are you sure the usage of
$this->getUniqidKey() targeting xiaodi\JWTAuth\Jwt::getUniqidKey() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||||||||||||||||||
105 | 1 | throw new JWTException('用户唯一值·uniqidKey·未配置', 500); |
|||||||||||||||||
106 | } |
||||||||||||||||||
107 | |||||||||||||||||||
108 | 6 | return (string) $claims[$this->getUniqidKey()]; |
|||||||||||||||||
0 ignored issues
–
show
Are you sure the usage of
$this->getUniqidKey() targeting xiaodi\JWTAuth\Jwt::getUniqidKey() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||||||||||||||||||
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()); |
|||||||||||||||||
0 ignored issues
–
show
Are you sure the usage of
$this->getReloginCode() targeting xiaodi\JWTAuth\Jwt::getReloginCode() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||||||||||||||||||
223 | } |
||||||||||||||||||
224 | |||||||||||||||||||
225 | // 验证密钥是否与创建签名的密钥一致 |
||||||||||||||||||
226 | if (false === $this->token->verify($this->getSigner(), $this->makeSignerKey())) { |
||||||||||||||||||
0 ignored issues
–
show
$this->makeSignerKey() of type Lcobucci\JWT\Signer\Key is incompatible with the type string expected by parameter $key of Lcobucci\JWT\Token::verify() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||||||||||||||
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()); |
||||||||||||||||||
0 ignored issues
–
show
Are you sure the usage of
$this->getAlreadyCode() targeting xiaodi\JWTAuth\Jwt::getAlreadyCode() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||||||||||||||||||
240 | 1 | } |
|||||||||||||||||
241 | |||||||||||||||||||
242 | 1 | throw new TokenAlreadyEexpired('Token 刷新时间已过,请重新登录', $this->getReloginCode()); |
|||||||||||||||||
0 ignored issues
–
show
Are you sure the usage of
$this->getReloginCode() targeting xiaodi\JWTAuth\Jwt::getReloginCode() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||||||||||||||||||
243 | } |
||||||||||||||||||
244 | 1 | ||||||||||||||||||
245 | 1 | $data = new ValidationData(); |
|||||||||||||||||
246 | 1 | ||||||||||||||||||
247 | 1 | $jwt_id = $this->token->getHeader('jti'); |
|||||||||||||||||
248 | 1 | $data->setIssuer($this->iss()); |
|||||||||||||||||
0 ignored issues
–
show
Are you sure the usage of
$this->iss() targeting xiaodi\JWTAuth\Jwt::iss() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() $this->iss() of type void is incompatible with the type string expected by parameter $issuer of Lcobucci\JWT\ValidationData::setIssuer() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||||||||||||||
249 | 1 | $data->setAudience($this->aud()); |
|||||||||||||||||
0 ignored issues
–
show
Are you sure the usage of
$this->aud() targeting xiaodi\JWTAuth\Jwt::aud() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() $this->aud() of type void is incompatible with the type string expected by parameter $audience of Lcobucci\JWT\ValidationData::setAudience() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||||||||||||||
250 | $data->setId($jwt_id); |
||||||||||||||||||
251 | 1 | ||||||||||||||||||
252 | if (!$this->token->validate($data)) { |
||||||||||||||||||
253 | throw new JWTException('此 Token 效验不通过', 500); |
||||||||||||||||||
254 | } |
||||||||||||||||||
255 | } |
||||||||||||||||||
256 | } |
||||||||||||||||||
257 |