44
55namespace xiaodi \JWTAuth \Service ;
66
7+ use DateTimeZone ;
8+ use DateTimeImmutable ;
79use think \App ;
8- use Lcobucci \JWT \Builder ;
9- use Lcobucci \JWT \Token as JwtToken ;
10- use Lcobucci \JWT \ValidationData ;
11- use Lcobucci \JWT \Parser ;
1210use xiaodi \JWTAuth \Config \Token as Config ;
13- use xiaodi \JWTAuth \Exception \JWTException ;
14- use xiaodi \JWTAuth \Exception \JWTInvalidArgumentException ;
15- use xiaodi \JWTAuth \Exception \TokenAlreadyEexpired ;
1611use xiaodi \JWTAuth \Handle \RequestToken ;
12+ use Lcobucci \JWT \Configuration ;
13+ use Lcobucci \JWT \Token as JwtToken ;
14+ use Lcobucci \JWT \Signer \Key \InMemory ;
15+ use Lcobucci \JWT \Validation \Constraint \SignedWith ;
16+ use Lcobucci \JWT \Validation \Constraint \ValidAt ;
17+ use Lcobucci \Clock \SystemClock ;
1718
1819/**
19- * Undocumented class
2020 *
2121 * @method JwtToken make()
2222 * @method bool verify()
@@ -41,6 +41,11 @@ class Token
4141 */
4242 protected $ token ;
4343
44+ /**
45+ * @var Configuration
46+ */
47+ private $ jwtConfiguration ;
48+
4449 public function __construct (App $ app )
4550 {
4651 $ this ->app = $ app ;
@@ -49,107 +54,59 @@ public function __construct(App $app)
4954
5055 protected function init ()
5156 {
52- $ options = $ this ->resolveConfig ();
57+ $ this ->resolveConfig ();
58+ $ this ->initJwtConfiguration ();
59+ }
5360
54- $ this ->config = new Config ($ options );
61+ protected function initJwtConfiguration ()
62+ {
63+ $ this ->jwtConfiguration = Configuration::forSymmetricSigner (
64+ $ this ->config ->getSigner (),
65+ InMemory::base64Encoded ($ this ->config ->getSigningKey ())
66+ );
5567 }
5668
5769 protected function getStore ()
5870 {
5971 return $ this ->app ->get ('jwt ' )->getStore ();
6072 }
6173
62- protected function resolveConfig (): array
74+ protected function resolveConfig ()
6375 {
6476 $ store = $ this ->getStore ();
6577 $ options = $ this ->app ->config ->get ("jwt.stores. {$ store }.token " , []);
6678
6779 if (!empty ($ options )) {
68- return $ options ;
69- }
70-
71- throw new JWTException ($ store . '应用 Token 配置未完整 ' , 500 );
72- }
73-
74- protected function makeId (array $ claims )
75- {
76- $ key = $ this ->config ->getIdKey ();
77- if (!array_key_exists ($ key , $ claims )) {
78- throw new JWTException ("claims {$ key } requried " , 500 );
80+ $ this ->config = new Config ($ options );
81+ } else {
82+ throw new JWTException ($ store . '应用 Token 配置未完整 ' , 500 );
7983 }
80-
81- return $ claims [$ key ];
8284 }
8385
84- public function make (array $ claims ): JwtToken
86+ public function make ($ identifier , array $ claims = [] ): JwtToken
8587 {
86- $ unique_id = $ this ->makeId ($ claims );
87-
88- $ now = time ();
89- $ expires = $ now + $ this ->config ->getExpires ();
90- $ refreshAt = $ expires + $ this ->config ->getRefreshTTL ();
91- $ notBefore = $ this ->config ->getNotBefore ();
92- $ iss = $ this ->config ->getIss ();
93- $ aud = $ this ->config ->getAud ();
94-
95- $ builder = new Builder ();
96- $ builder ->setIssuer ($ iss )
97- ->setAudience ($ aud )
98- ->setId ($ unique_id , true )
99- ->setIssuedAt ($ now )
100- ->setNotBefore ($ now + $ notBefore )
101- ->setExpiration ($ expires )
102- ->set ('refreshAt ' , $ refreshAt );
103-
104- $ builder ->set ('store ' , $ this ->getStore ());
105-
106- foreach ($ claims as $ key => $ claim ) {
107- $ builder ->set ($ key , $ claim );
108- }
109-
110- $ token = $ builder ->getToken ($ this ->config ->getSigner (), $ this ->config ->makeSignerKey ());
111-
112- return $ token ;
88+ $ now = new DateTimeImmutable ();
89+ return $ this ->jwtConfiguration ->builder ()
90+ ->permittedFor ($ this ->config ->getAud ())
91+ ->issuedBy ($ this ->config ->getIss ())
92+ ->identifiedBy ((string )$ identifier )
93+ ->issuedAt ($ now )
94+ ->canOnlyBeUsedAfter ($ now )
95+ ->expiresAt ($ this ->getExpiryDateTime ($ now ))
96+ ->relatedTo ((string ) $ identifier )
97+ ->withClaim ('scopes ' , json_encode ($ claims ))
98+ ->getToken ($ this ->jwtConfiguration ->signer (), $ this ->jwtConfiguration ->signingKey ());
11399 }
114100
115- /**
116- * Token 自动续期
117- *
118- * @param Token $token
119- * @param int|string $ttl 秒数
120- * @return void
121- */
122- protected function automaticRenewalToken (JwtToken $ token )
101+ public function getExpiryDateTime ($ now ): DateTimeImmutable
123102 {
124- $ claims = $ token ->getClaims ();
125-
126- unset($ claims ['iat ' ]);
127- unset($ claims ['jti ' ]);
128- unset($ claims ['nbf ' ]);
129- unset($ claims ['exp ' ]);
130- unset($ claims ['iss ' ]);
131- unset($ claims ['aud ' ]);
132- unset($ claims ['refreshAt ' ]);
133-
134- $ token = $ this ->make ($ claims );
135- $ claims = $ token ->getClaims ();
136- $ refreshAt = $ claims ['refreshAt ' ];
137-
138- header ('Access-Control-Expose-Headers:Automatic-Renewal-Token,Automatic-Renewal-Token-RefreshAt ' );
139- header ("Automatic-Renewal-Token: $ token " );
140- header ("Automatic-Renewal-Token-RefreshAt: $ refreshAt " );
141-
142- return $ token ;
103+ $ ttl = (string )$ this ->config ->getExpires ();
104+ return $ now ->modify ("+ {$ ttl } sec " );
143105 }
144106
145107 public function parseToken (string $ token ): JwtToken
146108 {
147- try {
148- $ token = (new Parser ())->parse ($ token );
149- } catch (\InvalidArgumentException $ e ) {
150- throw new JWTInvalidArgumentException ('此 Token 解析失败 ' , 500 );
151- }
152-
109+ $ token = $ this ->jwtConfiguration ->parser ()->parse ($ token );
153110 return $ token ;
154111 }
155112
@@ -167,70 +124,21 @@ public function verify(string $token): ?bool
167124 {
168125 $ this ->token = $ this ->parseToken ($ token );
169126
170- if (false === $ this ->token ->verify ($ this ->config ->getSigner (), $ this ->config ->makeSignerKey ())) {
171- throw new JWTException ('此 Token 与 密钥不匹配 ' , $ this ->config ->getReloginCode ());
172- }
173-
174- // Token 是否已可用
175- $ now = time ();
176- $ exp = $ this ->token ->getClaim ('nbf ' );
177- if ($ now < $ exp ) {
178- throw new JWTException ('此 Token 暂未可用 ' , 500 );
179- }
180-
181- // 是否已过期
182- if (true === $ this ->token ->isExpired ()) {
183- if ($ now <= $ this ->token ->getClaim ('refreshAt ' )) {
184- // 是否开启自动续签
185- if ($ this ->config ->getAutomaticRenewal ()) {
186- $ this ->token = $ this ->automaticRenewalToken ($ this ->token );
187- } else {
188- throw new TokenAlreadyEexpired ('Token 已过期,请重新刷新 ' , $ this ->config ->getRefreshCode ());
189- }
190- } else {
191- throw new TokenAlreadyEexpired ('Token 刷新时间已过,请重新登录 ' , $ this ->config ->getReloginCode ());
192- }
193- } else {
194- // 是否存在黑名单
195- if (true === $ this ->app ->get ('jwt.manager ' )->wasBan ($ this ->token )) {
196- throw new TokenAlreadyEexpired ('Token 已被禁用,请重新登录 ' , $ this ->config ->getReloginCode ());
197- }
198- }
199-
200- $ data = new ValidationData ();
127+ $ this ->jwtConfiguration ->setValidationConstraints (
128+ new ValidAt (new SystemClock (new DateTimeZone (\date_default_timezone_get ()))),
129+ new SignedWith ($ this ->jwtConfiguration ->signer (), $ this ->jwtConfiguration ->signingKey ())
130+ );
201131
202- $ jwt_id = $ this ->token ->getHeader ('jti ' );
203- $ data ->setIssuer ($ this ->config ->getIss ());
204- $ data ->setAudience ($ this ->config ->getAud ());
205- $ data ->setId ($ jwt_id );
132+ $ constraints = $ this ->jwtConfiguration ->validationConstraints ();
206133
207- if (!$ this ->token -> validate ($ data )) {
208- throw new JWTException ('此 Token 效验不通过 ' , $ this -> config -> getReloginCode () );
134+ if (!$ this ->jwtConfiguration -> validator ()-> validate ($ this -> token , ... $ constraints )) {
135+ throw new JWTException ('效验失败 ' , 401 );
209136 }
210137
211138 return true ;
212139 }
213140
214- public function refresh (string $ token = null ): JwtToken
215- {
216- $ token = $ token ?: $ this ->getRequestToken ();
217- $ token = $ this ->parseToken ($ token );
218-
219- $ claims = $ token ->getClaims ();
220-
221- unset($ claims ['iat ' ]);
222- unset($ claims ['jti ' ]);
223- unset($ claims ['nbf ' ]);
224- unset($ claims ['exp ' ]);
225- unset($ claims ['iss ' ]);
226- unset($ claims ['aud ' ]);
227-
228- $ this ->app ->get ('jwt.manager ' )->logout ($ token );
229-
230- return $ this ->make ($ claims );
231- }
232-
233- public function logout (?string $ token = null ): void
141+ public function logout (?string $ token ): void
234142 {
235143 $ token = $ token ?: $ this ->getRequestToken ();
236144 $ token = $ this ->parseToken ($ token );
@@ -256,9 +164,4 @@ public function getType(): string
256164 {
257165 return $ this ->config ->getTokenType ();
258166 }
259-
260- public function getRefreshTTL ()
261- {
262- return $ this ->config ->getRefreshTTL ();
263- }
264167}
0 commit comments