npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lark-apaas/nestjs-authzpaas

v0.1.12

Published

FullStack Nestjs authzpaas

Readme

nestjs-authzpaas

nestjs-authzpaas 是一个基于 NestJS 的权限管理模块,提供了完整的 RBAC(基于角色的访问控制)和 ABAC(基于属性的访问控制)解决方案。它基于 CASL 实现,支持角色检查、权限检查、环境检查等多种鉴权方式。

目录

核心概念

权限模型

本模块采用以下权限模型:

  • 角色 (Role): 用户所属的角色,如 admineditorviewer

核心组件

| 组件 | 类型 | 说明 | |------|------|------| | AuthZPaasGuard | 全局守卫 | 自动拦截所有请求,执行鉴权检查 | | RolesMiddleware | 中间件 | 自动获取并注入用户角色信息到请求上下文 | | CanRole | 装饰器 | 声明式角色检查 |

快速开始

1. 安装依赖

npm install @lark-apaas/nestjs-authzpaas
# 或
yarn add @lark-apaas/nestjs-authzpaas

2. 配置模块

app.module.ts 中导入并配置 AuthZPaasModule

import { Module, MiddlewareConsumer, NestModule } from '@nestjs/common';
import { AuthZPaasModule, RolesMiddleware } from '@lark-apaas/nestjs-authzpaas';
import { UserContextMiddleware } from '@lark-apaas/fullstack-nestjs-core';

@Module({
  imports: [
    // 配置 AuthZPaas 模块
    AuthZPaasModule.forRoot({
      permissionApi: {
        timeout: 5000, // 权限 API 超时时间(毫秒), 默认 5000
      },
    }),
  ],
  controllers: [/* ... */],
  providers: [/* ... */],
})

3. 异步配置(可选)

如果需要从 ConfigService 获取配置:

import { ConfigModule, ConfigService } from '@nestjs/config';

@Module({
  imports: [
    ConfigModule.forRoot(),
    AuthZPaasModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: async (configService: ConfigService) => ({
        permissionApi: {
          timeout: 5000,
        },
      }),
    }),
  ],
})
export class AppModule {}

配置选项详解

核心配置

| 配置项 | 类型 | 默认值 | 说明 | |--------|------|--------|------| | permissionApi | PermissionApiConfig | - | 权限 API 配置 |

装饰器使用

💡 装饰器是最常用的权限控制方式,推荐优先使用装饰器来进行权限检查,简单、直观、声明式。

1. CanRole - 角色检查

使用 @CanRole() 装饰器检查用户角色。

示例 1: 单个角色

import { Controller, Get } from '@nestjs/common';
import { CanRole } from '@lark-apaas/nestjs-authzpaas';

@Controller('demo')
export class DemoController {
  // 需要 admin 角色
  @CanRole(['admin'])
  @Get('admin-only')
  adminOnly() {
    return {
      message: '✅ 这是管理员专属接口',
      timestamp: new Date(),
    };
  }
}

示例 2: 多个角色(OR 逻辑)

@Controller('content')
export class ContentController {
  // 拥有 admin 或 editor 任一角色即可访问
  @CanRole(['admin', 'editor'])
  @Get('manage')
  manage() {
    return '管理内容';
  }
}

核心组件

📚 核心组件是装饰器的底层实现,了解核心组件有助于深入理解权限系统的工作原理。

AuthZPaasGuard

AuthZPaasGuard 是一个全局守卫,会自动拦截所有请求并执行以下操作:

  1. 提取用户ID: 从 req.userContext.userId 中提取用户ID
  2. 角色检查: 如果使用了 @CanRole(),检查用户角色
  3. 注入权限数据: 将权限数据附加到 req.userPermissions

工作流程:

   请求 
    ↓
AuthZPaasGuard
    ↓
提取 userId
    ↓
检查角色/权限
    ↓
注入权限数据
    ↓
Controller

配置: AuthZPaasGuard 会在模块导入时自动注册为全局守卫,无需手动配置。

PermissionService

PermissionService 提供权限管理的核心功能。

主要方法

