You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OneAuth/auth/design.md

361 lines
10 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Auth 权限系统设计
---
## 一、权限码层级
### 1.1 层级定义
```
层级从 1 开始计数:
- 奇数层第1、3、5层资源类型
- 偶数层第2、4、6层实例
```
### 1.2 示例
```
org → 第1层 (奇数) - 资源类型
org:orgA → 第2层 (偶数) - 实例
org:orgA:project → 第3层 (奇数) - 资源类型
org:orgA:project:projB → 第4层 (偶数) - 实例
```
---
## 二、权限等级
### 2.1 奇数层(资源类型)
| level | 二进制 | 含义 |
| ----- | ------ | -------------------- |
| 0 | 000 | 无权限 |
| 1 | 001 | 可创建该类型的子资源 |
### 2.2 偶数层(实例)
| level | 二进制 | 含义 |
| ----- | ------ | ---------------------- |
| 0 | 000 | 无权限 |
| 2 | 010 | 读取 |
| 4 | 100 | 写入(修改,不能删除) |
| 6 | 110 | 读写(读取+修改) |
| 7 | 111 | 管理员(完全控制:读写+删除+授权) |
---
## 三、检查规则
### 3.1 层级与权限对应
| 权限 | level | 检查层级 | 说明 |
|------|-------|----------|------|
| 创建 | 1 | 奇数层 | 检查资源类型层 |
| 读取 | 2 | 偶数层 | 检查实例层 |
| 写入 | 4 | 偶数层 | 检查实例层 |
| 读写 | 6 | 偶数层 | 检查实例层 |
| 管理 | 7 | 偶数层 | 检查实例层 |
### 3.2 具体规则
```
创建资源 (level 1)
→ 检查当前 permissionID 对应的奇数层
→ 例: "org:{orgID}:project" 检查 "org:{orgID}:project" 层
读取/更新/删除资源 (level 2,4,6,7)
→ 检查当前 permissionID 对应的偶数层
→ 如无权限,递归向上检查父实例层
→ 注意:只有 Level 7 (管理员) 权限才会向下继承Level 2,4,6 不会继承
→ 例: "org:{orgID}:project:{projectID}" 先检查实例层,再检查 "org:{orgID}"
```
---
## 四、权限流程示例
### 场景一:用户 A 创建组织
```
1. 用户A创建组织 "公司A"
2. 自动创建权限:
- PermissionID: "org:org_companyA"
- Level: 7 (创建者完全控制)
```
### 场景二:用户 A 邀请用户 B 加入组织
```
1. 用户A授予用户B: org:org_companyA level 2 (读)
2. 用户B权限表:
- org:org_companyA level 2
3. 用户B可执行:
- ✓ 读取 org_companyA
- ✗ 修改/删除
```
### 场景三:用户 B 创建项目
```
前置: 用户B有 org:org_companyA level 2 (读),需要额外授权
1. 用户A授予用户B: org:org_companyA:project level 1 (创建项目)
2. 用户B创建项目 "项目X"
3. 自动创建权限:
- PermissionID: "org:org_companyA:project:project_X"
- Level: 7
```
### 场景四:用户 C 加入项目并创建文档
```
1. 用户B授予用户C: org:org_companyA:project:project_X level 2 (读)
2. 用户C需要额外授权才能创建文档
3. 用户C创建文档 "文档Y"
4. 自动创建权限:
- PermissionID: "org:org_companyA:project:project_X:doc:doc_Y"
- Level: 7
```
---
## 五、Auth 接口设计
```go
package auth
import (
"context"
"github.com/veypi/vigo"
)
// ========== 权限等级 ==========
const (
LevelNone = 0
LevelCreate = 1 // 001 创建 (检查奇数层)
LevelRead = 2 // 010 读取 (检查偶数层)
LevelWrite = 4 // 100 写入 (检查偶数层)
LevelReadWrite = 6 // 110 读写 (检查偶数层)
LevelAdmin = 7 // 111 管理员 (完全控制)
)
// PermFunc 权限检查函数类型
type PermFunc func(x *vigo.X) error
// Auth 权限管理接口
type Auth interface {
// ========== 上下文 ==========
// UserID 获取当前用户ID
UserID(x *vigo.X) string
// ========== 登录检查 ==========
// Login 检查用户是否登录
Login() PermFunc
// ========== 权限检查 ==========
// Perm 检查权限
// code: 权限码,支持动态解析
// - 固定写法: "org:orgA"
// - 动态解析: "org:{orgID}" 从 path 获取
// "org:{orgID@query}" 从 query 获取
// "org:{orgID@header}" 从 header 获取
// "org:{orgID@ctx}" 从 ctx 获取
// level: 需要的权限等级
Perm(code string, level int) PermFunc
// ========== 快捷方法 ==========
// PermCreate 检查创建权限 (level 1检查奇数层)
PermCreate(code string) PermFunc
// PermRead 检查读取权限 (level 2检查偶数层)
PermRead(code string) PermFunc
// PermWrite 检查更新权限 (level 4检查偶数层)
PermWrite(code string) PermFunc
// PermAdmin 检查管理员权限 (level 7检查偶数层)
PermAdmin(code string) PermFunc
// ========== 权限授予(业务调用) ==========
// Grant 授予权限
// 在创建资源、被授权等业务逻辑中调用
// permissionID: 权限码,如 "org:orgA"
// level: 权限等级
Grant(ctx context.Context, userID, permissionID string, level int) error
// Revoke 撤销权限
Revoke(ctx context.Context, userID, permissionID string) error
// ========== 权限查询 ==========
// Check 检查权限 不支持动态解析
// permissionID: 完整的权限码,如 "org:orgA"
Check(ctx context.Context, userID, permissionID string, level int) bool
// ========== 资源列表查询 ==========
// ListResources 查询用户在特定资源类型下的详细权限信息
// 用于解决 "查询我有权限的 org 列表" 等场景
// userID: 用户ID
// resourceType: 资源类型 (奇数层),如 "org" 或 "org:{orgID}:project"
// 返回: map[实例ID]权限等级 (如 {"orgA": 2, "orgB": 7})
ListResources(ctx context.Context, userID, resourceType string) (map[string]int, error)
// ListUsers 查询特定资源的所有协作者及其权限
// 用于解决 "查看这个项目有哪些成员" 等场景
// permissionID: 资源实例权限码,如 "org:orgA"
// 返回: map[用户ID]权限等级 (如 {"user1": 2, "user2": 7})
ListUsers(ctx context.Context, permissionID string) (map[string]int, error)
}
// ========== 数据结构 ==========
// Permission 用户权限
type Permission struct {
ID string `json:"id"`
UserID string `json:"user_id"`
PermissionID string `json:"permission_id"`
Level int `json:"level"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
```
---
## 六、使用示例
### 6.1 固定写法
```go
var Router = vigo.NewRouter()
func init() {
// 创建组织 - 需要系统级 org 权限
Router.Post("/orgs", cfg.Auth.PermCreate("org"), CreateOrg)
// 超级管理员接口
Router.Get("/admin/users", cfg.Auth.PermAdmin("*"), AdminListUsers)
}
```
### 6.2 动态解析
```go
func init() {
// 从路径参数获取 orgID (默认)
// GET /orgs/{orgID}
Router.Get("/orgs/{orgID}", cfg.Auth.PermRead("org:{orgID}"), GetOrg)
// 从 query 参数获取
// GET /orgs?orgID=xxx
Router.Get("/orgs", cfg.Auth.PermRead("org:{orgID@query}"), GetOrg)
// 多层嵌套
// GET /orgs/{orgID}/projects/{projectID}
Router.Get("/orgs/{orgID}/projects/{projectID}",
cfg.Auth.PermRead("org:{orgID}:project:{projectID}"),
GetProject,
)
}
```
### 6.3 完整示例
```go
var Router = vigo.NewRouter().Use(cfg.Auth.Login())
func init() {
// 创建组织 - 系统级权限
Router.Post("/orgs", cfg.Auth.PermCreate("org"), CreateOrg)
// 列出我的组织 - 只需登录
Router.Get("/orgs", ListMyOrgs)
// 组织操作 - 从路径获取
Router.Get("/orgs/{orgID}", cfg.Auth.PermRead("org:{orgID}"), GetOrg)
Router.Put("/orgs/{orgID}", cfg.Auth.PermWrite("org:{orgID}"), UpdateOrg)
Router.Delete("/orgs/{orgID}", cfg.Auth.PermAdmin("org:{orgID}"), DeleteOrg)
// 项目操作 - 嵌套资源
Router.Post("/orgs/{orgID}/projects", cfg.Auth.PermCreate("org:{orgID}:project"), CreateProject)
Router.Get("/orgs/{orgID}/projects/{projectID}", cfg.Auth.PermRead("org:{orgID}:project:{projectID}"), GetProject)
Router.Put("/orgs/{orgID}/projects/{projectID}", cfg.Auth.PermWrite("org:{orgID}:project:{projectID}"), UpdateProject)
Router.Delete("/orgs/{orgID}/projects/{projectID}", cfg.Auth.PermAdmin("org:{orgID}:project:{projectID}"), DeleteProject)
// 文档操作
Router.Post("/orgs/{orgID}/projects/{projectID}/docs", cfg.Auth.PermCreate("org:{orgID}:project:{projectID}:doc"), CreateDoc)
Router.Get("/orgs/{orgID}/projects/{projectID}/docs/{docID}", cfg.Auth.PermRead("org:{orgID}:project:{projectID}:doc:{docID}"), GetDoc)
}
```
### 6.4 动态解析规则
| 语法 | 来源 | 示例 |
|------|------|------|
| `{key}` | path 参数 | `{orgID}` |
| `{key@query}` | query 参数 | `{orgID@query}` |
| `{key@header}` | header | `{orgID@header}` |
| `{key@ctx}` | context | `{orgID@ctx}` |
---
## 七、接口说明
### 7.1 业务调用 vs 管理端
此接口是**业务层**调用,用于:
- 创建资源时自动授予权限
- 业务逻辑中检查权限
**管理端**(如权限管理后台)可以通过直接操作数据库实现批量管理。
### 7.2 Grant 调用时机
```go
// 创建组织时
func CreateOrg(x *vigo.X, req *CreateOrgReq) (*OrgResp, error) {
org := models.Org{Name: req.Name}
db.Create(&org)
// 授予创建者完全控制权限
cfg.Auth.Grant(x.Context(), userID, "org:"+org.ID, auth.LevelAdmin)
return &OrgResp{Org: org}, nil
}
```
### 7.3 列表与搜索接口设计
对于资源列表List或搜索接口推荐以下设计模式
1. **全量管理接口**(如后台管理系统):
- 使用 `PermAdmin("*")``PermAdmin("org:*")`
- 这类接口返回所有数据,必须严格控制权限。
2. **用户侧列表/搜索**(如“我的项目”):
- **方式一(仅所有者)**
- 使用 `Login()` 确保登录。
- 业务层:`db.Where("owner_id = ?", userID).Find(&orgs)`
- **方式二(协作模式 - 使用 ListResources**
- 调用 Auth 接口获取有权限的 ID 列表。
- `perms, _ := auth.ListResources(ctx, userID, "org")`
- `ids := keys(perms)`
- 业务层:`db.Where("id IN ?", ids).Find(&orgs)`
- **方式三(混合模式)**
- 同时查询 owner_id 和 授权列表。
- `perms, _ := auth.ListResources(...)`
- `ids := keys(perms)`
- `db.Where("owner_id = ? OR id IN ?", userID, ids).Find(&orgs)`
3. **`PermXXX` 适用场景**
- 针对 **特定资源实例** 的操作URL 中包含 ID`/projects/{id}`)。
- 针对 **共享资源** 的访问控制。
- 针对 **管理功能** 的鉴权。