初次提交
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
import jwt from 'jsonwebtoken'
|
||||
import { db } from '~/drizzle/db'
|
||||
import { users } from '~/drizzle/schema'
|
||||
import { executeRedisCommand, isRedisReady } from '../../utils/redis'
|
||||
import { eq } from 'drizzle-orm'
|
||||
|
||||
// 用户认证缓存(永久缓存,登出或权限变更时主动失效)
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const token =
|
||||
getCookie(event, 'auth-token') || getHeader(event, 'authorization')?.replace('Bearer ', '')
|
||||
|
||||
if (!token) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: '未提供认证令牌'
|
||||
})
|
||||
}
|
||||
|
||||
// 验证JWT令牌
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as { userId: number }
|
||||
const userId = decoded.userId
|
||||
|
||||
// 优先从Redis缓存获取用户认证状态
|
||||
if (isRedisReady()) {
|
||||
const cachedUser = await executeRedisCommand(async () => {
|
||||
const client = (await import('../../utils/redis')).getRedisClient()
|
||||
if (!client) return null
|
||||
|
||||
const cacheKey = `auth:user:${userId}`
|
||||
const userData = await client.get(cacheKey)
|
||||
|
||||
if (userData) {
|
||||
console.log(`[API] 用户认证缓存命中: ${userId}`)
|
||||
return JSON.parse(userData)
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
if (cachedUser) {
|
||||
// 为缓存的用户数据添加字段
|
||||
const userWithDetails = {
|
||||
id: cachedUser.id,
|
||||
username: cachedUser.username,
|
||||
name: cachedUser.name,
|
||||
grade: cachedUser.grade,
|
||||
class: cachedUser.class,
|
||||
studentId: cachedUser.studentId,
|
||||
email: cachedUser.email,
|
||||
emailVerified: cachedUser.emailVerified,
|
||||
role: cachedUser.role,
|
||||
requirePasswordChange: cachedUser.forcePasswordChange || !cachedUser.passwordChangedAt,
|
||||
has2FA: cachedUser.identities?.some((id: any) => id.provider === 'totp') || false,
|
||||
avatar: cachedUser.identities?.find((id: any) => id.provider === 'github')?.providerUsername
|
||||
? `https://github.com/${cachedUser.identities.find((id: any) => id.provider === 'github').providerUsername}.png`
|
||||
: null
|
||||
}
|
||||
return {
|
||||
user: userWithDetails,
|
||||
valid: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 缓存未命中或Redis不可用,从数据库获取用户信息
|
||||
// 同时查询所有身份绑定信息
|
||||
const userResult = await db.query.users.findFirst({
|
||||
where: eq(users.id, userId),
|
||||
columns: {
|
||||
id: true,
|
||||
username: true,
|
||||
name: true,
|
||||
grade: true,
|
||||
class: true,
|
||||
studentId: true,
|
||||
email: true,
|
||||
emailVerified: true,
|
||||
role: true,
|
||||
forcePasswordChange: true,
|
||||
passwordChangedAt: true
|
||||
},
|
||||
with: {
|
||||
identities: {
|
||||
columns: {
|
||||
provider: true,
|
||||
providerUsername: true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const dbUser = userResult || null
|
||||
|
||||
if (!dbUser) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: '用户不存在'
|
||||
})
|
||||
}
|
||||
|
||||
// 构建返回的用户对象,只包含需要的字段
|
||||
const githubIdentity = dbUser.identities?.find((id: any) => id.provider === 'github')
|
||||
const user = {
|
||||
id: dbUser.id,
|
||||
username: dbUser.username,
|
||||
name: dbUser.name,
|
||||
grade: dbUser.grade,
|
||||
class: dbUser.class,
|
||||
studentId: dbUser.studentId,
|
||||
email: dbUser.email,
|
||||
emailVerified: dbUser.emailVerified,
|
||||
role: dbUser.role,
|
||||
requirePasswordChange: dbUser.forcePasswordChange || !dbUser.passwordChangedAt,
|
||||
has2FA: dbUser.identities?.some((id: any) => id.provider === 'totp') || false,
|
||||
// 动态生成 GitHub 头像 URL
|
||||
avatar: githubIdentity?.providerUsername
|
||||
? `https://github.com/${githubIdentity.providerUsername}.png`
|
||||
: null
|
||||
}
|
||||
|
||||
// 将用户认证状态缓存到Redis(如果可用)- 永久缓存
|
||||
if (isRedisReady()) {
|
||||
await executeRedisCommand(async () => {
|
||||
const client = (await import('../../utils/redis')).getRedisClient()
|
||||
if (!client) return
|
||||
|
||||
const cacheKey = `auth:user:${userId}`
|
||||
// 缓存完整的数据库用户信息,用于后续验证
|
||||
await client.set(cacheKey, JSON.stringify(dbUser))
|
||||
console.log(`[API] 用户认证状态已缓存: ${userId}`)
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
valid: true
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError') {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: '令牌无效或已过期'
|
||||
})
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user