import { Injectable } from '@nestjs/common';
import { PermissionService } from '@lark-apaas/nestjs-authzpaas';

@Injectable()
export class YourService {
  constructor(private readonly permissionService: PermissionService) {}

  async checkUserPermission(userId: string) {
    // 1. 获取用户权限数据(带缓存)
    const permissionData = await this.permissionService.getUserPermissions(userId);
    console.log('用户角色:', permissionData.roles);
    console.log('用户权限:', permissionData.permissions);

    // 2. 获取用户的 CASL Ability 实例
    const ability = await this.permissionService.getAbility(userId);
    
    // 使用 Ability 检查权限
    const canReadUser = ability.can('read', 'User');
    const canCreateTask = ability.can('create', 'Task');
    const canManageAll = ability.can('manage', 'all');

    // 3. 检查权限要求
    const hasPermission = await this.permissionService.checkPermissions(
      [{ actions: ['read'], subject: 'User' }],
      userId
    );

    // 4. 清除用户缓存(权限变更时)
    this.permissionService.clearUserCache(userId);

    // 5. 清除所有缓存
    this.permissionService.clearAllCache();

    // 6. 获取缓存统计
    const stats = this.permissionService.getCacheStats();
    console.log('缓存统计:', stats);
  }
}

工作原理

  1. req.userContext.userId 中提取用户ID
  2. 调用 PermissionService.getUserPermissions() 获取权限数据
  3. 将角色列表注入到 req.userContext.userRoles
  4. 如果获取失败,记录警告但不阻塞请求(由 Guard 处理)

高级用法

1. 手动权限检查

在某些场景下,你可能需要在业务逻辑中手动检查权限,而不是使用装饰器:

import { Injectable } from '@nestjs/common';
import { PermissionService } from '@lark-apaas/nestjs-authzpaas';

@Injectable()
export class TasksService {
  constructor(private readonly permissionService: PermissionService) {}

  async processTask(userId: string, taskId: string) {
    // 获取用户权限数据
    const permissionData = await this.permissionService.getUserPermissions(userId);
    
    // 检查权限
    const canUpdate = await this.permissionService.checkPermissions(
      [{ actions: ['update'], subject: 'Task' }],
      userId
    );

    if (!canUpdate) {
      throw new Error('没有权限');
    }

    // 执行业务逻辑
    return '任务处理成功';
  }
}

2. 使用 CASL Ability 进行细粒度控制

CASL Ability 提供了更灵活的权限检查方式:

import { Injectable } from '@nestjs/common';
import { PermissionService, ROLE_SUBJECT } from '@lark-apaas/nestjs-authzpaas';

@Injectable()
export class ArticleService {
  constructor(private readonly permissionService: PermissionService) {}

  async canUserEditArticle(userId: string, articleId: string) {
    const ability = await this.permissionService.getAbility(userId);
    
    // 检查是否可以更新文章
    if (ability.can('update', 'Article')) {
      return true;
    }
    
    // 或者检查是否有 manage all 权限
    if (ability.can('manage', 'all')) {
      return true;
    }
    
    // 检查角色
    if (ability.can('admin', ROLE_SUBJECT)) {
      return true;
    }
    
    return false;
  }
}

权限 API 规范

请求格式

权限 API 的 endpoint 配置支持动态参数替换:

AuthZPaasModule.forRoot({
  permissionApi: {
    timeout: 5000,
  },
})

响应格式

您的权限 API 必须返回以下格式的 JSON 数据:

{
  "roles": ["role_admin", "role_editor"],
  "fetchedAt": "2024-01-01T00:00:00.000Z"
}

字段说明

  • userId: 用户ID(必需)
  • roles: 角色名称列表(必需)
    • 字符串数组 ['role_admin', 'role_editor']
  • fetchedAt: 获取时间(可选,SDK 会自动添加)

错误处理

1. 常见异常类型

| 异常 | 说明 | HTTP 状态码 | |------|------|------------| | PermissionDeniedException.unauthenticated() | 未认证(无 userId) | 403 | | PermissionDeniedException.roleRequired() | 缺少必需的角色 | 403 | | PermissionDeniedException.permissionDenied() | 缺少必需的权限 | 403 | | PermissionDeniedException.environmentRestricted() | 环境限制(如 IP 白名单) | 403 |

