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/docs/auth.md

311 lines
9.2 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 示例
```
app → 第1层 (奇数) - 资源类型
app:vbase → 第2层 (偶数) - 实例
app:vbase:role → 第3层 (奇数) - 资源类型
app:vbase:role:admin → 第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 对应的奇数层
→ 例: "app:{appID}:role" 检查 "app:{appID}:role" 层
读取/更新/删除资源 (level 2,4,6,7)
→ 检查当前 permissionID 对应的偶数层
→ 如无权限,递归向上检查父实例层
→ 注意:只有 Level 7 (管理员) 权限才会向下继承Level 2,4,6 不会继承
→ 例: "app:{appID}:role:{roleID}" 先检查实例层,再检查 "app:{appID}"
```
---
## 四、权限流程示例
### 场景一:用户 A 创建应用
```
1. 用户A创建应用 "VBase"
2. 自动创建权限:
- PermissionID: "app:vbase"
- Level: 7 (创建者完全控制)
```
### 场景二:用户 A 邀请用户 B 加入应用
```
1. 用户A授予用户B: app:vbase level 2 (读)
2. 用户B权限表:
- app:vbase level 2
3. 用户B可执行:
- ✓ 读取 vbase 信息
- ✗ 修改/删除
```
### 场景三:用户 B 创建角色
```
前置: 用户B有 app:vbase level 2 (读),需要额外授权
1. 用户A授予用户B: app:vbase:role level 1 (创建角色)
2. 用户B创建角色 "Editor"
3. 自动创建权限:
- PermissionID: "app:vbase:role:editor"
- Level: 7
```
---
## 五、Auth 接口设计
VBase 的权限系统基于 vigo 框架的 SPI 模式,分为两层:
- **Provider 接口**:由 VBase 实现 (`vbaseProvider`),负责实际的权限存储和校验逻辑。
- **Auth 结构体**:由 vigo 框架提供 (`auth.Auth`),包装 Provider提供中间件和高层 API 供业务模块直接使用。
### 5.1 Provider 接口VBase 实现)
```go
// Provider 是实现端需要实现的 SPI
type Provider interface {
UserID(x *vigo.X) string
Check(ctx context.Context, userID, permCode string, permLevel int) bool
Grant(ctx context.Context, userID, permCode string, permLevel int) error
Revoke(ctx context.Context, userID, permCode string) error
ListResources(ctx context.Context, userID, resourceType string) (map[string]int, error)
ListUsers(ctx context.Context, permCode string) (map[string]int, error)
GrantRole(ctx context.Context, userID, roleCode string) error
RevokeRole(ctx context.Context, userID, roleCode string) error
AddRole(roleCode, roleName string, permPolicies ...string) error
}
```
VBase 通过 `auth.Factory.New(scope)` 创建作用域隔离的 Provider 实例。
### 5.2 Auth 结构体(业务调用)
```go
// cfg.Auth 是全局权限管理实例
// 中间件方法:
cfg.Auth.Login()
cfg.Auth.Require(permExpr, permLevel) // 通用权限检查
cfg.Auth.RequireCreate(permExpr) // 创建 (level 1)
cfg.Auth.RequireRead(permExpr) // 读取 (level 2)
cfg.Auth.RequireWrite(permExpr) // 写入 (level 4)
cfg.Auth.RequireAdmin(permExpr) // 管理员 (level 7)
// 业务调用方法:
cfg.Auth.UserID(x) // 获取当前用户 ID
cfg.Auth.Check(ctx, userID, permCode, level) // 静态权限检查
cfg.Auth.Grant(ctx, userID, permCode, level) // 授予权限
cfg.Auth.Revoke(ctx, userID, permCode) // 撤销权限
cfg.Auth.GrantRole(ctx, userID, roleCode) // 授予角色
cfg.Auth.RevokeRole(ctx, userID, roleCode) // 撤销角色
cfg.Auth.ListResources(ctx, userID, resourceType) // 列出资源权限
cfg.Auth.ListUsers(ctx, permCode) // 列出资源协作者
```
### 5.3 权限等级常量
```go
const (
LevelNone = 0
LevelCreate = 1 // 001 创建 (检查奇数层)
LevelRead = 2 // 010 读取 (检查偶数层)
LevelWrite = 4 // 100 写入 (检查偶数层)
LevelReadWrite = 6 // 110 读写 (检查偶数层)
LevelAdmin = 7 // 111 管理员 (完全控制)
)
```
### 5.4 Permission 数据模型
```go
type Permission struct {
ID string `json:"id"`
Scope string `json:"scope"`
UserID *string `json:"user_id"`
RoleID *string `json:"role_id"`
PermissionID string `json:"permission_id"`
Level int `json:"level"`
ExpireAt *time.Time `json:"expire_at"`
}
```
---
## 六、使用示例
### 6.1 固定写法
```go
var Router = vigo.NewRouter()
func init() {
// 创建应用 - 需要系统级 app 权限
Router.Post("/apps", cfg.Auth.RequireCreate("app"), CreateApp)
// 超级管理员接口
Router.Get("/admin/users", cfg.Auth.RequireAdmin("*"), AdminListUsers)
}
```
### 6.2 动态解析
```go
func init() {
// 从路径参数获取 appID (默认)
// GET /apps/{appID}
Router.Get("/apps/{appID}", cfg.Auth.RequireRead("app:{appID}"), GetApp)
// 从 query 参数获取
// GET /apps?appID=xxx
Router.Get("/apps", cfg.Auth.RequireRead("app:{appID@query}"), GetApp)
// 多层嵌套
// GET /apps/{appID}/roles/{roleID}
Router.Get("/apps/{appID}/roles/{roleID}",
cfg.Auth.RequireRead("app:{appID}:role:{roleID}"),
GetRole,
)
}
```
### 6.3 完整示例
```go
var Router = vigo.NewRouter().Use(cfg.Auth.Login())
func init() {
// 创建应用 - 系统级权限
Router.Post("/apps", cfg.Auth.RequireCreate("app"), CreateApp)
// 列出我的应用 - 只需登录
Router.Get("/apps", ListMyApps)
// 应用操作 - 从路径获取
Router.Get("/apps/{appID}", cfg.Auth.RequireRead("app:{appID}"), GetApp)
Router.Put("/apps/{appID}", cfg.Auth.RequireWrite("app:{appID}"), UpdateApp)
Router.Delete("/apps/{appID}", cfg.Auth.RequireAdmin("app:{appID}"), DeleteApp)
// 角色操作 - 嵌套资源
Router.Post("/apps/{appID}/roles", cfg.Auth.RequireCreate("app:{appID}:role"), CreateRole)
Router.Get("/apps/{appID}/roles/{roleID}", cfg.Auth.RequireRead("app:{appID}:role:{roleID}"), GetRole)
Router.Put("/apps/{appID}/roles/{roleID}", cfg.Auth.RequireWrite("app:{appID}:role:{roleID}"), UpdateRole)
Router.Delete("/apps/{appID}/roles/{roleID}", cfg.Auth.RequireAdmin("app:{appID}:role:{roleID}"), DeleteRole)
}
```
### 6.4 动态解析规则
| 语法 | 来源 | 示例 |
| -------------- | ---------- | ---------------- |
| `{key}` | path 参数 | `{appID}` |
| `{key@query}` | query 参数 | `{appID@query}` |
| `{key@header}` | header | `{appID@header}` |
| `{key@ctx}` | context | `{appID@ctx}` |
---
## 七、接口说明
### 7.1 业务调用 vs 管理端
此接口是**业务层**调用,用于:
- 创建资源时自动授予权限
- 业务逻辑中检查权限
**管理端**(如权限管理后台)可以通过直接操作数据库实现批量管理。
### 7.2 Grant 调用时机
```go
// 创建应用时
func CreateApp(x *vigo.X, req *CreateAppReq) (*AppResp, error) {
app := models.App{Name: req.Name}
db.Create(&app)
// 授予创建者完全控制权限
cfg.Auth.Grant(x.Context(), userID, "app:"+app.ID, auth.LevelAdmin)
return &AppResp{App: app}, nil
}
```
### 7.3 列表与搜索接口设计
对于资源列表List或搜索接口推荐以下设计模式
1. **全量管理接口**(如后台管理系统):
- 使用 `RequireAdmin("*")``RequireAdmin("app:*")`
- 这类接口返回所有数据,必须严格控制权限。
2. **用户侧列表/搜索**(如“我的应用”):
- **方式一(仅所有者)**
- 使用 `Login()` 确保登录。
- 业务层:`db.Where("owner_id = ?", userID).Find(&apps)`
- **方式二(协作模式 - 使用 ListResources**
- 调用 Auth 接口获取有权限的 ID 列表。
- `perms, _ := auth.ListResources(ctx, userID, "app")`
- `ids := keys(perms)`
- 业务层:`db.Where("id IN ?", ids).Find(&apps)`
- **方式三(混合模式)**
- 同时查询 owner_id 和 授权列表。
- `perms, _ := auth.ListResources(...)`
- `ids := keys(perms)`
- `db.Where("owner_id = ? OR id IN ?", userID, ids).Find(&apps)`