Răsfoiți Sursa

🐛 fix(auth): 修复过期令牌验证问题

- 增强JWT工具以区分过期令牌和无效令牌
- 修复generateToken方法支持自定义过期时间
- 改进sso-verify端点错误处理,区分过期和无效令牌
- 更新认证集成测试以验证过期令牌处理

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname 4 luni în urmă
părinte
comite
0bef45e600

+ 4 - 1
src/server/api/auth/sso-verify.ts

@@ -55,7 +55,10 @@ const app = new OpenAPIHono().openapi(routeDef, async (c) => {
 
       return c.text('OK', 200)
     } catch (tokenError) {
-      return c.json({ code: 401, message: '令牌验证失败' }, 401)
+      const errorMessage = tokenError instanceof Error && tokenError.message === '令牌已过期'
+        ? '令牌已过期'
+        : '令牌验证失败'
+      return c.json({ code: 401, message: errorMessage }, 401)
     }
   } catch (error) {
     return c.json({ code: 500, message: 'SSO验证失败' }, 500)

+ 1 - 1
src/server/modules/auth/auth.service.ts

@@ -70,7 +70,7 @@ export class AuthService {
   }
 
   generateToken(user: User, expiresIn?: string): string {
-    return JWTUtil.generateToken(user, expiresIn ? { expiresIn } as any : {});
+    return JWTUtil.generateToken(user, {}, expiresIn);
   }
 
   verifyToken(token: string): any {

+ 9 - 3
src/server/utils/jwt.util.ts

@@ -1,4 +1,4 @@
-import jwt, { SignOptions } from 'jsonwebtoken';
+import jwt, { SignOptions, TokenExpiredError } from 'jsonwebtoken';
 import { UserEntity } from '@/server/modules/users/user.entity';
 import debug from 'debug';
 
@@ -24,7 +24,7 @@ export class JWTUtil {
    * @param additionalPayload 额外的 payload 数据
    * @returns JWT token
    */
-  static generateToken(user: UserEntity, additionalPayload: Partial<JWTPayload> = {}): string {
+  static generateToken(user: UserEntity, additionalPayload: Partial<JWTPayload> = {}, expiresIn?: string): string {
     if (!user.id || !user.username) {
       throw new Error('用户ID和用户名不能为空');
     }
@@ -38,7 +38,10 @@ export class JWTUtil {
     };
 
     try {
-      return jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN as SignOptions['expiresIn']});
+      const options: SignOptions = {
+        expiresIn: expiresIn || JWT_EXPIRES_IN as SignOptions['expiresIn']
+      };
+      return jwt.sign(payload, JWT_SECRET, options);
     } catch (error) {
       logger.error('生成JWT token失败:', error);
       throw new Error('生成token失败');
@@ -55,6 +58,9 @@ export class JWTUtil {
       return jwt.verify(token, JWT_SECRET) as JWTPayload;
     } catch (error) {
       logger.error('验证JWT token失败:', error);
+      if (error instanceof TokenExpiredError) {
+        throw new Error('令牌已过期');
+      }
       throw new Error('无效的token');
     }
   }

+ 1 - 1
tests/integration/server/auth.integration.test.ts

@@ -193,7 +193,7 @@ describe('认证API集成测试 (使用hono/testing)', () => {
       expect(response.status).toBe(401);
       if (response.status === 401) {
         const responseData = await response.json();
-        expect(responseData.message).toContain('令牌验证失败');
+        expect(responseData.message).toContain('令牌已过期');
       }
     });
   });