Pārlūkot izejas kodu

实现单点登录功能,根据最后一次登录时间,之前的提示失效

wilhelm wong 2 mēneši atpakaļ
vecāks
revīzija
8e176e14b9
1 mainītis faili ar 71 papildinājumiem un 0 dzēšanām
  1. 71 0
      src/services/member-token-manager.service.ts

+ 71 - 0
src/services/member-token-manager.service.ts

@@ -0,0 +1,71 @@
+import { FastifyInstance } from 'fastify'
+import { Repository } from 'typeorm'
+import { Member } from '../entities/member.entity'
+
+export class MemberTokenManagerService {
+  private memberRepository: Repository<Member>
+  private app: FastifyInstance
+
+  constructor(app: FastifyInstance) {
+    this.app = app
+    this.memberRepository = app.dataSource.getRepository(Member)
+  }
+
+  /**
+   * 用户登录时,使该用户的所有其他token失效
+   * 通过更新member表的lastLoginAt时间戳来实现
+   */
+  async invalidateUserTokens(userId: number): Promise<void> {
+    const now = new Date()
+    await this.memberRepository.update(
+      { userId }, 
+      { lastLoginAt: now }
+    )
+  }
+
+  /**
+   * 验证token是否有效
+   * 通过比较token的签发时间与member表的最后登录时间
+   */
+  async isTokenValid(userId: number, tokenIssuedAt: number): Promise<boolean> {
+    try {
+      const member = await this.memberRepository.findOne({ 
+        where: { userId },
+        select: ['id', 'lastLoginAt']
+      })
+
+      if (!member || !member.lastLoginAt) {
+        return false
+      }
+
+      // 将token的签发时间转换为Date对象
+      const tokenIssuedDate = new Date(tokenIssuedAt * 1000)
+      
+      // 添加1秒的容错范围,因为JWT的iat是秒级时间戳,而数据库是毫秒级
+      const tolerance = 1000 // 1秒容错
+      const adjustedLastLoginAt = new Date(member.lastLoginAt.getTime() - tolerance)
+      
+      // 如果token的签发时间早于调整后的最后登录时间,则token无效
+      return tokenIssuedDate >= adjustedLastLoginAt
+    } catch (error) {
+      this.app.log.error(error, '验证token有效性失败')
+      return false
+    }
+  }
+
+  /**
+   * 获取用户最后登录时间
+   */
+  async getUserLastLoginTime(userId: number): Promise<Date | null> {
+    try {
+      const member = await this.memberRepository.findOne({ 
+        where: { userId },
+        select: ['lastLoginAt']
+      })
+      return member?.lastLoginAt || null
+    } catch (error) {
+      this.app.log.error(error, '获取用户最后登录时间失败')
+      return null
+    }
+  }
+}