|
|
# 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 框架的 `auth` 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 是全局权限管理实例,由 vigo 框架提供
|
|
|
// 中间件方法:
|
|
|
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"` // 用户ID(直接授权)
|
|
|
RoleID *string `json:"role_id"` // 角色ID(角色授权)
|
|
|
PermissionID string `json:"permission_id"` // 权限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)`
|