You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 17:31:57