@lark-apaas/coding-steering 0.1.6-alpha.1 → 0.1.6-alpha.10
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/README.md +11 -2
- package/package.json +1 -1
- package/steering/design-stack/skills/.gitkeep +0 -0
- package/steering/nestjs-react-fullstack/skills/authn-guide/SKILL.md +122 -0
- package/steering/nestjs-react-fullstack/skills/authz-guide/SKILL.md +174 -0
- package/steering/nestjs-react-fullstack/skills/authz-guide/references/dynamic-permission-guide.md +621 -0
- package/steering/nestjs-react-fullstack/skills/authz-guide/references/management-page-spec.md +505 -0
- package/steering/nestjs-react-fullstack/skills/authz-guide/references/runtime-role-controller-spec.md +203 -0
- package/steering/nestjs-react-fullstack/skills/authz-guide/references/sdk-examples.md +90 -0
- package/steering/nestjs-react-fullstack/skills/authz-guide/references/sdk-types.md +216 -0
- package/steering/nestjs-react-fullstack/skills/client-add-aily-web-chat/SKILL.md +139 -0
- package/steering/nestjs-react-fullstack/skills/client-builtins-file-storage-service/SKILL.md +405 -0
- package/steering/nestjs-react-fullstack/skills/client-builtins-user-service/SKILL.md +628 -0
- package/steering/nestjs-react-fullstack/skills/devops-guide/SKILL.md +119 -0
- package/steering/nestjs-react-fullstack/skills/feishu/SKILL.md +270 -0
- package/steering/nestjs-react-fullstack/skills/feishu/references/approval.md +214 -0
- package/steering/nestjs-react-fullstack/skills/feishu/references/attendance.md +163 -0
- package/steering/nestjs-react-fullstack/skills/feishu/references/bitable.md +309 -0
- package/steering/nestjs-react-fullstack/skills/feishu/references/calendar.md +190 -0
- package/steering/nestjs-react-fullstack/skills/feishu/references/contacts.md +160 -0
- package/steering/nestjs-react-fullstack/skills/feishu/references/doc.md +256 -0
- package/steering/nestjs-react-fullstack/skills/feishu/references/drive.md +103 -0
- package/steering/nestjs-react-fullstack/skills/feishu/references/events.md +198 -0
- package/steering/nestjs-react-fullstack/skills/feishu/references/id-convert.md +128 -0
- package/steering/nestjs-react-fullstack/skills/feishu/references/messaging.md +207 -0
- package/steering/nestjs-react-fullstack/skills/feishu/references/oauth.md +164 -0
- package/steering/nestjs-react-fullstack/skills/feishu/references/perm.md +90 -0
- package/steering/nestjs-react-fullstack/skills/feishu/references/wiki.md +164 -0
- package/steering/nestjs-react-fullstack/skills/openapi-guide/SKILL.md +267 -0
- package/steering/nestjs-react-fullstack/skills/plugin-guide/SKILL.md +582 -0
- package/steering/nestjs-react-fullstack/skills/plugin-guide/references/plugin-coding-guide.md +357 -0
- package/steering/nestjs-react-fullstack/skills/plugin-guide/references/table.md +513 -0
- package/steering/nestjs-react-fullstack/skills/react-hook-best-practices/SKILL.md +118 -0
- package/steering/nestjs-react-fullstack/skills/server-builtins-file-storage-service/SKILL.md +177 -0
- package/steering/nestjs-react-fullstack/skills/trigger-guide/SKILL.md +452 -0
- package/steering/nestjs-react-fullstack/skills/user-identity/SKILL.md +300 -0
- package/steering/nestjs-react-fullstack/skills/user-management-best-practices/SKILL.md +142 -0
- package/steering/nestjs-react-fullstack/skills_local/code-fix/SKILL.md +232 -0
- package/steering/nestjs-react-fullstack/skills_local/coding-guide/SKILL.md +585 -0
package/README.md
CHANGED
|
@@ -12,7 +12,9 @@ steering/
|
|
|
12
12
|
│ └── skills/ # cross-stack shared skills
|
|
13
13
|
├── <stack>/
|
|
14
14
|
│ ├── tech.md # always-loaded, injected into agent system prompt
|
|
15
|
-
│
|
|
15
|
+
│ ├── skills/<id>/SKILL.md # progressive disclosure, triggered by description match
|
|
16
|
+
│ └── skills_local/<id>/SKILL.md # local-only variant; overrides skills/<id> when syncing
|
|
17
|
+
│ # to a local-dev project (e.g. via lark-cli)
|
|
16
18
|
└── ...
|
|
17
19
|
```
|
|
18
20
|
|
|
@@ -24,9 +26,16 @@ Top-level directory names (other than `_common`) ARE the stackId — discovery i
|
|
|
24
26
|
|---|---|
|
|
25
27
|
| `steering/<stack>/tech.md` | `.agent/steering/tech.md` |
|
|
26
28
|
| `steering/<stack>/skills/<id>/**` | `.agent/steering/skills/<id>/**` |
|
|
29
|
+
| `steering/<stack>/skills_local/<id>/**` | `.agent/steering/skills/<id>/**` (local-dev sync only) |
|
|
27
30
|
| `steering/_common/skills/<id>/**` | `.agent/steering/skills/<id>/**` |
|
|
28
31
|
|
|
29
|
-
|
|
32
|
+
Precedence when the same `<id>` exists in multiple sources (lowest → highest, later wins):
|
|
33
|
+
`_common/skills` → `<stack>/skills` → `<stack>/skills_local` (local-dev sync only).
|
|
34
|
+
|
|
35
|
+
`skills_local` is a stack-specific variant intended for the local-development sync path
|
|
36
|
+
(`miaoda skills sync --local`, driven by `lark-cli apps`). The cloud sandbox sync path
|
|
37
|
+
ignores `skills_local` and uses `skills` as-is, so authors of `skills_local` content can
|
|
38
|
+
assume "本地 agent" semantics (no `ddl_sql` / `plugin_instance` / sandbox-log tools, etc.).
|
|
30
39
|
|
|
31
40
|
## Writing rules
|
|
32
41
|
|
package/package.json
CHANGED
|
File without changes
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: authn-guide
|
|
3
|
+
description: "Use when implementing authentication features: @NeedLogin decorator (AuthN is opt-in, public interfaces need no decorator), AuthNPaasGuard flow, or handling 401 errors. 触发词:认证, authn, 身份验证, 登录检查, NeedLogin, Public, 公开接口, AuthNPaasGuard, x-login-url, 401, 未授权"
|
|
4
|
+
steering: true
|
|
5
|
+
steering-topic: authn_guide
|
|
6
|
+
match-template-name: nestjs-react-fullstack
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# 身份认证(AuthN)编码指南
|
|
10
|
+
|
|
11
|
+
本 skill 专注于**接口认证**:装饰器、守卫流程、401 处理。
|
|
12
|
+
|
|
13
|
+
**用户身份**(`req.userContext` 字段定义、`useCurrentUserProfile`、`AuthNPaasService`、`lark_user_id`、妙搭 ↔ 飞书 ID 转换)请使用 [`user-identity`](../user-identity/SKILL.md) skill。本文也会在认证流程里出现 `req.userContext`,那是守卫语义,**字段含义**仍以 user-identity 为准。
|
|
14
|
+
|
|
15
|
+
## 一、功能决策树
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
用户需求
|
|
19
|
+
│
|
|
20
|
+
├─ 需要标记某些接口必须登录才能访问?
|
|
21
|
+
│ └─ 是 ──→ 使用 @NeedLogin() 装饰器(第二节)
|
|
22
|
+
│
|
|
23
|
+
├─ 需要让某些接口公开访问?
|
|
24
|
+
│ └─ 不需要任何装饰器 ──→ AuthNPaasGuard 默认放行未标 @NeedLogin() 的接口
|
|
25
|
+
│ (`@Public()` 是历史遗留 no-op,不要使用,详见第二节)
|
|
26
|
+
│
|
|
27
|
+
├─ 接口返回 401 / 登录跳转不对?
|
|
28
|
+
│ └─ 是 ──→ 第四节 · 401 未授权
|
|
29
|
+
│
|
|
30
|
+
└─ 想读取当前用户 ID / 角色 / 飞书 ID?
|
|
31
|
+
└─ 这属于"用户身份"范畴 ──→ 跳到 `user-identity` skill
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
> **相关技能**:用户身份/ID 转换/userContext 字段参见 `user-identity`;登录/登出/获取用户信息的 Dataloom SDK 操作参见 `client-builtins-user-service`;权限点位鉴权参见 `authz-guide`。
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 二、认证装饰器
|
|
39
|
+
|
|
40
|
+
### 模块注册
|
|
41
|
+
|
|
42
|
+
`AuthNPaasModule` 已通过 `PlatformModule.forRoot()` 自动注册,**无需手动导入**。模块全局生效,自动注册 `AuthNPaasGuard` 守卫。
|
|
43
|
+
|
|
44
|
+
### @NeedLogin()
|
|
45
|
+
|
|
46
|
+
标记接口需要登录。未登录用户访问时,守卫设置 `x-login-url` 响应头并返回 401。
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { Controller, Get } from '@nestjs/common';
|
|
50
|
+
import { NeedLogin } from '@lark-apaas/fullstack-nestjs-core';
|
|
51
|
+
|
|
52
|
+
// 控制器级别 — 所有路由都需要登录
|
|
53
|
+
@Controller('api/dashboard')
|
|
54
|
+
@NeedLogin()
|
|
55
|
+
export class DashboardController {
|
|
56
|
+
@Get()
|
|
57
|
+
getDashboard() { return '仪表板'; }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 方法级别 — 仅特定路由需要登录
|
|
61
|
+
@Controller('api')
|
|
62
|
+
export class ApiController {
|
|
63
|
+
@Get('public-data')
|
|
64
|
+
getPublicData() { return '公开数据'; }
|
|
65
|
+
|
|
66
|
+
@Get('private-data')
|
|
67
|
+
@NeedLogin()
|
|
68
|
+
getPrivateData() { return '私密数据'; }
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### ⚠️ @Public() 是 no-op,不要使用
|
|
73
|
+
|
|
74
|
+
`@lark-apaas/fullstack-nestjs-core` 仍然 export 了 `@Public()`,但 `AuthNPaasGuard` / `AuthZPaasGuard` 都**不读** `IS_PUBLIC_KEY`,整段是历史遗留死代码。Guard 默认 opt-in:**没标 `@NeedLogin()` 的接口直接放行**——这意味着公开接口本身就不需要任何装饰器。如果项目里有现存的 `@Public()` 用法,可以删掉。
|
|
75
|
+
|
|
76
|
+
### 认证流程
|
|
77
|
+
|
|
78
|
+
`AuthNPaasGuard` 是 **opt-in 模式**:未标记 `@NeedLogin()` 的接口默认放行,**公开接口无需任何装饰器**。
|
|
79
|
+
|
|
80
|
+
```text
|
|
81
|
+
请求 → Gateway 注入 x-larkgw-suda-webuser 头
|
|
82
|
+
→ UserContextMiddleware 解析 → req.userContext { userId, tenantId, appId, loginUrl, ... }
|
|
83
|
+
→ AuthNPaasGuard 检查:
|
|
84
|
+
有 @NeedLogin() 且无 userId → 设置 x-login-url + 401
|
|
85
|
+
其他(含未标记的接口)→ 放行
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
> `req.userContext` 的完整字段表(`userId` / `tenantId` / `roles` / `userName` 等)见 `user-identity` skill 第二节。
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 三、禁止行为清单
|
|
93
|
+
|
|
94
|
+
| 禁止行为 | 正确做法 |
|
|
95
|
+
|---------|---------|
|
|
96
|
+
| 单独注册 `AuthNPaasModule.forRoot()` | 已通过 `PlatformModule.forRoot()` 自动注册,无需手动导入 |
|
|
97
|
+
| 手动实例化 `AuthNPaasGuard` | 模块自动注册为全局守卫,无需手动 `@UseGuards()` |
|
|
98
|
+
| 用 `@Public()` 标记公开接口 | `@Public()` 是 no-op;公开接口不需要任何装饰器(opt-in 模式默认放行未标 `@NeedLogin()` 的接口) |
|
|
99
|
+
| 在未标 `@NeedLogin()` 的接口里依赖 `req.userContext.userId` 做业务判断 | 该接口默认放行未登录请求,`userId` 可能为 `undefined`,必须先判空 |
|
|
100
|
+
| 业务层自行返回 401 实现"未登录跳转" | 用 `@NeedLogin()` 让守卫统一处理,确保 `x-login-url` 头被正确写入 |
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 四、常见问题
|
|
105
|
+
|
|
106
|
+
### 401 未授权
|
|
107
|
+
|
|
108
|
+
1. 确认接口是否标记了 `@NeedLogin()`
|
|
109
|
+
2. 检查请求是否携带了正确的 Cookie(`credentials: 'include'`)
|
|
110
|
+
3. 确认 Gateway 是否正确注入了 `x-larkgw-suda-webuser` 头
|
|
111
|
+
4. 确认响应头是否包含 `x-login-url`,前端拦截器需读取此头执行跳转
|
|
112
|
+
|
|
113
|
+
### 登录跳转 URL 错误
|
|
114
|
+
|
|
115
|
+
1. 检查 `req.userContext.loginUrl` 是否被中间件正确解析
|
|
116
|
+
2. 确认应用部署环境(preview / online)对应的网关配置
|
|
117
|
+
|
|
118
|
+
### 公开接口被错误地要求登录
|
|
119
|
+
|
|
120
|
+
1. 检查 Controller 或父级是否打了 `@NeedLogin()`(继承到本方法);按 opt-in 默认行为,**移除装饰器**接口就会公开
|
|
121
|
+
2. **不要**用 `@Public()` 试图取消登录要求——它是 no-op,对 guard 无影响
|
|
122
|
+
3. 检查是否有自定义 Guard 比 `AuthNPaasGuard` 更早执行并主动拦了未登录请求
|
|
@@ -0,0 +1,174 @@
|
|
|
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
|
+
## 零、模式决策与实施
|
|
12
|
+
|
|
13
|
+
### 决策总表
|
|
14
|
+
|
|
15
|
+
| 信号 | 先 DESIGN? | 模式 | 实施章节 | CanRole |
|
|
16
|
+
|------|------------|------|---------|---------|
|
|
17
|
+
| "加权限控制"、"按角色控制可见性" | 否 | 静态角色鉴权 | 第一节 | ✅ 使用 |
|
|
18
|
+
| "应用内管理角色成员"、"角色管理页面" | ✅ 是 | 静态 + 运行态角色管理 | 第一节 + 第二节 | ✅ 使用 |
|
|
19
|
+
| "开启权限服务"、"设计权限体系"、"规划角色" | ✅ 是 | 按方案决定 | 按方案 | 按方案 |
|
|
20
|
+
| "动态配置权限"、"无需改代码调整权限"、"权限点位" | ✅ 是 | 动态权限点位(新建) | 第二节 + 第三节 | ⛔ 禁止 |
|
|
21
|
+
| "升级为动态权限"、"CanRole 迁移到 Can" | ✅ 是 | 动态权限点位(升级) | 第三节(升级分支) | ⛔ 禁止 |
|
|
22
|
+
|
|
23
|
+
> **⛔ 互斥硬规则**:选择动态权限点位鉴权后,**全部业务鉴权和管理 API 鉴权必须用 `@Can`/`<Can>`**,禁止混用 `CanRole`。需求明确需要动态权限时,禁止先用 `CanRole` 实现再升级为 `@Can`,直接进入第三节,一步到位。
|
|
24
|
+
|
|
25
|
+
> **⛔ 点位全覆盖**:编写鉴权代码时,必须对照权限设计方案的**功能权限表**,将每个权限点位逐一落实到对应的后端 API(`@CanRole` / `@Can`)和前端入口(`<CanRole>` / `<Can>`),完成后逐行核对确认无遗漏。
|
|
26
|
+
|
|
27
|
+
### DESIGN 前置步骤
|
|
28
|
+
|
|
29
|
+
"开启权限服务"/"设计权限体系"/"规划角色"/"升级鉴权模式"/"开启角色服务" → **必须先调用 `rbac_role_manager` tool 的 DESIGN action**,用户确认后再实施。日常"给某功能加权限"不触发 DESIGN。
|
|
30
|
+
|
|
31
|
+
> **相关技能**:角色的查询、创建、更新、模拟等 CLI 操作通过 `authz-cli` skill 执行。
|
|
32
|
+
|
|
33
|
+
### 403 统一处理(所有模式通用)
|
|
34
|
+
|
|
35
|
+
**⛔ 403 是正常 HTTP 响应(status 200 范畴外但不会触发 axios error)**,`axiosForBackend` 不会抛异常,必须在 `.then()` / `response` 层面检查 `status`,**禁止放在 `.catch()` 或 `try-catch` 的 catch 块中**——catch 永远捕获不到 403。
|
|
36
|
+
|
|
37
|
+
在前端统一请求层拦截 403 并抛错,**禁止**在业务组件中单独处理:
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
const response = await axiosForBackend(config);
|
|
41
|
+
// ⛔ 403 是正常响应,不会进 catch——必须在 response 层面检查
|
|
42
|
+
if (response.status === 403) throw new Error('无操作权限,请联系管理员分配角色');
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 平台的角色面板入口
|
|
46
|
+
|
|
47
|
+
**只允许在对话中给其入口链接,严禁写到代码中**:`[角色面板](BaseURL?openPanel=auth)` 或 `[角色面板](?openPanel=auth)`
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 一、静态角色鉴权
|
|
52
|
+
|
|
53
|
+
### 核心原则
|
|
54
|
+
|
|
55
|
+
1. 系统已内置角色权限表,无需建表
|
|
56
|
+
2. 统一使用 `useAuth()` 获取 `{ ability, isLoading }`,**禁止** `useAuthAbility` / `useCanRole`(已移除)
|
|
57
|
+
3. 必须处理 `isLoading`——加载期间 `ability.can()` 返回 false,不检查会误判无权限
|
|
58
|
+
4. 创建角色后必须编写鉴权代码,切忌只创建角色不编写代码
|
|
59
|
+
|
|
60
|
+
### 前端
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { CanRole, useAuth, ROLE_SUBJECT } from '@lark-apaas/client-toolkit/auth';
|
|
64
|
+
|
|
65
|
+
// 组件级 —— CanRole 内置 isLoading 保护,fallback 仅用于加载态占位(Skeleton/Spinner)
|
|
66
|
+
// ⛔ fallback 禁止传入 <Navigate> 等重定向组件
|
|
67
|
+
<CanRole roles={['admin', 'super_admin']} fallback={<MenuSkeleton />}>
|
|
68
|
+
<NavLink to="/admin">后台管理</NavLink>
|
|
69
|
+
</CanRole>
|
|
70
|
+
|
|
71
|
+
// 路由级 —— 必须用 ProtectedRoute + useAuth,禁止用 CanRole 做路由守卫
|
|
72
|
+
const ProtectedRoute: React.FC<{ children: React.ReactNode; requiredRoles: string[] }> = ({ children, requiredRoles }) => {
|
|
73
|
+
const { ability, isLoading } = useAuth();
|
|
74
|
+
if (isLoading) return <Loading />;
|
|
75
|
+
const hasPermission = requiredRoles.some((role) => ability.can(role, ROLE_SUBJECT));
|
|
76
|
+
return hasPermission ? <>{children}</> : <Navigate to="/unauthorized" replace />;
|
|
77
|
+
};
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 后端
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import { CanRole } from '@lark-apaas/fullstack-nestjs-core';
|
|
84
|
+
|
|
85
|
+
@CanRole(['admin']) // 单角色
|
|
86
|
+
@CanRole(['admin', 'editor']) // 多角色(OR 逻辑:任一即可)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 二、运行态角色管理(AuthorizationSDK)
|
|
92
|
+
|
|
93
|
+
**前置条件**:应用已开启角色服务。
|
|
94
|
+
|
|
95
|
+
### 核心原则
|
|
96
|
+
|
|
97
|
+
1. 必须通过 `AuthorizationSDK` 操作,禁止自行封装 HTTP 或操作数据库
|
|
98
|
+
2. 通过 NestJS 依赖注入获取实例(`constructor(private readonly authzSDK: AuthorizationSDK)`),禁止手动实例化
|
|
99
|
+
3. 先根据 SDK 出入参签名定义接口规格(DTO / API Path / 请求方式),再写 Controller 和前端代码
|
|
100
|
+
4. 管理页面严格对标规格,禁止自由发挥
|
|
101
|
+
|
|
102
|
+
### 实施指引
|
|
103
|
+
|
|
104
|
+
| 内容 | 参考文档 |
|
|
105
|
+
|------|---------|
|
|
106
|
+
| Controller 注入 + DTO | [runtime-role-controller-spec.md](references/runtime-role-controller-spec.md) |
|
|
107
|
+
| Shared 类型定义 | [runtime-role-controller-spec.md § Shared 类型](references/runtime-role-controller-spec.md) |
|
|
108
|
+
| 管理页面 UI(从 Step 0 开始,禁止跳步) | [management-page-spec.md](references/management-page-spec.md) |
|
|
109
|
+
| SDK 完整类型 | [sdk-types.md](references/sdk-types.md) |
|
|
110
|
+
| SDK 调用示例 | [sdk-examples.md](references/sdk-examples.md) |
|
|
111
|
+
|
|
112
|
+
**关键约束**:
|
|
113
|
+
- 成员按类型分组传递(`MemberMutationData`),不是扁平数组
|
|
114
|
+
- `allEmployees`/`public` 只读,包含「企业全员」或「互联网公开」的角色不支持删除
|
|
115
|
+
- `userID` 可不传,默认为当前登录用户
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 三、动态权限点位鉴权(`@Can` + `<Can>`)
|
|
120
|
+
|
|
121
|
+
**适用场景**:运行时配置「哪个角色拥有哪些权限」,无需改代码调整权限策略。
|
|
122
|
+
|
|
123
|
+
| | 静态角色鉴权 | 动态权限点位鉴权 |
|
|
124
|
+
|---|---|---|
|
|
125
|
+
| 判断依据 | 用户是否属于某角色 | 角色是否拥有某权限点位 |
|
|
126
|
+
| 配置方式 | 代码硬编码角色名 | 运行时管理页面配置 |
|
|
127
|
+
| 后端 | `@CanRole(['admin'])` | `@Can('create', 'Task')` |
|
|
128
|
+
| 前端 | `<CanRole roles={[...]}>` | `<Can action="read" subject="Task">` |
|
|
129
|
+
| 数据存储 | 平台角色 API | 平台角色 API + 业务库 `authz_permissions` 和 `authz_role_permissions` 表 |
|
|
130
|
+
|
|
131
|
+
**必须严格遵循 [dynamic-permission-guide.md](references/dynamic-permission-guide.md) 实施,从 Step 0 开始逐步执行。** 禁止跳步或自由发挥。
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## 四、禁止行为清单
|
|
137
|
+
|
|
138
|
+
| 禁止行为 | 正确做法 |
|
|
139
|
+
|----------|----------|
|
|
140
|
+
| 动态权限模式下使用 `CanRole`/`@CanRole`/`<CanRole>` | 全部用 `@Can`/`<Can>`,grep 确认零残留 |
|
|
141
|
+
| 需要动态权限时先落 CanRole 再升级 | 直接用 `@Can`/`<Can>`,一步到位 |
|
|
142
|
+
| 使用 `useAuthAbility` 或 `useCanRole` | 已移除,统一用 `useAuth()` |
|
|
143
|
+
| 不检查 `isLoading` 直接判断权限 | 必须先判断 `isLoading`,加载期间显示 Loading |
|
|
144
|
+
| `fallback` 中使用 `<Navigate>` 或重定向 | `fallback` 仅用于加载态占位(Skeleton/Spinner) |
|
|
145
|
+
| 绕过 AuthorizationSDK 自行封装接口 | 必须通过 SDK 操作运行时角色和权限点位 |
|
|
146
|
+
| 升级权限体系时未调用 DESIGN 就编码 | 先调用 DESIGN 产出方案,确认后再动手 |
|
|
147
|
+
| 在业务组件中单独处理 403 | API 层统一拦截 403 |
|
|
148
|
+
| 在 `.catch()` 或 catch 块中处理 403 | 403 不触发异常,必须在 response 层面检查 `status` |
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 五、常见问题
|
|
153
|
+
|
|
154
|
+
| 问题 | 处理方式 |
|
|
155
|
+
|------|---------|
|
|
156
|
+
| 403 错误 | 明确告知是无权限报错,确认是否符合预期;用 `miaoda-auth-cli` MOCK 模拟角色调试 |
|
|
157
|
+
| 开发环境授权不生效 | 引导用户使用 `miaoda-auth-cli` MOCK 功能 |
|
|
158
|
+
| 用户要求管理角色 | 开发态:`[角色面板](BaseURL?openPanel=auth)`;运行态:按第二节实施 |
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## 六、自查清单
|
|
163
|
+
|
|
164
|
+
- [ ] 创建角色后编写了对应鉴权代码
|
|
165
|
+
- [ ] 对照功能权限表,每个点位均已落实到后端 API 和前端入口,无遗漏
|
|
166
|
+
- [ ] 前端从 `@lark-apaas/client-toolkit/auth` 导入
|
|
167
|
+
- [ ] `useAuth()` 已处理 `isLoading` 状态
|
|
168
|
+
- [ ] 前端 API 层统一处理了 403
|
|
169
|
+
- [ ] 前后端权限规则一致
|
|
170
|
+
- [ ] 开发完成后告知:开发环境用 MOCK,线上去角色面板授权
|
|
171
|
+
- [ ] 动态权限:通过 `PlatformModule.forRoot({ authz })` 注册 resolver,未单独注册 `AuthZPaasModule`
|
|
172
|
+
- [ ] 动态权限:grep 确认 `CanRole`/`useCanRole`/`@CanRole`/`<CanRole>` 零残留
|
|
173
|
+
- [ ] 运行态:管理页面对照 [management-page-spec.md 检查表](references/management-page-spec.md)
|
|
174
|
+
- [ ] 接口端到端测试通过
|