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 { signSharedJwt, sharedCookieConfig } from '@/lib/jwt-shared'
|
||||||
import { ldapAuth } from '@/lib/ldap'
|
import { ldapAuth } from '@/lib/ldap'
|
||||||
import { getDb } from '@/lib/db'
|
import { getDb } from '@/lib/db'
|
||||||
|
import { getUserPermissions } from '@/lib/permissions'
|
||||||
import bcrypt from 'bcryptjs'
|
import bcrypt from 'bcryptjs'
|
||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
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)
|
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
|
// 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 sharedToken = signSharedJwt({ username, displayName })
|
||||||
const sharedCfg = sharedCookieConfig()
|
const sharedCfg = sharedCookieConfig()
|
||||||
|
|
||||||
const response = NextResponse.json({
|
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, {
|
response.cookies.set('session_issue', localToken, {
|
||||||
httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax',
|
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 { verifySharedJwt } from './jwt-shared'
|
||||||
import { ldapUserExists } from './ldap'
|
import { ldapUserExists } from './ldap'
|
||||||
|
import { getUserPermissions } from './permissions'
|
||||||
|
|
||||||
export async function getCurrentUser(): Promise<UserPayload | null> {
|
export async function getCurrentUser(): Promise<UserPayload | null> {
|
||||||
const cookieStore = await cookies()
|
const cookieStore = await cookies()
|
||||||
|
|
@ -29,6 +30,7 @@ export async function getCurrentUser(): Promise<UserPayload | null> {
|
||||||
).get(sharedPayload.username) as UserPayload | undefined
|
).get(sharedPayload.username) as UserPayload | undefined
|
||||||
if (row) {
|
if (row) {
|
||||||
db.prepare("UPDATE users SET last_active_at = datetime('now', '+8 hours') WHERE id = ?").run(row.id)
|
db.prepare("UPDATE users SET last_active_at = datetime('now', '+8 hours') WHERE id = ?").run(row.id)
|
||||||
|
row.permissions = getUserPermissions(row.role)
|
||||||
return row
|
return row
|
||||||
}
|
}
|
||||||
// SSO 免登录:LLDAP 验证通过但本地无记录 → 自动创建(viewer 角色)
|
// SSO 免登录:LLDAP 验证通过但本地无记录 → 自动创建(viewer 角色)
|
||||||
|
|
@ -38,7 +40,10 @@ export async function getCurrentUser(): Promise<UserPayload | null> {
|
||||||
const newRow = db.prepare(
|
const newRow = db.prepare(
|
||||||
'SELECT id, username, display_name, role FROM users WHERE username = ? AND is_active = 1'
|
'SELECT id, username, display_name, role FROM users WHERE username = ? AND is_active = 1'
|
||||||
).get(sharedPayload.username) as UserPayload | undefined
|
).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) {
|
if (payload) {
|
||||||
const db2 = getDb()
|
const db2 = getDb()
|
||||||
db2.prepare("UPDATE users SET last_active_at = datetime('now', '+8 hours') WHERE id = ?").run(payload.id)
|
db2.prepare("UPDATE users SET last_active_at = datetime('now', '+8 hours') WHERE id = ?").run(payload.id)
|
||||||
|
payload.permissions = getUserPermissions(payload.role)
|
||||||
}
|
}
|
||||||
return payload
|
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
|
const user = db.prepare('SELECT * FROM users WHERE username = ? AND is_active = 1').get(username) as any
|
||||||
if (!user) return null
|
if (!user) return null
|
||||||
if (!bcrypt.compareSync(password, user.password_hash)) 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 }
|
return { token: await createToken(payload), user: payload }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ export interface UserPayload {
|
||||||
username: string
|
username: string
|
||||||
display_name: string
|
display_name: string
|
||||||
role: string
|
role: string
|
||||||
|
permissions: string[] // 用户权限列表
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode Uint8Array to base64url
|
// Encode Uint8Array to base64url
|
||||||
|
|
@ -72,7 +73,7 @@ export async function verifyToken(token: string): Promise<UserPayload | null> {
|
||||||
const payload = JSON.parse(base64urlToStr(parts[1]))
|
const payload = JSON.parse(base64urlToStr(parts[1]))
|
||||||
if (payload.exp && payload.exp < Math.floor(Date.now() / 1000)) return null
|
if (payload.exp && payload.exp < Math.floor(Date.now() / 1000)) return null
|
||||||
if (payload.id == null) 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 {
|
} catch {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,3 +31,15 @@ export function requirePermission(user: UserPayload | null, permission: string):
|
||||||
if (!user) throw new Error('未登录')
|
if (!user) throw new Error('未登录')
|
||||||
if (!hasPermission(user, permission)) 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