Issues (44)

src/Jwt.php (8 issues)

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
Deprecated Code introduced by
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 ignore-deprecated  annotation

76
        /** @scrutinizer ignore-deprecated */ $this->builder->setIssuer($this->iss())

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.

Loading history...
Deprecated Code introduced by
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 ignore-deprecated  annotation

76
        /** @scrutinizer ignore-deprecated */ $this->builder->setIssuer($this->iss())

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.

Loading history...
Deprecated Code introduced by
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 ignore-deprecated  annotation

76
        /** @scrutinizer ignore-deprecated */ $this->builder->setIssuer($this->iss())

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.

Loading history...
Deprecated Code introduced by
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 ignore-deprecated  annotation

76
        /** @scrutinizer ignore-deprecated */ $this->builder->setIssuer($this->iss())

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.

Loading history...
Deprecated Code introduced by
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 ignore-deprecated  annotation

76
        /** @scrutinizer ignore-deprecated */ $this->builder->setIssuer($this->iss())

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.

Loading history...
Deprecated Code introduced by
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 ignore-deprecated  annotation

76
        /** @scrutinizer ignore-deprecated */ $this->builder->setIssuer($this->iss())

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.

Loading history...
Deprecated Code introduced by
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 ignore-deprecated  annotation

76
        /** @scrutinizer ignore-deprecated */ $this->builder->setIssuer($this->iss())

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.

Loading history...
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
Deprecated Code introduced by
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 ignore-deprecated  annotation

85
            /** @scrutinizer ignore-deprecated */ $this->builder->set($key, $claim);

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.

Loading history...
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