FeatherJS中如何将用户角色存入params.user优化权限校验
FeatherJS 角色信息注入上下文解决方案
问题分析
你当前代码的核心问题是数据库查询是异步操作,login事件中的代码没有等待角色查询完成就继续执行,导致authResult.user还没被修改就返回给客户端;另外authResult.user['roles']未初始化,直接push会触发报错。
解决方案
方案一:异步等待角色查询完成(修改login事件)
将login事件处理函数改为async/await,确保角色查询完成后再修改用户对象:
app.on('login', async (authResult: AuthenticationResult, { connection }: Params) => { try { // 先初始化roles数组,避免push时出错 authResult.user.roles = []; // 等待数据库查询完成 const results = await app .get('postgresqlClient') .select('users_roles.role_id') .from('users_roles') .where('users_roles.user_id', '=', authResult.user.id); // 遍历结果添加角色ID results.forEach(result => { authResult.user.roles.push(result.role_id); }); } catch (err) { console.error('获取角色失败:', err); } if (connection) { app.channel('anonymous').leave(connection); console.log('用户离开匿名频道'); app.channel('authenticated').join(connection); console.log('用户认证成功并加入已认证频道'); } });
修改后,认证返回结果会包含roles字段,后续请求的context.params.user中也能直接访问该字段。
方案二:将角色写入JWT Payload(推荐)
如果希望后续所有请求无需重复查询数据库,直接从JWT中解析角色,可修改认证策略的getPayload方法:
// src/services/authentication/authentication.service.ts import { AuthenticationService } from '@feathersjs/authentication'; import { JWTStrategy } from '@feathersjs/authentication-jwt'; import { LocalStrategy } from '@feathersjs/authentication-local'; import type { Application } from '../declarations'; export class CustomAuthenticationService extends AuthenticationService { async getPayload(authResult: any) { // 获取默认JWT Payload const payload = await super.getPayload(authResult); // 查询用户关联的角色ID const roleRecords = await this.app .get('postgresqlClient') .select('role_id') .from('users_roles') .where('user_id', '=', authResult.user.id); // 将角色ID数组写入Payload payload.roles = roleRecords.map(record => record.role_id); return payload; } } export const authentication = (app: Application) => { const authService = new CustomAuthenticationService(app); authService.register('jwt', new JWTStrategy()); authService.register('local', new LocalStrategy()); app.use('/authentication', authService); };
这样JWT Token中会包含roles字段,每次请求解析Token时,context.params.user会自动带上该字段,完全避免重复查询数据库。
权限校验Hook示例
拿到角色后,可编写全局或服务级Hook做权限校验:
// src/hooks/check-roles.ts import type { HookContext } from '../declarations'; export const checkRoles = (requiredRoles: number[]) => { return async (context: HookContext) => { const { user } = context.params; // 校验用户是否拥有指定角色 if (!user?.roles || !requiredRoles.some(role => user.roles.includes(role))) { throw new Error('无访问权限'); } return context; }; };
在服务中使用该Hook:
// src/services/posts/posts.hooks.ts import { checkRoles } from '../../hooks/check-roles'; export default { before: { all: [authenticate('jwt'), checkRoles([0, 1])], // 仅允许角色0、1的用户访问 create: [], // ...其他生命周期Hook } };
内容的提问来源于stack exchange,提问作者Palguno Wicaksono
相关产品推荐
相关产品推荐