最佳实践

1. 使用 TypeScript 类型提示

充分利用 TypeScript 的类型系统:

import { 
  CanPermission, 
  CanRole,
  PermissionService,
  ROLE_SUBJECT 
} from '@lark-apaas/nestjs-authzpaas';

// TypeScript 会自动提供代码提示和类型检查

2. 测试建议

编写单元测试时,可以 mock PermissionService

describe('TasksController', () => {
  let controller: TasksController;
  let permissionService: PermissionService;

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      controllers: [TasksController],
      providers: [
        {
          provide: PermissionService,
          useValue: {
            getUserPermissions: jest.fn(),
            getAbility: jest.fn(),
            checkPermissions: jest.fn(),
          },
        },
      ],
    }).compile();

    controller = module.get<TasksController>(TasksController);
    permissionService = module.get<PermissionService>(PermissionService);
  });

  it('should check permissions', async () => {
    jest.spyOn(permissionService, 'checkPermissions').mockResolvedValue(true);
    
    // 测试逻辑...
  });
});

运行时权限 SDK(AuthorizationSDK)

AuthorizationSDK 提供对平台运行态权限 API 的封装,包括角色管理、成员管理和混合搜索三个子模块。SDK 自动从请求上下文获取 appIduserId,无需手动传递。

注入方式

import { Injectable } from '@nestjs/common';
import { AuthorizationSDK } from '@lark-apaas/nestjs-authzpaas';

@Injectable()
export class YourService {
  constructor(private readonly authzSDK: AuthorizationSDK) {}
}

AuthorizationSDK 暴露三个子对象:

| 属性 | 类型 | 说明 | |------|------|------| | authzSDK.roles | RolesAPI | 角色 CRUD | | authzSDK.members | MembersAPI | 角色成员管理 | | authzSDK.search | SearchAPI | 混合搜索(用户/部门/群组) |


roles — 角色管理

roles.list(params?)

获取角色列表。

const roles = await authzSDK.roles.list();
// 带参数
const roles = await authzSDK.roles.list({ needMember: true });

| 参数 | 类型 | 说明 | |------|------|------| | needMember | boolean | 是否返回角色成员信息 | | userID | string \| number | 可选,覆盖上下文中的 userId |

返回: ForceRoleDTO[]

roles.get(bizID)

获取单个角色详情。

const role = await authzSDK.roles.get('admin');

返回: ForceRoleDTO

roles.create(params)

创建角色。

const result = await authzSDK.roles.create({
  role: {
    bizID: 'editor',
    name: '编辑者',
    description: '可编辑内容',
  },
});
// result: { bizID: 'editor', apiID: '...' }

| 参数 | 类型 | 说明 | |------|------|------| | role.bizID | string | 业务 ID(唯一标识) | | role.name | string | 角色名称 | | role.description | string? | 角色描述 |

返回: CreateRoleResponse{ bizID, apiID }

roles.update(bizID, params)

更新角色。

await authzSDK.roles.update('editor', {
  role: { name: '高级编辑者', description: '更新后的描述' },
});

roles.delete(bizID)

删除角色。

await authzSDK.roles.delete('editor');

members — 成员管理

所有成员操作以角色的 bizID 为入口。成员通过 RoleMemberDTO 按类型分组:

interface RoleMemberDTO {
  userList?: UserSimpleDTO[];       // 用户列表
  departmentList?: DepartmentDTO[]; // 部门列表
  groupChatList?: ChatSimpleDTO[];  // 群组列表
}

members.list(bizID, params?)

获取角色下的成员列表。不传 type 时返回所有类型的成员。

// 获取所有类型成员
const res = await authzSDK.members.list('admin');

// 仅获取用户类型
const res = await authzSDK.members.list('admin', { type: 'User' });

// 分页
const res = await authzSDK.members.list('admin', { page: 1, page_size: 20 });

