feat(role): role bind

This commit is contained in:
2026-01-31 17:23:56 +08:00
parent 4dc46659c9
commit 41cdbb5b29
30 changed files with 1773 additions and 52 deletions

View File

@@ -149,6 +149,17 @@
下一步依赖:`access_token`
(可选)使用 refresh_token 自动续期:
- **POST** `/auth/refresh`
- Tag`Auth`
- Header
- Body
```json
{ "refresh_token": "<opaque>" }
```
### Step 3获取当前租户信息Tenant
**GET** `/tenants/me`
@@ -319,6 +330,133 @@ enabled_apps 维护建议:
{ "code": 0, "message": "Success", "data": [{ "id": "<role_id>", "name": "Admin", "description": "..." }], "trace_id": null }
```
## Role & Permission Management
本节补齐“角色Role与权限Permission管理”的完整指引适用于横向扩展到多个应用`cms` / `tms` / `iam` 等)。
### 权限模型(可扩展到十几个应用)
权限编码规范:
- `permission.code` 统一采用:`${app_code}:${resource}:${action}`
- 示例:`cms:article:publish``tms:task:assign``iam:tenant:enabled_apps:write`
- `app_code` 必须与应用注册表app_registry中的应用标识一致本项目对应表为 `apps.id`)。
- 支持通配符(需要在 `permissions.code` 中显式存储通配符权限并分配到角色):
- `cms:*:*`CMS 全权限
- `cms:article:*`:文章相关全权限
> 说明:鉴权时支持通配符匹配(例如持有 `cms:article:*` 可满足 `cms:article:publish`),但只有当该通配符权限被写入 `permissions` 并绑定到角色时才会生效。
数据库关系ER 图):
```mermaid
erDiagram
apps {
VARCHAR id PK
VARCHAR name
VARCHAR status
}
permissions {
UUID id PK
VARCHAR code
TEXT description
VARCHAR resource
VARCHAR action
}
users {
UUID id PK
UUID tenant_id FK
VARCHAR email
}
roles {
UUID id PK
UUID tenant_id FK
VARCHAR name
BOOLEAN is_system
}
role_permissions {
UUID role_id FK
UUID permission_id FK
}
user_roles {
UUID user_id FK
UUID role_id FK
}
apps ||--o{ permissions : namespaces
roles ||--o{ role_permissions : grants
permissions ||--o{ role_permissions : assigned
users ||--o{ user_roles : has
roles ||--o{ user_roles : assigned
```
应用区分方式:
- 通过 `apps`app_registry维护“允许的应用标识”并要求权限码的 `app_code``apps.id` 一致。
- 在租户侧通过 `enabled_apps` 控制某租户开通哪些应用;登录/权限查询会按 `enabled_apps` 过滤应用级权限(例如 `cms:*:*` 会在租户未开通 `cms` 时被过滤)。
### 1) 为 CMS 添加最小必要权限SQL
脚本位置:`scripts/db/migrations/0006_cms_permissions.sql`
包含权限项:
- 文章:创建、编辑、发布
- 栏目:管理
- 媒体库:管理
- 系统配置:管理
### 2) 查询权限列表(分页/搜索)
**GET** `/permissions?page=1&page_size=20&app_code=cms&search=article`
- Tag`Permission`
- Header`Authorization: Bearer <access_token>`
- 需要权限:`role:read`
### 3) 角色 CRUDRole
创建角色:
- **POST** `/roles`
查询角色详情:
- **GET** `/roles/{id}`
更新角色:
- **PATCH** `/roles/{id}`
删除角色:
- **DELETE** `/roles/{id}`
说明:
- 系统内置角色(`is_system=true`,如 `Admin`)不允许通过 API 修改/删除。
### 4) 为角色批量绑定/解绑权限
绑定权限(批量):
- **POST** `/roles/{id}/permissions/grant`
- Body
```json
{ "permission_codes": ["cms:article:create", "cms:article:publish"] }
```
解绑权限(批量):
- **POST** `/roles/{id}/permissions/revoke`
### 5) 批量给用户授予/回收角色
批量授予:
- **POST** `/roles/{id}/users/grant`
- Body
```json
{ "user_ids": ["<user_id_1>", "<user_id_2>"] }
```
批量回收:
- **POST** `/roles/{id}/users/revoke`
审计说明:
- 角色创建/更新/删除、角色-权限绑定/解绑、角色-用户授予/回收都会写入 `audit_logs`
### Step 7用户-角色绑定User
用户注册后默认无角色;通常由具备 `user:write` 的管理员进行角色分配。

View File

@@ -1,23 +1,55 @@
`00000000-0000-0000-0000-000000000001`
`superadmin@example.com`
`superadmin1234`
```json
{
"code": 0,
"message": "Success",
"data": {
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiI3YTgzYmJjMC0yZjA1LTQwMDItOGFmZC0yYWI1M2RkZDMxNWIiLCJ0ZW5hbnRfaWQiOiI0ZDc3OTQxNC1kYTA0LTQ5YzMtYjM0Mi1kYWJmOTNiNmExMTkiLCJleHAiOjE3Njk3NTc3MTAsImlhdCI6MTc2OTc1NjgxMCwiaXNzIjoiaWFtLXNlcnZpY2UiLCJyb2xlcyI6WyJBZG1pbiJdLCJwZXJtaXNzaW9ucyI6WyJyb2xlOnJlYWQiLCJyb2xlOndyaXRlIiwidGVuYW50OnJlYWQiLCJ0ZW5hbnQ6d3JpdGUiLCJ1c2VyOnJlYWQiLCJ1c2VyOndyaXRlIl19.VGsoZdMwodRWKW4NQuQwezh3xZivFbRUzSw_-RnD-EJIv7qPHmcNbdIcxNSKXCHGKdK_b1B3404m7ji2wdEOweKz0GEcwPWswc9fannP5_6l9k83jn0ZKQ1pS3l27V5mr9feym_83ZIqEtFfKcCKGIM684Ze7CMM6i-gfYisn0poG1XW3K4ptsVnuNZux0TWNFl5TO6kgiw0_399tZnSH5qc4CckHOuoF3Jz1Q2aIgnvyfxbxEFTNZm-ykjhlbK5zWBpYfJdYOALQg-FQ3eGuVnSF4U_If1MNQKQ0p6DqDKMCO0IfdCr2WMBvfCYA1SxmPbETr2Tm7RguhJBEiVQ4Q",
"refresh_token": "e1649e730ef3583cd80087f7fa63774330deb88e81aec2edae41322764e441eb",
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiI4YWRlYzEzOC0wMzU5LTQ5NjgtYjAzNy03ZDI3Nzg2NTZhNjIiLCJ0ZW5hbnRfaWQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDEiLCJleHAiOjE3Njk4NTA1ODIsImlhdCI6MTc2OTg0OTY4MiwiaXNzIjoiaWFtLXNlcnZpY2UiLCJyb2xlcyI6WyJBZG1pbiJdLCJwZXJtaXNzaW9ucyI6WyJpYW06dGVuYW50OmVuYWJsZWRfYXBwczpyZWFkIiwiaWFtOnRlbmFudDplbmFibGVkX2FwcHM6d3JpdGUiLCJyb2xlOnJlYWQiLCJyb2xlOndyaXRlIiwidGVuYW50OnJlYWQiLCJ0ZW5hbnQ6d3JpdGUiLCJ1c2VyOnBhc3N3b3JkOnJlc2V0OmFueSIsInVzZXI6cmVhZCIsInVzZXI6d3JpdGUiXSwiYXBwcyI6W10sImFwcHNfdmVyc2lvbiI6MH0.JouDbnuLxCIXl4CpZ3G3zcwILohS1Dv6_k-2pV_5Iezrnk9IGlnmv-TqhP1BB0_A88etOIV9EPx8LVD0dJQaci_jABVr9ciQU126yu8GeTkoJTKDXuroornAxv6C7K2qp7jl9lfxgmm0h0jYla79YdEW8RdDu8q9VojjVNnyiT-0thOKRJddflKjovOrlOsVpqjtZvf_F86e9vEFBFImUs8_xuWo3gUUysbINbZc-I-DGRqIC95pVOOts3LzashkIVlPl4akXpbukSJjwNd9WnTSr9zf-7bN_YBFJGSutLor74cMlklZ6k828GmHrsuAIxyZMqoeTLrrsG3VcUjYKQ",
"refresh_token": "46d067317f9a6181255d151b099311b609683baa0c25ff62a81a3e6e7d8a9942",
"token_type": "Bearer",
"expires_in": 900
}
}
```
`4d779414-da04-49c3-b342-dabf93b6a119`
`shay7sev@gmail.com`
`tenantdev1`
```json
{
"code": 0,
"message": "Success",
"data": {
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJmOTg5MWI2Yy0xNTQ1LTQ0NGItODIxOC0yNTQ5MDA1NDczMGYiLCJ0ZW5hbnRfaWQiOiI0ZDc3OTQxNC1kYTA0LTQ5YzMtYjM0Mi1kYWJmOTNiNmExMTkiLCJleHAiOjE3Njk3NTg4NDMsImlhdCI6MTc2OTc1Nzk0MywiaXNzIjoiaWFtLXNlcnZpY2UiLCJyb2xlcyI6W10sInBlcm1pc3Npb25zIjpbXX0.Is-JjQ9l1BuZoYVr6QAt-4ZSfRQro3COPSVYHVl1NI0CAz7T2x9Hz2QiJPFamjsX7cFrCIJxNMn9ioqK2FzEnSTu2oVATqlhE5OMCcK1M7Mq_FvZ7WqGgPl8CE06s7yvneC97mknk5y1-nm5claYYGeHAjLrRPbjO2t3zUQO5boNPdjzEGx4kTFvgmJbwWMrsBtkeaW1nacxhFiSj-RFCSzHOOaSRoKLDsx9nUsuDJL1NCaHDuKDacphkwpjP5AWLd41hlrs6PC8XLUPey2EXHqJ5SmOaDdQ60LfItvohgHBTY6CO8IUIJgtZobrFsKUlnHqA9eZwm2dvAW560g4VA",
"refresh_token": "71e3ba6285b503891294ca7dad81cdc6bd5b3f72b09b1e2b796979a433d687f3",
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiI3YTgzYmJjMC0yZjA1LTQwMDItOGFmZC0yYWI1M2RkZDMxNWIiLCJ0ZW5hbnRfaWQiOiI0ZDc3OTQxNC1kYTA0LTQ5YzMtYjM0Mi1kYWJmOTNiNmExMTkiLCJleHAiOjE3Njk4NTEyOTIsImlhdCI6MTc2OTg1MDM5MiwiaXNzIjoiaWFtLXNlcnZpY2UiLCJyb2xlcyI6WyJBZG1pbiJdLCJwZXJtaXNzaW9ucyI6WyJyb2xlOnJlYWQiLCJyb2xlOndyaXRlIiwidGVuYW50OnJlYWQiLCJ0ZW5hbnQ6d3JpdGUiLCJ1c2VyOnBhc3N3b3JkOnJlc2V0OmFueSIsInVzZXI6cmVhZCIsInVzZXI6d3JpdGUiXSwiYXBwcyI6WyJjbXMiXSwiYXBwc192ZXJzaW9uIjoxfQ.jt1os6re3yhxBk4wfmBjy1_Qh8n5nkfwe8ptn-yi7Vws_MOepOAmdxqSY_sabOnvGZ74Rq2EFSDpOaan4HuPln35Vlt6-CPlEu5eikLu3AIBl7sZfGoOquHwnybuOwo8b5oFwgAWF0bqSmn1v--LdGvv7vX4zWiKxK6GeeCTZ8279GqO70tl4o6ug2swSMqPbspL-ZwnWrnvFRhfZkyrRmM6jn3TVUMFWX3FfTlm68lNl_UPj9OcUPvbIXFL3X-h8qk-W1Dq2hV_Z1WxjkwVV0XEa0iwz12Mb_-QFys2xLSXSxL4ubUJhV2RVQ2WmW-I0njLEJAQ5oR56nZi7XMZHA",
"refresh_token": "982236b2f680366a895768df1ffc29bf4bbc09eb82d6a88a4413a07f66b3badb",
"token_type": "Bearer",
"expires_in": 900
}
}
```
`user1@example.com`
`user1secret`
```json
{
"code": 0,
"message": "Success",
"data": {
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJmOTg5MWI2Yy0xNTQ1LTQ0NGItODIxOC0yNTQ5MDA1NDczMGYiLCJ0ZW5hbnRfaWQiOiI0ZDc3OTQxNC1kYTA0LTQ5YzMtYjM0Mi1kYWJmOTNiNmExMTkiLCJleHAiOjE3Njk4NTE1NDYsImlhdCI6MTc2OTg1MDY0NiwiaXNzIjoiaWFtLXNlcnZpY2UiLCJyb2xlcyI6WyJlZGl0b3IiXSwicGVybWlzc2lvbnMiOlsiY21zOmFydGljbGU6Y3JlYXRlIiwiY21zOmFydGljbGU6ZWRpdCIsImNtczphcnRpY2xlOnB1Ymxpc2giXSwiYXBwcyI6WyJjbXMiXSwiYXBwc192ZXJzaW9uIjoxfQ.r3G5AVkJuvR9vqZhv2Gnj-kZT04Vp_FWNLaZxU7mCvq6uo0unv0d4n3kxDnVYnTYi8cAMtYd4OXvAcpyJ6cN1c9UvyjHEZbhYscZXT6bsc794Jv1kC2rj0upra3zBOUPz-rSeVaQiOS4vA7th2GlvOCQhpkVqHyvZXuoG8AhS17ZyMWl9ZPtzhrE5Ql14w0Au3pmnj6p8zhZslDHXbLeejV0PC314yexqdpbXS6lB72ovzNGUgu30t3Va5sQFbb18PyRzEtI2JOzvGC0TlN6w4n8o3aXQ7rwurkqx00fuzf5nQpOssfO3EFtxIIOT-ndzsM0pDZgh4l6QBXqz4O20Q",
"refresh_token": "f876c11eb2cd221151096406ccd8878879b5bc3ba32822845674e117964c54b4",
"token_type": "Bearer",
"expires_in": 900
}
}
```
```text
permission项你是推荐数据库迁移的方式新增吗permission项的管理的最佳方式是什么
```