feat: UserPayload 增加 permissions 字段,getCurrentUser 补全权限列表
- UserPayload 新增 permissions: string[] 字段 - getCurrentUser() 三个返回路径(共享JWT查DB/SSO自动创建/本地JWT) 均通过 getUserPermissions() 从 roles 表实时查询补全权限 - 新增 getUserPermissions(role) 工具函数,admin 返回 ['*'], 其他角色从 roles 表解析 permissions JSON - login() 函数和 login API 路由同步补全 permissions - verifyToken() 返回对象补全 permissions 空值防止类型错误
This commit is contained in:
parent
4b6bee1868
commit
1ae84294bb
|
|
@ -4,6 +4,7 @@ import { initDatabase } from '@/lib/db-schema'
|
|||
import { signSharedJwt, sharedCookieConfig } from '@/lib/jwt-shared'
|
||||
import { ldapAuth } from '@/lib/ldap'
|
||||
import { getDb } from '@/lib/db'
|
||||
import { getUserPermissions } from '@/lib/permissions'
|
||||
import bcrypt from 'bcryptjs'
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
|
|
@ -76,12 +77,12 @@ export async function POST(request: NextRequest) {
|
|||
db.prepare("UPDATE users SET last_login_at = datetime('now', '+8 hours'), last_active_at = datetime('now', '+8 hours') WHERE id = ?").run(userId)
|
||||
|
||||
// 4. 签发两个 cookie
|
||||
const localToken = await createToken({ id: userId, username, display_name: displayName, role })
|
||||
const localToken = await createToken({ id: userId, username, display_name: displayName, role, permissions: [] })
|
||||
const sharedToken = signSharedJwt({ username, displayName })
|
||||
const sharedCfg = sharedCookieConfig()
|
||||
|
||||
const response = NextResponse.json({
|
||||
user: { id: userId, username, display_name: displayName, role },
|
||||
user: { id: userId, username, display_name: displayName, role, permissions: getUserPermissions(role) },
|
||||
})
|
||||
response.cookies.set('session_issue', localToken, {
|
||||
httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax',
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ export { createToken, verifyToken, type UserPayload }
|
|||
|
||||
import { verifySharedJwt } from './jwt-shared'
|
||||
import { ldapUserExists } from './ldap'
|
||||
import { getUserPermissions } from './permissions'
|
||||
|
||||
export async function getCurrentUser(): Promise<UserPayload | null> {
|
||||
const cookieStore = await cookies()
|
||||
|
|
@ -29,6 +30,7 @@ export async function getCurrentUser(): Promise<UserPayload | null> {
|
|||
).get(sharedPayload.username) as UserPayload | undefined
|
||||
if (row) {
|
||||
db.prepare("UPDATE users SET last_active_at = datetime('now', '+8 hours') WHERE id = ?").run(row.id)
|
||||
row.permissions = getUserPermissions(row.role)
|
||||
return row
|
||||
}
|
||||
// SSO 免登录:LLDAP 验证通过但本地无记录 → 自动创建(viewer 角色)
|
||||
|
|
@ -38,7 +40,10 @@ export async function getCurrentUser(): Promise<UserPayload | null> {
|
|||
const newRow = db.prepare(
|
||||
'SELECT id, username, display_name, role FROM users WHERE username = ? AND is_active = 1'
|
||||
).get(sharedPayload.username) as UserPayload | undefined
|
||||
if (newRow) return newRow
|
||||
if (newRow) {
|
||||
newRow.permissions = getUserPermissions(newRow.role)
|
||||
return newRow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -49,6 +54,7 @@ export async function getCurrentUser(): Promise<UserPayload | null> {
|
|||
if (payload) {
|
||||
const db2 = getDb()
|
||||
db2.prepare("UPDATE users SET last_active_at = datetime('now', '+8 hours') WHERE id = ?").run(payload.id)
|
||||
payload.permissions = getUserPermissions(payload.role)
|
||||
}
|
||||
return payload
|
||||
}
|
||||
|
|
@ -58,7 +64,7 @@ export async function login(username: string, password: string) {
|
|||
const user = db.prepare('SELECT * FROM users WHERE username = ? AND is_active = 1').get(username) as any
|
||||
if (!user) return null
|
||||
if (!bcrypt.compareSync(password, user.password_hash)) return null
|
||||
const payload: UserPayload = { id: user.id, username: user.username, display_name: user.display_name, role: user.role }
|
||||
const payload: UserPayload = { id: user.id, username: user.username, display_name: user.display_name, role: user.role, permissions: getUserPermissions(user.role) }
|
||||
return { token: await createToken(payload), user: payload }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ export interface UserPayload {
|
|||
username: string
|
||||
display_name: string
|
||||
role: string
|
||||
permissions: string[] // 用户权限列表
|
||||
}
|
||||
|
||||
// Encode Uint8Array to base64url
|
||||
|
|
@ -72,7 +73,7 @@ export async function verifyToken(token: string): Promise<UserPayload | null> {
|
|||
const payload = JSON.parse(base64urlToStr(parts[1]))
|
||||
if (payload.exp && payload.exp < Math.floor(Date.now() / 1000)) return null
|
||||
if (payload.id == null) return null
|
||||
return { id: payload.id, username: payload.username, display_name: payload.display_name, role: payload.role }
|
||||
return { id: payload.id, username: payload.username, display_name: payload.display_name, role: payload.role, permissions: payload.permissions || [] }
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,3 +31,15 @@ export function requirePermission(user: UserPayload | null, permission: string):
|
|||
if (!user) throw new Error('未登录')
|
||||
if (!hasPermission(user, permission)) throw new Error('权限不足')
|
||||
}
|
||||
|
||||
export function getUserPermissions(role: string): string[] {
|
||||
if (role === 'admin') return ['*']
|
||||
const db = getDb()
|
||||
const row = db.prepare('SELECT permissions FROM roles WHERE name = ?').get(role) as { permissions: string } | undefined
|
||||
if (!row) return []
|
||||
try {
|
||||
return JSON.parse(row.permissions)
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue