@lark-apaas/coding-steering 0.1.17-alpha.0 → 0.1.17-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/steering/design-html/skills/make-a-deck/SKILL.md +2 -6
- package/steering/nestjs-react-fullstack/skills/client-builtins-user-service/SKILL.md +61 -375
- package/steering/nestjs-react-fullstack/{skills → skills_common}/user-identity/SKILL.md +12 -5
- package/steering/nestjs-react-fullstack/skills_local/authz-guide/SKILL.md +196 -0
- package/steering/nestjs-react-fullstack/skills_local/authz-guide/references/dynamic-permission-guide.md +643 -0
- package/steering/nestjs-react-fullstack/skills_local/authz-guide/references/management-page-spec.md +505 -0
- package/steering/nestjs-react-fullstack/skills_local/authz-guide/references/runtime-role-controller-spec.md +203 -0
- package/steering/nestjs-react-fullstack/skills_local/authz-guide/references/sdk-examples.md +92 -0
- package/steering/nestjs-react-fullstack/skills_local/authz-guide/references/sdk-types.md +229 -0
- package/steering/nestjs-react-fullstack/skills_local/client-builtins-user-service/SKILL.md +240 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: user-identity
|
|
3
|
-
description: "Use when getting current user info/profile, displaying user name/avatar/email, converting miaoda userId
|
|
3
|
+
description: "Use when getting current user info/profile, displaying user name/avatar/email, converting miaoda userId ↔ lark_user_id (both directions via AuthNPaasService), or reading req.userContext fields (userId/roles/tenantId): useCurrentUserProfile, AuthNPaasService, FeishuID conversion. 触发词:用户身份, 用户信息, 用户资料, 当前用户, userProfile, useCurrentUserProfile, 飞书ID, FeishuID, 飞书用户ID, lark_user_id, 用户ID转换, AuthNPaasService, getBatchMiaodaUserIds, 飞书ID转妙搭, employee_id 转 userId, 用户上下文, userContext, userContext.roles, 用户角色, 当前用户角色, 获取请求者角色, 展示用户, 显示用户, 用户面板, 我是谁, 获取用户"
|
|
4
4
|
steering: true
|
|
5
5
|
steering-topic: user_identity
|
|
6
6
|
match-template-name: nestjs-react-fullstack
|
|
@@ -77,7 +77,7 @@ match-template-name: nestjs-react-fullstack
|
|
|
77
77
|
│ └─ 跳到 `feishu` skill 的 `references/id-convert.md`(spark id_convert type 20/21)
|
|
78
78
|
│
|
|
79
79
|
├─ 需要把 飞书 user_id(employee_id)反查成 妙搭 userId?
|
|
80
|
-
│ └─
|
|
80
|
+
│ └─ 后端 ──→ `AuthNPaasService.getBatchMiaodaUserIds()`(第三节,SDK 一步,convertType 31);非模板项目 ──→ `feishu` skill 两步兜底
|
|
81
81
|
│
|
|
82
82
|
└─ 需要自定义飞书 ID 转换接口?
|
|
83
83
|
└─ 是 ──→ 注入 AuthNPaasService 编写 Controller(第四节,仅适用于 user_id)
|
|
@@ -102,7 +102,7 @@ match-template-name: nestjs-react-fullstack
|
|
|
102
102
|
| `appId` | `string` | 应用 ID |
|
|
103
103
|
| `loginUrl` | `string` | 登录跳转 URL |
|
|
104
104
|
| `userType` | `string` | 用户类型(如 `_employee`) |
|
|
105
|
-
| `env` | `string` |
|
|
105
|
+
| `env` | `string` | 环境(`preview` 预览态、`runtime` 发布运行态) |
|
|
106
106
|
| `userName` | `string` | 用户名 |
|
|
107
107
|
| `userNameI18n` | `{ zh_cn, en_us, ja_jp }` | 多语言用户名 |
|
|
108
108
|
| `isSystemAccount` | `boolean` | 是否系统账号 |
|
|
@@ -146,9 +146,9 @@ export class TasksController {
|
|
|
146
146
|
|
|
147
147
|
妙搭平台的用户 ID(`userId`)与飞书用户 ID 是两套独立体系。调用飞书 OpenAPI 时需要传入某种飞书侧 ID(`open_id` / `union_id` / `user_id` 任选其一,具体通过哪个参数指定取决于 API:消息 API 用 `receive_id_type`,多数其他 API 用 `user_id_type`,文档协作者用 `member_id_type`)。
|
|
148
148
|
|
|
149
|
-
`AuthNPaasService` 暴露的飞书 ID 是 **`user_id`**(即 `employee_id
|
|
149
|
+
`AuthNPaasService` 暴露的飞书 ID 是 **`user_id`**(即 `employee_id`,飞书企业内的用户标识)这一种,支持 **妙搭 userId ↔ 飞书 user_id 双向**转换(正向 `getBatchLarkUserIds`/`getCurrentUserLarkUserId`,反向 `getBatchMiaodaUserIds`)。
|
|
150
150
|
|
|
151
|
-
> 如果需要 `open_id` / `union_id
|
|
151
|
+
> 如果需要 `open_id` / `union_id`(无论正反向),请改用飞书开放平台 `spark id_convert` 接口,参见 `feishu` skill 的 `references/id-convert.md`。`employee_id` ↔ 妙搭 userId 双向都在本 SDK 内(见下方方法表)。
|
|
152
152
|
|
|
153
153
|
### 后端 API
|
|
154
154
|
|
|
@@ -168,6 +168,10 @@ export class MyService {
|
|
|
168
168
|
// 批量转换(最多 100 个)
|
|
169
169
|
const larkUserIds = await this.authnService.getBatchLarkUserIds(['uid1', 'uid2']);
|
|
170
170
|
// => ['<飞书 user_id>', null] 顺序与输入对应,失败项为 null
|
|
171
|
+
|
|
172
|
+
// 反向:飞书 user_id(employee_id) → 妙搭 userId
|
|
173
|
+
const miaodaUserIds = await this.authnService.getBatchMiaodaUserIds(['emp1', 'emp2']);
|
|
174
|
+
// => ['<妙搭 userId>', null] 顺序与输入对应,失败项为 null
|
|
171
175
|
}
|
|
172
176
|
}
|
|
173
177
|
```
|
|
@@ -176,6 +180,7 @@ export class MyService {
|
|
|
176
180
|
|------|------|------|
|
|
177
181
|
| `getCurrentUserLarkUserId` | `() → Promise<string \| null>` | 从请求上下文获取当前用户的飞书 ID |
|
|
178
182
|
| `getBatchLarkUserIds` | `(userIds: string[]) → Promise<(string \| null)[]>` | 批量转换,最多 100 个,与输入顺序一一对应 |
|
|
183
|
+
| `getBatchMiaodaUserIds` | `(employeeIds: string[]) → Promise<(string \| null)[]>` | 反向:批量把飞书 user_id(employee_id)转为妙搭 userId,最多 100 个,顺序一一对应,失败项 `null`(底层 convertType 31) |
|
|
179
184
|
|
|
180
185
|
### 内置接口
|
|
181
186
|
|
|
@@ -249,6 +254,8 @@ export class FeishuIdController {
|
|
|
249
254
|
}
|
|
250
255
|
```
|
|
251
256
|
|
|
257
|
+
> 反向转换(employee_id → 妙搭 userId)同理,把 `getBatchLarkUserIds` 换成 `getBatchMiaodaUserIds` 即可,无需新增 Controller。
|
|
258
|
+
|
|
252
259
|
前端调用示例:
|
|
253
260
|
|
|
254
261
|
```typescript
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: authz-guide
|
|
3
|
+
description: "Use when writing permission control code with CanRole/@Can/useCan decorator, managing roles/members at runtime via AuthorizationSDK, implementing dynamic permission-point-based auth, designing RBAC role/permission system, debugging 403 errors, or checking role panel entry. 触发词:权限控制, CanRole, @Can, useCan, RBAC, 403, permission, access control, 鉴权代码, 角色面板, 运行时角色, 成员管理, AuthorizationSDK, 权限点位, 动态鉴权, 权限配置, authz_permissions, authz_role_permissions, IPermissionResolver, 设计权限体系, 开启权限服务, 规划角色, 开启角色服务, 设计角色"
|
|
4
|
+
steering: true
|
|
5
|
+
steering-topic: authz_guide
|
|
6
|
+
match-template-name: nestjs-react-fullstack
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# RBAC 权限编码指南
|
|
10
|
+
|
|
11
|
+
## 本地 lark-cli 开发适配
|
|
12
|
+
|
|
13
|
+
本节只规定 `apps init` 后的本地工程识别、平台资源核验和验证流程,**不改变下方既有的权限模式、模板代码或页面规格**。
|
|
14
|
+
|
|
15
|
+
1. 以当前 `apps init` 工程为唯一项目根,先读取 `.spark/meta.json` 获取真实 `app_id`,再读取工程内 `.agents/skills/authz-guide/SKILL.md` 及当前模式引用的 reference。不得用目录名、用户口述的 ID 或工作区外同名 skill 代替。
|
|
16
|
+
2. 应用源码仍按本 skill 使用 `CanRole`、`AuthorizationSDK`、`@Can` / `<Can>` 实现;`lark-cli` 只负责平台角色、成员和应用数据库等外部资源操作,不能替代模板代码。
|
|
17
|
+
3. 代码或 SQL 使用具体角色标识前,先执行 `lark-cli apps +role-list --app-id <app_id> --as user --format json`;对每个已存在的目标 `role_id` 再执行 `lark-cli apps +role-get --app-id <app_id> --role-id <role_id> --as user --format json`。禁止把示例中的 `admin`、`editor` 等名称直接当作真实 `role_id`;新建角色则使用创建响应返回的 `role_id` 并独立回读。
|
|
18
|
+
4. 平台角色查询和变更统一使用 `lark-cli apps +role-*` / `+role-member-*`。真实写操作遵守命令自身的确认与回读要求;诊断和预演不得声称平台状态已经改变。
|
|
19
|
+
5. 收尾按项目 `package.json` 的真实脚本运行 typecheck、测试和 build,并确认新增页面、Controller、Module 已进入实际 router/bootstrap 注册链;只创建未接线文件不算完成。
|
|
20
|
+
|
|
21
|
+
如果请求只操作平台角色或成员、完全不修改应用源码,应停止使用本 skill,改用当前环境中的 `lark-apps` skill;只有应用代码改造才继续执行下方模板。
|
|
22
|
+
|
|
23
|
+
## 零、模式决策与实施
|
|
24
|
+
|
|
25
|
+
### 决策总表
|
|
26
|
+
|
|
27
|
+
| 信号 | 先 DESIGN? | 模式 | 实施章节 | CanRole |
|
|
28
|
+
|------|------------|------|---------|---------|
|
|
29
|
+
| "加权限控制"、"按角色控制可见性" | 否 | 静态角色鉴权 | 第一节 | ✅ 使用 |
|
|
30
|
+
| "应用内管理角色成员"、"角色管理页面" | ✅ 是 | 静态 + 运行态角色管理 | 第一节 + 第二节 | ✅ 使用 |
|
|
31
|
+
| "开启权限服务"、"设计权限体系"、"规划角色" | ✅ 是 | 按方案决定 | 按方案 | 按方案 |
|
|
32
|
+
| "动态配置权限"、"无需改代码调整权限"、"权限点位" | ✅ 是 | 动态权限点位(新建) | 第二节 + 第三节 | ⛔ 禁止 |
|
|
33
|
+
| "升级为动态权限"、"CanRole 迁移到 Can" | ✅ 是 | 动态权限点位(升级) | 第三节(升级分支) | ⛔ 禁止 |
|
|
34
|
+
|
|
35
|
+
> **⛔ 互斥硬规则**:选择动态权限点位鉴权后,**全部业务鉴权和管理 API 鉴权必须用 `@Can`/`<Can>`**,禁止混用 `CanRole`。需求明确需要动态权限时,禁止先用 `CanRole` 实现再升级为 `@Can`,直接进入第三节,一步到位。
|
|
36
|
+
|
|
37
|
+
> **⛔ 点位全覆盖**:编写鉴权代码时,必须对照权限设计方案的**功能权限表**,将每个权限点位逐一落实到对应的后端 API(`@CanRole` / `@Can`)和前端入口(`<CanRole>` / `<Can>`),完成后逐行核对确认无遗漏。
|
|
38
|
+
|
|
39
|
+
> **⛔ 角色来源一律是平台,禁止另搞一套平台不认的权限系统**:多级审批、按城市/部门分权、多角色组合等复杂授权,角色必须是**平台真实角色**(先用 `lark-cli apps +role-list` 查询;需要创建时使用 `+role-create`,并让返回的 `role_id` 与代码对齐)。**消费平台角色的方式不限**——`@CanRole`/`@Can` 是语法糖;在 service 里读 `req.userContext.roles` 再判断是否包含已核验的角色标识,同样是走平台机制(roles 来自平台),按场景选用即可,**不强制每个 API 都用 `@CanRole`**(注解 cover 不了的细粒度场景就读 `userContext.roles` 自行判断)。**真正要禁止的是绕开平台另起炉灶**:自建任何平行的角色/权限存储(自建角色表 / 角色字段 / 用户名单,不限具体命名)、引用平台上不存在的角色标识——这些 dev 看似能跑、线上必失效。复杂度用「**平台角色(组合)+ 数据层行级过滤**」表达——例如"审核人只审本城市"=`reviewer` 角色把门 + service 按 `cityBranchId` 过滤;"多级审批"=每级一个平台角色 + 把"当前在第几级"存成业务数据状态。
|
|
40
|
+
|
|
41
|
+
### DESIGN 前置步骤
|
|
42
|
+
|
|
43
|
+
"开启权限服务"/"设计权限体系"/"规划角色"/"升级鉴权模式"/"开启角色服务" → **必须先结合现有代码和平台真实角色产出结构化权限设计方案**,用户确认后再实施。日常"给某功能加权限"不触发 DESIGN。
|
|
44
|
+
|
|
45
|
+
> **本地命令**:角色查询、创建、更新、成员维护和用户角色匹配分别使用 `lark-cli apps +role-*`、`+role-member-*`、`+role-match-list`;命令参数以当前 `lark-apps` skill 和 `--help` 为准。
|
|
46
|
+
|
|
47
|
+
### 403 统一处理(所有模式通用)
|
|
48
|
+
|
|
49
|
+
403 进入 response 分支还是 reject 分支取决于当前工程 `axiosForBackend` 的 `validateStatus` 和拦截器合同,**禁止断言 catch 永远捕获不到 403**。先读取工程内真实请求封装;在统一 API 层同时按其实际合同识别 403,业务组件只消费归一化后的无权限错误。
|
|
50
|
+
|
|
51
|
+
若当前请求器会 resolve 403,则在前端统一请求层按 response 检查并抛错,**禁止**在业务组件中单独处理:
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
const response = await axiosForBackend(config);
|
|
55
|
+
if (response.status === 403) throw new Error('无操作权限,请联系管理员分配角色');
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
若请求器会 reject 403,则在同一 API 层从结构化错误中读取 HTTP status 后转换,并保留原始 cause;不要在页面 catch 中用错误字符串猜测。
|
|
59
|
+
|
|
60
|
+
本地只读排查 403 时,读取 `.spark/meta.json` 和真实 handler/policy 后,依次用 `+role-list`、`+role-match-list --user-id <open_id>`、`+role-get --role-id <required_role_id>` 对比“代码要求角色”和“用户实际命中角色”。只有代码绑定链和平台结果都能对上时才下根因结论,不为排查自动创建角色、添加成员或修改可见范围。
|
|
61
|
+
|
|
62
|
+
### 平台的角色面板入口
|
|
63
|
+
|
|
64
|
+
**只允许在对话中给其入口链接,严禁写到代码中**:`[角色面板](BaseURL?openPanel=auth)` 或 `[角色面板](?openPanel=auth)`
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 一、静态角色鉴权
|
|
69
|
+
|
|
70
|
+
### 核心原则
|
|
71
|
+
|
|
72
|
+
1. 系统已内置角色权限表,**无需也禁止自建任何平行的角色/权限存储**(自建角色表 / 角色字段 / 用户名单等,不限具体命名)——@CanRole 只读平台角色,应用自建的存储对鉴权无效
|
|
73
|
+
2. 统一使用 `useAuth()` 获取 `{ ability, isLoading }`,**禁止** `useAuthAbility` / `useCanRole`(已移除)
|
|
74
|
+
3. 必须处理 `isLoading`——加载期间 `ability.can()` 返回 false,不检查会误判无权限
|
|
75
|
+
4. 创建角色后必须编写鉴权代码,切忌只创建角色不编写代码
|
|
76
|
+
5. **`@CanRole([X])`/`<CanRole roles={[X]}>` 里的 X 必须等于平台实际角色标识,不能凭语义臆造**——需要稳定标识时用 `lark-cli apps +role-create --app-id <app_id> --name <name> --role-id <X> --as user` 创建并回读,已有角色先用 `+role-list` / `+role-get` 取得真实 `role_id` 再写代码。若代码写 `'admin'`/`'reviewer'` 但平台分配给用户的角色标识是 `role_xxx`,两者对不上 → 线上恒 403(日志可见 `用户角色 [role_xxx], 需要 [admin, reviewer]`)
|
|
77
|
+
|
|
78
|
+
### 前端
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { CanRole, useAuth, ROLE_SUBJECT } from '@lark-apaas/client-toolkit/auth';
|
|
82
|
+
|
|
83
|
+
// 组件级 —— CanRole 内置 isLoading 保护,fallback 仅用于加载态占位(Skeleton/Spinner)
|
|
84
|
+
// ⛔ fallback 禁止传入 <Navigate> 等重定向组件
|
|
85
|
+
<CanRole roles={['admin', 'super_admin']} fallback={<MenuSkeleton />}>
|
|
86
|
+
<NavLink to="/admin">后台管理</NavLink>
|
|
87
|
+
</CanRole>
|
|
88
|
+
|
|
89
|
+
// 路由级 —— 必须用 ProtectedRoute + useAuth,禁止用 CanRole 做路由守卫
|
|
90
|
+
const ProtectedRoute: React.FC<{ children: React.ReactNode; requiredRoles: string[] }> = ({ children, requiredRoles }) => {
|
|
91
|
+
const { ability, isLoading } = useAuth();
|
|
92
|
+
if (isLoading) return <Loading />;
|
|
93
|
+
const hasPermission = requiredRoles.some((role) => ability.can(role, ROLE_SUBJECT));
|
|
94
|
+
return hasPermission ? <>{children}</> : <Navigate to="/unauthorized" replace />;
|
|
95
|
+
};
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 后端
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
import { CanRole } from '@lark-apaas/fullstack-nestjs-core';
|
|
102
|
+
|
|
103
|
+
@CanRole(['admin']) // 单角色
|
|
104
|
+
@CanRole(['admin', 'editor']) // 多角色(OR 逻辑:任一即可)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 二、运行态角色管理(AuthorizationSDK)
|
|
110
|
+
|
|
111
|
+
**前置条件**:应用已开启角色服务。
|
|
112
|
+
|
|
113
|
+
### 核心原则
|
|
114
|
+
|
|
115
|
+
1. 必须通过 `AuthorizationSDK` 操作,禁止自行封装 HTTP 或操作数据库
|
|
116
|
+
2. 通过 NestJS 依赖注入获取实例(`constructor(private readonly authzSDK: AuthorizationSDK)`),禁止手动实例化
|
|
117
|
+
3. 先根据 SDK 出入参签名定义接口规格(DTO / API Path / 请求方式),再写 Controller 和前端代码
|
|
118
|
+
4. 管理页面严格对标规格,禁止自由发挥
|
|
119
|
+
|
|
120
|
+
### 实施指引
|
|
121
|
+
|
|
122
|
+
| 内容 | 参考文档 |
|
|
123
|
+
|------|---------|
|
|
124
|
+
| Controller 注入 + DTO | [runtime-role-controller-spec.md](references/runtime-role-controller-spec.md) |
|
|
125
|
+
| Shared 类型定义 | [runtime-role-controller-spec.md § Shared 类型](references/runtime-role-controller-spec.md) |
|
|
126
|
+
| 管理页面 UI(从 Step 0 开始,禁止跳步) | [management-page-spec.md](references/management-page-spec.md) |
|
|
127
|
+
| SDK 完整类型 | [sdk-types.md](references/sdk-types.md) |
|
|
128
|
+
| SDK 调用示例 | [sdk-examples.md](references/sdk-examples.md) |
|
|
129
|
+
|
|
130
|
+
**关键约束**:
|
|
131
|
+
- 成员按类型分组传递(`MemberMutationData`),不是扁平数组
|
|
132
|
+
- `allEmployees`/`public` 只读,包含「企业全员」或「互联网公开」的角色不支持删除
|
|
133
|
+
- `userID` 可不传,默认为当前登录用户
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 三、动态权限点位鉴权(`@Can` + `<Can>`)
|
|
138
|
+
|
|
139
|
+
**适用场景**:运行时配置「哪个角色拥有哪些权限」,无需改代码调整权限策略。
|
|
140
|
+
|
|
141
|
+
| | 静态角色鉴权 | 动态权限点位鉴权 |
|
|
142
|
+
|---|---|---|
|
|
143
|
+
| 判断依据 | 用户是否属于某角色 | 角色是否拥有某权限点位 |
|
|
144
|
+
| 配置方式 | 代码硬编码角色名 | 运行时管理页面配置 |
|
|
145
|
+
| 后端 | `@CanRole(['admin'])` | `@Can('create', 'Task')` |
|
|
146
|
+
| 前端 | `<CanRole roles={[...]}>` | `<Can action="read" subject="Task">` |
|
|
147
|
+
| 数据存储 | 平台角色 API | 平台角色 API + 业务库 `authz_permissions` 和 `authz_role_permissions` 表 |
|
|
148
|
+
|
|
149
|
+
**必须严格遵循 [dynamic-permission-guide.md](references/dynamic-permission-guide.md) 实施,从 Step 0 开始逐步执行。** 禁止跳步或自由发挥。
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## 四、禁止行为清单
|
|
155
|
+
|
|
156
|
+
| 禁止行为 | 正确做法 |
|
|
157
|
+
|----------|----------|
|
|
158
|
+
| 动态权限模式下使用 `CanRole`/`@CanRole`/`<CanRole>` | 全部用 `@Can`/`<Can>`,grep 确认零残留 |
|
|
159
|
+
| 需要动态权限时先落 CanRole 再升级 | 直接用 `@Can`/`<Can>`,一步到位 |
|
|
160
|
+
| 使用 `useAuthAbility` 或 `useCanRole` | 已移除,统一用 `useAuth()` |
|
|
161
|
+
| 不检查 `isLoading` 直接判断权限 | 必须先判断 `isLoading`,加载期间显示 Loading |
|
|
162
|
+
| `fallback` 中使用 `<Navigate>` 或重定向 | `fallback` 仅用于加载态占位(Skeleton/Spinner) |
|
|
163
|
+
| 绕过 AuthorizationSDK 自行封装接口 | 必须通过 SDK 操作运行时角色和权限点位 |
|
|
164
|
+
| 升级权限体系时未完成 DESIGN 就编码 | 先基于现有代码和平台角色产出方案,确认后再动手 |
|
|
165
|
+
| 在业务组件中单独处理 403 | API 层统一拦截 403 |
|
|
166
|
+
| 自建任意平行的角色/权限存储(自建角色表 / 角色字段 / 用户名单,不限具体命名)来做鉴权判断 | @CanRole 只认平台角色,应用自建的存储不会被鉴权读取;角色一律用平台真实角色 |
|
|
167
|
+
| 复杂授权就**另起炉灶搞一套平台不认的权限**(自建角色表 / 引用平台没 create 过的标识 / 写死 user_id 名单) | 角色一律用平台真实角色;消费方式不限(`@CanRole` 或读 `req.userContext.roles` 都行),复杂度用「平台角色 + 数据层行级过滤」表达 |
|
|
168
|
+
| `@CanRole([X])` 的 X 凭语义臆造、与平台真实角色标识不一致 | 用 `lark-cli apps +role-list` / `+role-get` 获取真实 `role_id`;需要稳定 ID 时创建角色显式传 `--role-id` |
|
|
169
|
+
| 未读取请求器合同就断言 403 只会进入 response 或 catch | 按实际 `validateStatus` / 拦截器合同在统一 API 层处理两种交付方式 |
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## 五、常见问题
|
|
174
|
+
|
|
175
|
+
| 问题 | 处理方式 |
|
|
176
|
+
|------|---------|
|
|
177
|
+
| 403 错误 | 明确告知是无权限报错;用 `+role-match-list` 查询用户实际命中角色,并用 `+role-get` 核验代码要求的角色 |
|
|
178
|
+
| 线上 403 但 dev 正常 / 用户"已授权"仍 403 | 先核对两件事:(1) `+role-match-list` 返回的真实 `role_id` 是否等于代码 `@CanRole` 里的字符串;(2) 是否另搞了一套平台不认的权限(自建角色表 / 引用平台不存在的标识 / user_id 名单)绕开平台。再结合 handler/policy 的真实绑定链判断根因 |
|
|
179
|
+
| 开发环境授权不生效 | 用 `+role-match-list` 和 `+role-member-list` 核对平台真实状态,不尝试本地模拟角色 |
|
|
180
|
+
| 用户要求管理角色 | 开发态:`[角色面板](BaseURL?openPanel=auth)`;运行态:按第二节实施 |
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## 六、自查清单
|
|
185
|
+
|
|
186
|
+
- [ ] 创建角色后编写了对应鉴权代码
|
|
187
|
+
- [ ] 对照功能权限表,每个点位均已落实到后端 API 和前端入口,无遗漏
|
|
188
|
+
- [ ] 前端从 `@lark-apaas/client-toolkit/auth` 导入
|
|
189
|
+
- [ ] `useAuth()` 已处理 `isLoading` 状态
|
|
190
|
+
- [ ] 前端 API 层统一处理了 403
|
|
191
|
+
- [ ] 前后端权限规则一致
|
|
192
|
+
- [ ] 开发完成后已用 `+role-match-list` 核对测试用户的真实角色;需要人工授权时给出角色面板入口
|
|
193
|
+
- [ ] 动态权限:通过 `PlatformModule.forRoot({ authz })` 注册 resolver,未单独注册 `AuthZPaasModule`
|
|
194
|
+
- [ ] 动态权限:grep 确认 `CanRole`/`useCanRole`/`@CanRole`/`<CanRole>` 零残留
|
|
195
|
+
- [ ] 运行态:管理页面对照 [management-page-spec.md 检查表](references/management-page-spec.md)
|
|
196
|
+
- [ ] 接口端到端测试通过
|