|
|
|
@ -110,16 +110,56 @@ app:vbase:role:admin → 第4层 (偶数) - 实例
|
|
|
|
|
|
|
|
|
|
|
|
## 五、Auth 接口设计
|
|
|
|
## 五、Auth 接口设计
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VBase 的权限系统基于 vigo 框架的 `auth` SPI 模式,分为两层:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **Provider 接口**:由 VBase 实现 `vbaseProvider`,负责实际的权限存储和校验逻辑。
|
|
|
|
|
|
|
|
- **Auth 结构体**:由 vigo 框架提供 (`auth.Auth`),包装 Provider,提供中间件和高层 API,用于业务模块直接调用。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 5.1 Provider 接口(VBase 实现)
|
|
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
```go
|
|
|
|
package auth
|
|
|
|
// 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
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
VBase 通过 `auth.Factory.New(scope)` 创建作用域隔离的 Provider 实例,实现多应用间权限隔离。
|
|
|
|
"context"
|
|
|
|
|
|
|
|
"github.com/veypi/vigo"
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ========== 权限等级 ==========
|
|
|
|
### 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 (
|
|
|
|
const (
|
|
|
|
LevelNone = 0
|
|
|
|
LevelNone = 0
|
|
|
|
LevelCreate = 1 // 001 创建 (检查奇数层)
|
|
|
|
LevelCreate = 1 // 001 创建 (检查奇数层)
|
|
|
|
@ -128,92 +168,19 @@ const (
|
|
|
|
LevelReadWrite = 6 // 110 读写 (检查偶数层)
|
|
|
|
LevelReadWrite = 6 // 110 读写 (检查偶数层)
|
|
|
|
LevelAdmin = 7 // 111 管理员 (完全控制)
|
|
|
|
LevelAdmin = 7 // 111 管理员 (完全控制)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
// PermFunc 权限检查函数类型
|
|
|
|
### 5.4 Permission 数据模型
|
|
|
|
type PermFunc func(x *vigo.X) error
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Auth 权限管理接口
|
|
|
|
|
|
|
|
type Auth interface {
|
|
|
|
|
|
|
|
// ========== 上下文 ==========
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// UserID 获取当前用户ID
|
|
|
|
|
|
|
|
UserID(x *vigo.X) string
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ========== 登录检查 ==========
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Login 检查用户是否登录
|
|
|
|
|
|
|
|
Login() PermFunc
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ========== 权限检查 ==========
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Perm 检查权限
|
|
|
|
|
|
|
|
// code: 权限码,支持动态解析
|
|
|
|
|
|
|
|
// - 固定写法: "app:vbase"
|
|
|
|
|
|
|
|
// - 动态解析: "app:{appID}" 从 path 获取
|
|
|
|
|
|
|
|
// "app:{appID@query}" 从 query 获取
|
|
|
|
|
|
|
|
// "app:{appID@header}" 从 header 获取
|
|
|
|
|
|
|
|
// "app:{appID@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: 权限码,如 "app:vbase"
|
|
|
|
|
|
|
|
// level: 权限等级
|
|
|
|
|
|
|
|
Grant(ctx context.Context, userID, permissionID string, level int) error
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Revoke 撤销权限
|
|
|
|
|
|
|
|
Revoke(ctx context.Context, userID, permissionID string) error
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ========== 权限查询 ==========
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check 检查权限 不支持动态解析
|
|
|
|
|
|
|
|
// permissionID: 完整的权限码,如 "app:vbase"
|
|
|
|
|
|
|
|
Check(ctx context.Context, userID, permissionID string, level int) bool
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ========== 资源列表查询 ==========
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ListResources 查询用户在特定资源类型下的详细权限信息
|
|
|
|
|
|
|
|
// 用于解决 "查询我有权限的 app 列表" 等场景
|
|
|
|
|
|
|
|
// userID: 用户ID
|
|
|
|
|
|
|
|
// resourceType: 资源类型 (奇数层),如 "app" 或 "app:{appID}:role"
|
|
|
|
|
|
|
|
// 返回: map[实例ID]权限等级 (如 {"vbase": 2, "other": 7})
|
|
|
|
|
|
|
|
ListResources(ctx context.Context, userID, resourceType string) (map[string]int, error)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ListUsers 查询特定资源的所有协作者及其权限
|
|
|
|
|
|
|
|
// 用于解决 "查看这个应用有哪些成员" 等场景
|
|
|
|
|
|
|
|
// permissionID: 资源实例权限码,如 "app:vbase"
|
|
|
|
|
|
|
|
// 返回: map[用户ID]权限等级 (如 {"user1": 2, "user2": 7})
|
|
|
|
|
|
|
|
ListUsers(ctx context.Context, permissionID string) (map[string]int, error)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ========== 数据结构 ==========
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Permission 用户权限
|
|
|
|
```go
|
|
|
|
type Permission struct {
|
|
|
|
type Permission struct {
|
|
|
|
ID string `json:"id"`
|
|
|
|
ID string `json:"id"`
|
|
|
|
UserID string `json:"user_id"`
|
|
|
|
Scope string `json:"scope"` // 作用域
|
|
|
|
RoleID string `json:"role_id"`
|
|
|
|
UserID *string `json:"user_id"` // 用户ID(直接授权)
|
|
|
|
PermissionID string `json:"permission_id"`
|
|
|
|
RoleID *string `json:"role_id"` // 角色ID(角色授权)
|
|
|
|
Level int `json:"level"`
|
|
|
|
PermissionID string `json:"permission_id"` // 权限ID,层级结构
|
|
|
|
CreatedAt int64 `json:"created_at"`
|
|
|
|
Level int `json:"level"` // 权限等级
|
|
|
|
UpdatedAt int64 `json:"updated_at"`
|
|
|
|
ExpireAt *time.Time `json:"expire_at"` // 过期时间(可选)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
@ -228,10 +195,10 @@ var Router = vigo.NewRouter()
|
|
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
func init() {
|
|
|
|
// 创建应用 - 需要系统级 app 权限
|
|
|
|
// 创建应用 - 需要系统级 app 权限
|
|
|
|
Router.Post("/apps", cfg.Auth.PermCreate("app"), CreateApp)
|
|
|
|
Router.Post("/apps", cfg.Auth.RequireCreate("app"), CreateApp)
|
|
|
|
|
|
|
|
|
|
|
|
// 超级管理员接口
|
|
|
|
// 超级管理员接口
|
|
|
|
Router.Get("/admin/users", cfg.Auth.PermAdmin("*"), AdminListUsers)
|
|
|
|
Router.Get("/admin/users", cfg.Auth.RequireAdmin("*"), AdminListUsers)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
@ -241,16 +208,16 @@ func init() {
|
|
|
|
func init() {
|
|
|
|
func init() {
|
|
|
|
// 从路径参数获取 appID (默认)
|
|
|
|
// 从路径参数获取 appID (默认)
|
|
|
|
// GET /apps/{appID}
|
|
|
|
// GET /apps/{appID}
|
|
|
|
Router.Get("/apps/{appID}", cfg.Auth.PermRead("app:{appID}"), GetApp)
|
|
|
|
Router.Get("/apps/{appID}", cfg.Auth.RequireRead("app:{appID}"), GetApp)
|
|
|
|
|
|
|
|
|
|
|
|
// 从 query 参数获取
|
|
|
|
// 从 query 参数获取
|
|
|
|
// GET /apps?appID=xxx
|
|
|
|
// GET /apps?appID=xxx
|
|
|
|
Router.Get("/apps", cfg.Auth.PermRead("app:{appID@query}"), GetApp)
|
|
|
|
Router.Get("/apps", cfg.Auth.RequireRead("app:{appID@query}"), GetApp)
|
|
|
|
|
|
|
|
|
|
|
|
// 多层嵌套
|
|
|
|
// 多层嵌套
|
|
|
|
// GET /apps/{appID}/roles/{roleID}
|
|
|
|
// GET /apps/{appID}/roles/{roleID}
|
|
|
|
Router.Get("/apps/{appID}/roles/{roleID}",
|
|
|
|
Router.Get("/apps/{appID}/roles/{roleID}",
|
|
|
|
cfg.Auth.PermRead("app:{appID}:role:{roleID}"),
|
|
|
|
cfg.Auth.RequireRead("app:{appID}:role:{roleID}"),
|
|
|
|
GetRole,
|
|
|
|
GetRole,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -263,21 +230,21 @@ var Router = vigo.NewRouter().Use(cfg.Auth.Login())
|
|
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
func init() {
|
|
|
|
// 创建应用 - 系统级权限
|
|
|
|
// 创建应用 - 系统级权限
|
|
|
|
Router.Post("/apps", cfg.Auth.PermCreate("app"), CreateApp)
|
|
|
|
Router.Post("/apps", cfg.Auth.RequireCreate("app"), CreateApp)
|
|
|
|
|
|
|
|
|
|
|
|
// 列出我的应用 - 只需登录
|
|
|
|
// 列出我的应用 - 只需登录
|
|
|
|
Router.Get("/apps", ListMyApps)
|
|
|
|
Router.Get("/apps", ListMyApps)
|
|
|
|
|
|
|
|
|
|
|
|
// 应用操作 - 从路径获取
|
|
|
|
// 应用操作 - 从路径获取
|
|
|
|
Router.Get("/apps/{appID}", cfg.Auth.PermRead("app:{appID}"), GetApp)
|
|
|
|
Router.Get("/apps/{appID}", cfg.Auth.RequireRead("app:{appID}"), GetApp)
|
|
|
|
Router.Put("/apps/{appID}", cfg.Auth.PermWrite("app:{appID}"), UpdateApp)
|
|
|
|
Router.Put("/apps/{appID}", cfg.Auth.RequireWrite("app:{appID}"), UpdateApp)
|
|
|
|
Router.Delete("/apps/{appID}", cfg.Auth.PermAdmin("app:{appID}"), DeleteApp)
|
|
|
|
Router.Delete("/apps/{appID}", cfg.Auth.RequireAdmin("app:{appID}"), DeleteApp)
|
|
|
|
|
|
|
|
|
|
|
|
// 角色操作 - 嵌套资源
|
|
|
|
// 角色操作 - 嵌套资源
|
|
|
|
Router.Post("/apps/{appID}/roles", cfg.Auth.PermCreate("app:{appID}:role"), CreateRole)
|
|
|
|
Router.Post("/apps/{appID}/roles", cfg.Auth.RequireCreate("app:{appID}:role"), CreateRole)
|
|
|
|
Router.Get("/apps/{appID}/roles/{roleID}", cfg.Auth.PermRead("app:{appID}:role:{roleID}"), GetRole)
|
|
|
|
Router.Get("/apps/{appID}/roles/{roleID}", cfg.Auth.RequireRead("app:{appID}:role:{roleID}"), GetRole)
|
|
|
|
Router.Put("/apps/{appID}/roles/{roleID}", cfg.Auth.PermWrite("app:{appID}:role:{roleID}"), UpdateRole)
|
|
|
|
Router.Put("/apps/{appID}/roles/{roleID}", cfg.Auth.RequireWrite("app:{appID}:role:{roleID}"), UpdateRole)
|
|
|
|
Router.Delete("/apps/{appID}/roles/{roleID}", cfg.Auth.PermAdmin("app:{appID}:role:{roleID}"), DeleteRole)
|
|
|
|
Router.Delete("/apps/{appID}/roles/{roleID}", cfg.Auth.RequireAdmin("app:{appID}:role:{roleID}"), DeleteRole)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
@ -322,7 +289,7 @@ func CreateApp(x *vigo.X, req *CreateAppReq) (*AppResp, error) {
|
|
|
|
对于资源列表(List)或搜索接口,推荐以下设计模式:
|
|
|
|
对于资源列表(List)或搜索接口,推荐以下设计模式:
|
|
|
|
|
|
|
|
|
|
|
|
1. **全量管理接口**(如后台管理系统):
|
|
|
|
1. **全量管理接口**(如后台管理系统):
|
|
|
|
- 使用 `PermAdmin("*")` 或 `PermAdmin("app:*")`。
|
|
|
|
- 使用 `RequireAdmin("*")` 或 `RequireAdmin("app:*")`。
|
|
|
|
- 这类接口返回所有数据,必须严格控制权限。
|
|
|
|
- 这类接口返回所有数据,必须严格控制权限。
|
|
|
|
|
|
|
|
|
|
|
|
2. **用户侧列表/搜索**(如“我的应用”):
|
|
|
|
2. **用户侧列表/搜索**(如“我的应用”):
|
|
|
|
|