| 参数 | 类型 | 说明 | |------|------|------| | type | MemberType? | 成员类型过滤:'User' | 'Department' | 'GroupChat' | | page | number? | 页码 | | page_size | number? | 每页数量 |

返回: ListMembersResponse

interface ListMembersResponse {
  members: RoleMemberDTO;
  total: number;
  hasMore: boolean;
}

members.add(bizID, params)

向角色添加成员。支持同时添加用户、部门、群组。

// 添加用户
await authzSDK.members.add('admin', {
  members: { userList: [{ userID: '1826968659245100' }] },
});

// 添加部门
await authzSDK.members.add('admin', {
  members: { departmentList: [{ id: '7579138586559286811' }] },
});

// 添加群组
await authzSDK.members.add('admin', {
  members: { groupChatList: [{ chatID: '123456' }] },
});

members.remove(bizID, params)

从角色移除成员。SDK 会自动补充 allEmployeespublicpresetGroup 等默认参数。

await authzSDK.members.remove('admin', {
  members: { userList: [{ userID: '1826968659245100' }] },
});

members.clear(bizID)

清空角色下的所有成员。

await authzSDK.members.clear('admin');

search — 混合搜索

search.search(params)

搜索用户、部门、群组。不传 filters 时自动使用默认参数(每种类型 pageSize=20)。

// 最简用法
const res = await authzSDK.search.search({ query: '张三' });

// 自定义过滤
const res = await authzSDK.search.search({
  query: '张三',
  filters: {
    userParam: { commonParam: { searchable: true, pageSize: 10, offset: 0 } },
    departmentParam: { commonParam: { searchable: true, pageSize: 5, offset: 0 } },
    chatParam: { commonParam: { searchable: true, pageSize: 5, offset: 0 } },
  },
  includeExternalUser: false,
  includeExternalGroup: true,
});

| 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | query | string | — | 搜索关键词(必填) | | filters | FilterParams? | 三种类型各 pageSize=20 | 按类型配置搜索参数 | | includeExternalUser | boolean? | false | 是否包含外部用户 | | includeExternalGroup | boolean? | true | 是否包含外部群组 |

返回: SearchResponse

interface SearchResponse {
  result: SearchResult;
}

interface SearchResult {
  userResult?: { total?: number; items?: SearchUserEntity[] };
  departmentResult?: { total?: number; items?: DepartmentEntity[] };
  chatResult?: { total?: number; items?: SearchChatEntity[] };
}

完整使用示例

import { Controller, Get, Post, Delete, Param, Body, Query } from '@nestjs/common';
import { AuthorizationSDK } from '@lark-apaas/nestjs-authzpaas';

@Controller('api/authorization')
export class AuthorizationController {
  constructor(private readonly authzSDK: AuthorizationSDK) {}

  @Get('roles')
  listRoles() {
    return this.authzSDK.roles.list();
  }

  @Post('roles')
  createRole(@Body() dto: { role: { bizID: string; name: string; description?: string } }) {
    return this.authzSDK.roles.create(dto);
  }

  @Get('roles/:bizID/members')
  listMembers(@Param('bizID') bizID: string, @Query('type') type?: string) {
    return this.authzSDK.members.list(bizID, { type: type as any });
  }

  @Post('roles/:bizID/members')
  addMembers(@Param('bizID') bizID: string, @Body() dto: { members: any }) {
    return this.authzSDK.members.add(bizID, dto);
  }

  @Post('search')
  search(@Body() dto: { query: string }) {
    return this.authzSDK.search.search(dto);
  }
}

完整示例

查看 examples/node-demo 目录获取完整的工作示例,包括:

  • ✅ 模块配置和中间件设置
  • ✅ Mock 权限 API 实现
  • ✅ 角色和权限装饰器使用
  • ✅ 手动权限检查
  • ✅ CASL Ability 使用
  • ✅ 测试脚本

运行示例:

cd examples/node-demo
yarn install
yarn start

# 测试不同用户的权限
curl -H "x-user-id: user-admin" http://localhost:3000/demo/admin-only
curl -H "x-user-id: user-normal" http://localhost:3000/demo/reader-only

License

MIT

相关链接