Selaa lähdekoodia

feat: 完善jwt实效问题

runphp 1 kuukausi sitten
vanhempi
sitoutus
61e08bf66a
2 muutettua tiedostoa jossa 29 lisäystä ja 35 poistoa
  1. 6 10
      src/Auth.php
  2. 23 25
      src/Hook/AuthHook.php

+ 6 - 10
src/Auth.php

@@ -10,6 +10,7 @@ use Firebase\JWT\Key;
 use Ramsey\Uuid\Uuid;
 use SixShop\Auth\Contracts\AuthInterface;
 use SixShop\Auth\Enum\UserTypeEnum;
+use think\Env;
 use think\facade\Event;
 
 class Auth implements AuthInterface
@@ -20,12 +21,12 @@ class Auth implements AuthInterface
 
     private static array $config;
 
-    public function __construct(private readonly UserTypeEnum $userType)
+    public function __construct(private readonly UserTypeEnum $userType, private readonly Env $env)
     {
         if (empty(self::$config)) {
             self::$config = [
-                'jwt_secret' => env('JWT_SECRET', ''),
-                'expire_in' => (int) env('JWT_EXPIRE_IN', 3600),
+                'jwt_secret' => $this->env->get('JWT_SECRET', ''),
+                'expire_in' => (int) $this->env->get('JWT_EXPIRE_IN', 3600),
             ];
         }
     }
@@ -56,16 +57,11 @@ class Auth implements AuthInterface
     public function verifyToken(string $jwt): string
     {
         JWT::$leeway = self::SLEEP_WAY;
-        try {
-            $payload = JWT::decode($jwt, new Key(self::$config['jwt_secret'], self::ALGORITHM));
-        } catch (ExpiredException $e) {
-            // ... 忽略
-            $payload = $e->getPayload();
-        }
+        $payload = JWT::decode($jwt, new Key(self::$config['jwt_secret'], self::ALGORITHM));
 
         $res = match (UserTypeEnum::tryFrom($payload->aud)) {
             $this->userType => decrypt_data($payload->sub, self::$config['jwt_secret']),
-            default => throw new \Exception('token 类型错误'),
+            default => throw new \Exception('Invalid token type'),
         };
         Event::trigger('token_verify', $payload);
         return $res;

+ 23 - 25
src/Hook/AuthHook.php

@@ -12,52 +12,50 @@ use think\Cache;
 class AuthHook
 {
     public const string TOKEN_REVOKE = 'token_revoke:';
-
-    private int $expireIn;
+    public const string TOKEN_INFO = 'token_info:';
 
     public function __construct(private Cache $cache)
     {
-        $this->expireIn = Auth::getConfig()['expire_in'];
     }
 
+    /**
+     * Token生成时在Redis中记录token信息,方便服务端查看token状态
+     */
     #[Hook("token_generate")]
     public function generateToken($payload): void
     {
-        $this->renewToken($payload);
+        $this->cache->remember(self::TOKEN_INFO . $payload->jti, (array) $payload, $payload->exp - time());
     }
 
     /**
+     * Token验证时检查是否被提前撤销
      * @throws \ExpiredException
      */
     #[Hook("token_verify")]
     public function checkToken($payload): void
     {
-        if ($this->cache->has(self::TOKEN_REVOKE . $payload->jti)
-            && $this->cache->get(self::TOKEN_REVOKE . $payload->jti) > time() - Auth::SLEEP_WAY) {
-            // 未过期可以续期
-            $this->renewToken($payload);
-            return;
+        $revokeTime = $this->cache->get(self::TOKEN_REVOKE . $payload->jti);
+
+        // token被撤销且超过SLEEP_WAY时间,拒绝访问
+        if ($revokeTime !== null && $revokeTime <= time() - Auth::SLEEP_WAY) {
+            $ex = new ExpiredException('Token expired early');
+            $ex->setPayload($payload);
+            $ex->setTimestamp($payload->exp);
+            throw $ex;
         }
-        $ex = new ExpiredException('Expired token');
-        $ex->setPayload($payload);
-        $ex->setTimestamp($payload->exp);
-        throw $ex;
-    }
-
-    #[Hook("token_revoke")]
-    public function revokeToken($payload): void
-    {
-        $this->cache->remember(self::TOKEN_REVOKE . $payload->jti, time(), $payload->exp - time() + Auth::SLEEP_WAY);
     }
 
     /**
-     * @param $payload
-     * @return void
-     * @throws \Throwable
+     * Token被撤销时,在Redis中存储撤销标记,并清除token信息
+     * 撤销标记的TTL设置为token剩余有效期,确保撤销记录在token过期后自动清除
      */
-    public function renewToken($payload): void
+    #[Hook("token_revoke")]
+    public function revokeToken($payload): void
     {
-        $exp = time() + $this->expireIn;
-        $this->cache->remember(self::TOKEN_REVOKE . $payload->jti, $exp, $this->expireIn + Auth::SLEEP_WAY);
+        $remainingTime = $payload->exp - time();
+        if ($remainingTime > 0) {
+            $this->cache->remember(self::TOKEN_REVOKE . $payload->jti, time(), $remainingTime);
+            $this->cache->delete(self::TOKEN_INFO . $payload->jti);
+        }
     }
 }