refactor(auth): 重构权限系统,简化API并添加缓存支持

主要变更:
- 将权限域从 appKey 改为 scope,权限ID格式为 scope:resource:action
- 新增 AddRole(roleCode, roleName, policies...) 方法,支持动态添加角色
- 简化 Factory.New() 只保留 scope 参数,移除 models.AppConfig 依赖
- PermAny/PermAll 改为变长参数 ...string 形式,使用更简洁
- AuthMiddleware 添加 Redis 缓存组织成员身份和角色信息(5分钟过期)
- 移除 models.AppConfig 和 models.RoleDefinition 结构体
- 更新测试和文档

BREAKING CHANGE: Factory.New() 签名变更,需要使用新的 AddRole API
v3
veypi 1 week ago
parent 37acea3420
commit 33eabfa013

@ -32,10 +32,15 @@ type Auth interface {
PermOnResource(permissionID, resourceKey string) func(*vigo.X) error PermOnResource(permissionID, resourceKey string) func(*vigo.X) error
// 满足任一权限 // 满足任一权限
PermAny(permissionIDs []string) func(*vigo.X) error PermAny(permissionIDs ...string) func(*vigo.X) error
// 满足所有权限 // 满足所有权限
PermAll(permissionIDs []string) func(*vigo.X) error PermAll(permissionIDs ...string) func(*vigo.X) error
// ========== 角色管理 ==========
// 添加角色定义
// policies 格式: "resource:action",例如 "user:read", "*:*"
AddRole(roleCode, roleName string, policies ...string) error
// ========== 权限管理 ========== // ========== 权限管理 ==========
// 授予角色 // 授予角色
@ -71,43 +76,46 @@ var Factory = &authFactory{
// VBaseAuth vbase 自身的权限管理实例 // VBaseAuth vbase 自身的权限管理实例
// 由 vbase 包在初始化时注入 // 由 vbase 包在初始化时注入
var ( var VBaseAuth = Factory.New("vb")
VBaseAuth = Factory.New("vb", models.AppConfig{
Name: "VBase", func init() {
Description: "VBase 基础设施", // 为 VBaseAuth 添加默认角色
DefaultRoles: []models.RoleDefinition{ VBaseAuth.AddRole("admin", "管理员", "*:*")
{Code: "admin", Name: "管理员", Policies: []string{"*:*"}}, VBaseAuth.AddRole("user", "普通用户",
{Code: "user", Name: "普通用户", Policies: []string{ "user:read",
"user:read", "user:update", "user:update",
"org:read", "org:create", "org:read",
"oauth-client:read", "oauth-client:create", "oauth-client:update", "oauth-client:delete", "org:create",
}}, "oauth-client:read",
}, "oauth-client:create",
}) "oauth-client:update",
) "oauth-client:delete",
)
}
type authFactory struct { type authFactory struct {
apps map[string]*appAuth // appKey -> auth实例 apps map[string]*appAuth // appKey -> auth实例
} }
// New 创建权限管理实例(注册应用) // New 创建权限管理实例(注册权限域)
func (f *authFactory) New(appKey string, config models.AppConfig) Auth { func (f *authFactory) New(scope string) Auth {
if _, exists := f.apps[appKey]; exists { if _, exists := f.apps[scope]; exists {
return f.apps[appKey] return f.apps[scope]
}
// 验证默认角色中的权限格式
for _, role := range config.DefaultRoles {
for _, policy := range role.Policies {
validatePermissionID(policy)
}
} }
auth := &appAuth{ auth := &appAuth{
appKey: appKey, scope: scope,
config: config, roleDefs: make(map[string]roleDefinition),
} policies: make(map[string][][2]string),
f.apps[appKey] = auth roleInitDone: make(map[string]bool),
}
// 设置权限域信息
auth.roleDefs["_scope_info"] = roleDefinition{
code: "_scope_info",
name: scope,
description: scope + " scope",
}
f.apps[scope] = auth
return auth return auth
} }
@ -145,10 +153,77 @@ func (f *authFactory) Init() error {
return nil return nil
} }
// appAuth 单个应用的权限管理 // roleDefinition 角色定义(内部使用)
type roleDefinition struct {
code string
name string
description string
policies []string // 权限列表: ["resource:action", "*:*"]
}
// appAuth 单个权限域的权限管理
type appAuth struct { type appAuth struct {
appKey string scope string // 权限域标识
config models.AppConfig roleDefs map[string]roleDefinition // roleCode -> role definition
policies map[string][][2]string // roleCode -> list of [resource, action] pairs
roleInitDone map[string]bool // roleCode -> whether role is initialized in DB
}
// AddRole 添加角色定义
// policies 格式: "resource:action",例如 "user:read", "*:*"
func (a *appAuth) AddRole(roleCode, roleName string, policies ...string) error {
if roleCode == "" || roleName == "" {
return fmt.Errorf("role code and name cannot be empty")
}
if roleCode == "_scope_info" {
return fmt.Errorf("reserved role code: _scope_info")
}
// 解析并验证权限格式
parsedPolicies := make([][2]string, 0, len(policies))
for _, policy := range policies {
// 严格检查格式: resource:action
parts := strings.Split(policy, ":")
if len(parts) != 2 {
return fmt.Errorf("invalid policy format: %s, expected 'resource:action'", policy)
}
resource, action := parts[0], parts[1]
// 验证 resource 和 action 不为空
if resource == "" || action == "" {
return fmt.Errorf("resource and action cannot be empty in policy: %s", policy)
}
// 验证 resource 格式(如果不是通配符)
if resource != "*" {
if !validResourceRegex.MatchString(resource) {
return fmt.Errorf("invalid resource identifier: %s in policy: %s, must start with letter and contain only letters, numbers, '-' or '_'", resource, policy)
}
}
// 验证 action 格式(如果不是通配符)
if action != "*" {
if !validResourceRegex.MatchString(action) {
return fmt.Errorf("invalid action identifier: %s in policy: %s, must start with letter and contain only letters, numbers, '-' or '_'", action, policy)
}
}
parsedPolicies = append(parsedPolicies, [2]string{resource, action})
}
// 存储角色定义
a.roleDefs[roleCode] = roleDefinition{
code: roleCode,
name: roleName,
}
a.policies[roleCode] = parsedPolicies
// 如果已经初始化过,立即同步到数据库
if len(a.roleInitDone) > 0 {
return a.initRole(roleCode)
}
return nil
} }
// init 初始化应用的权限配置 // init 初始化应用的权限配置
@ -166,9 +241,12 @@ func (a *appAuth) init() error {
} }
} }
// 2. 创建系统预设角色 // 2. 创建系统预设角色(跳过 _app_info
for _, roleDef := range a.config.DefaultRoles { for roleCode := range a.roleDefs {
if err := a.initRole(roleDef); err != nil { if roleCode == "_app_info" {
continue
}
if err := a.initRole(roleCode); err != nil {
return err return err
} }
} }
@ -180,24 +258,25 @@ func (a *appAuth) init() error {
func (a *appAuth) extractPermissions() []models.Permission { func (a *appAuth) extractPermissions() []models.Permission {
permMap := make(map[string]models.Permission) permMap := make(map[string]models.Permission)
for _, roleDef := range a.config.DefaultRoles { for roleCode, policies := range a.policies {
for _, policy := range roleDef.Policies { if roleCode == "_app_info" {
// policy 格式: "resource:action" 或 "*:*" continue
parts := strings.Split(policy, ":") }
if len(parts) != 2 { for _, policy := range policies {
resource, action := policy[0], policy[1]
// 跳过通配符权限的特殊处理
if resource == "*" && action == "*" {
continue continue
} }
permID := fmt.Sprintf("%s:%s:%s", a.scope, resource, action)
resource, action := parts[0], parts[1]
permID := fmt.Sprintf("%s:%s:%s", a.appKey, resource, action)
if _, exists := permMap[permID]; !exists { if _, exists := permMap[permID]; !exists {
permMap[permID] = models.Permission{ permMap[permID] = models.Permission{
ID: permID, ID: permID,
AppKey: a.appKey, Scope: a.scope,
Resource: resource, Resource: resource,
Action: action, Action: action,
Description: fmt.Sprintf("%s %s on %s", a.config.Name, action, resource), Description: fmt.Sprintf("%s %s on %s", a.scope, action, resource),
} }
} }
} }
@ -211,34 +290,44 @@ func (a *appAuth) extractPermissions() []models.Permission {
} }
// initRole 初始化系统预设角色 // initRole 初始化系统预设角色
func (a *appAuth) initRole(roleDef models.RoleDefinition) error { func (a *appAuth) initRole(roleCode string) error {
roleDef, exists := a.roleDefs[roleCode]
if !exists {
return fmt.Errorf("role not found: %s", roleCode)
}
policies, hasPolicies := a.policies[roleCode]
if !hasPolicies {
policies = [][2]string{}
}
// 查找或创建系统角色 // 查找或创建系统角色
var role models.Role var role models.Role
err := cfg.DB().Where("code = ? AND org_id IS NULL", roleDef.Code).First(&role).Error err := cfg.DB().Where("code = ? AND org_id IS NULL", roleDef.code).First(&role).Error
if err != nil { if err != nil {
// 创建新角色 // 创建新角色
role = models.Role{ role = models.Role{
OrgID: nil, OrgID: nil,
Code: roleDef.Code, Code: roleDef.code,
Name: roleDef.Name, Name: roleDef.name,
Description: roleDef.Description, Description: roleDef.description,
IsSystem: true, IsSystem: true,
Status: 1, Status: 1,
} }
if err := cfg.DB().Create(&role).Error; err != nil { if err := cfg.DB().Create(&role).Error; err != nil {
return fmt.Errorf("failed to create role %s: %w", roleDef.Code, err) return fmt.Errorf("failed to create role %s: %w", roleDef.code, err)
} }
} }
// 同步角色权限 // 同步角色权限
for _, policy := range roleDef.Policies { hasWildcard := false
parts := strings.Split(policy, ":") for _, policy := range policies {
if len(parts) != 2 { resource, action := policy[0], policy[1]
// 处理通配符权限
if resource == "*" && action == "*" {
hasWildcard = true
continue continue
} }
permID := fmt.Sprintf("%s:%s:%s", a.scope, resource, action)
resource, action := parts[0], parts[1]
permID := fmt.Sprintf("%s:%s:%s", a.appKey, resource, action)
// 检查关联是否存在 // 检查关联是否存在
var count int64 var count int64
@ -258,6 +347,26 @@ func (a *appAuth) initRole(roleDef models.RoleDefinition) error {
} }
} }
// 为通配符权限创建特殊记录
if hasWildcard {
wildcardPermID := fmt.Sprintf("%s:*:*", a.scope)
var count int64
cfg.DB().Model(&models.RolePermission{}).
Where("role_id = ? AND permission_id = ?", role.ID, wildcardPermID).
Count(&count)
if count == 0 {
rp := models.RolePermission{
RoleID: role.ID,
PermissionID: wildcardPermID,
Condition: "none",
}
if err := cfg.DB().Create(&rp).Error; err != nil {
return fmt.Errorf("failed to create wildcard role permission: %w", err)
}
}
}
a.roleInitDone[roleCode] = true
return nil return nil
} }
@ -366,7 +475,7 @@ func (a *appAuth) checkPermission(ctx context.Context, userID, orgID, permission
return nil return nil
} }
func (a *appAuth) PermAny(permissionIDs []string) func(*vigo.X) error { func (a *appAuth) PermAny(permissionIDs ...string) func(*vigo.X) error {
for _, pid := range permissionIDs { for _, pid := range permissionIDs {
validatePermissionID(pid) validatePermissionID(pid)
} }
@ -396,7 +505,7 @@ func (a *appAuth) PermAny(permissionIDs []string) func(*vigo.X) error {
} }
} }
func (a *appAuth) PermAll(permissionIDs []string) func(*vigo.X) error { func (a *appAuth) PermAll(permissionIDs ...string) func(*vigo.X) error {
for _, pid := range permissionIDs { for _, pid := range permissionIDs {
validatePermissionID(pid) validatePermissionID(pid)
} }
@ -511,7 +620,7 @@ func (a *appAuth) RevokeRole(ctx context.Context, userID, orgID, roleCode string
func (a *appAuth) GrantResourcePerm(ctx context.Context, userID, orgID, permissionID, resourceID string) error { func (a *appAuth) GrantResourcePerm(ctx context.Context, userID, orgID, permissionID, resourceID string) error {
if strings.Count(permissionID, ":") == 1 { if strings.Count(permissionID, ":") == 1 {
permissionID = fmt.Sprintf("%s:%s", a.appKey, permissionID) permissionID = fmt.Sprintf("%s:%s", a.scope, permissionID)
} }
// 检查权限是否存在 // 检查权限是否存在
var perm models.Permission var perm models.Permission
@ -557,7 +666,7 @@ func (a *appAuth) GrantResourcePerm(ctx context.Context, userID, orgID, permissi
func (a *appAuth) RevokeResourcePerm(ctx context.Context, userID, orgID, permissionID, resourceID string) error { func (a *appAuth) RevokeResourcePerm(ctx context.Context, userID, orgID, permissionID, resourceID string) error {
if strings.Count(permissionID, ":") == 1 { if strings.Count(permissionID, ":") == 1 {
permissionID = fmt.Sprintf("%s:%s", a.appKey, permissionID) permissionID = fmt.Sprintf("%s:%s", a.scope, permissionID)
} }
query := cfg.DB().Where("user_id = ? AND permission_id = ? AND resource_id = ?", query := cfg.DB().Where("user_id = ? AND permission_id = ? AND resource_id = ?",
userID, permissionID, resourceID) userID, permissionID, resourceID)
@ -604,7 +713,7 @@ func (a *appAuth) RevokeAll(ctx context.Context, userID, orgID string) error {
func (a *appAuth) CheckPermission(ctx context.Context, userID, orgID, permissionID, resourceID string) (bool, error) { func (a *appAuth) CheckPermission(ctx context.Context, userID, orgID, permissionID, resourceID string) (bool, error) {
if strings.Count(permissionID, ":") == 1 { if strings.Count(permissionID, ":") == 1 {
permissionID = fmt.Sprintf("%s:%s", a.appKey, permissionID) permissionID = fmt.Sprintf("%s:%s", a.scope, permissionID)
} }
// Check cache // Check cache
@ -659,6 +768,12 @@ func (a *appAuth) checkPermissionDB(ctx context.Context, userID, orgID, permissi
permsToCheck = append(permsToCheck, fmt.Sprintf("%s:%s:*", parts[0], parts[1])) permsToCheck = append(permsToCheck, fmt.Sprintf("%s:%s:*", parts[0], parts[1]))
// app:*:* // app:*:*
permsToCheck = append(permsToCheck, fmt.Sprintf("%s:*:*", parts[0])) permsToCheck = append(permsToCheck, fmt.Sprintf("%s:*:*", parts[0]))
} else if len(parts) == 2 {
// resource:action -> appKey:resource:action
fullPermID := fmt.Sprintf("%s:%s", a.scope, permissionID)
permsToCheck = append(permsToCheck, fullPermID)
permsToCheck = append(permsToCheck, fmt.Sprintf("%s:%s:*", a.scope, parts[0]))
permsToCheck = append(permsToCheck, fmt.Sprintf("%s:*:*", a.scope))
} }
// 检查这些角色是否有所需权限 // 检查这些角色是否有所需权限
@ -680,12 +795,24 @@ func (a *appAuth) checkPermissionDB(ctx context.Context, userID, orgID, permissi
if len(parts) == 3 { if len(parts) == 3 {
permsToCheck = append(permsToCheck, fmt.Sprintf("%s:%s:*", parts[0], parts[1])) permsToCheck = append(permsToCheck, fmt.Sprintf("%s:%s:*", parts[0], parts[1]))
permsToCheck = append(permsToCheck, fmt.Sprintf("%s:*:*", parts[0])) permsToCheck = append(permsToCheck, fmt.Sprintf("%s:*:*", parts[0]))
} else if len(parts) == 2 {
// resource:action -> appKey:resource:action
fullPermID := fmt.Sprintf("%s:%s", a.scope, permissionID)
permsToCheck = append(permsToCheck, fullPermID)
permsToCheck = append(permsToCheck, fmt.Sprintf("%s:%s:*", a.scope, parts[0]))
permsToCheck = append(permsToCheck, fmt.Sprintf("%s:*:*", a.scope))
} }
var userPermCount int64 var userPermCount int64
query := cfg.DB().Model(&models.UserPermission{}). query := cfg.DB().Model(&models.UserPermission{}).
Where("user_id = ? AND org_id = ? AND permission_id IN ? AND (expire_at IS NULL OR expire_at > ?)", Where("user_id = ? AND permission_id IN ? AND (expire_at IS NULL OR expire_at > ?)",
userID, orgID, permsToCheck, time.Now()) userID, permsToCheck, time.Now())
if orgID != "" {
query = query.Where("org_id = ?", orgID)
} else {
query = query.Where("org_id IS NULL")
}
if resourceID != "" { if resourceID != "" {
query = query.Where("resource_id = ? OR resource_id = '*'", resourceID) query = query.Where("resource_id = ? OR resource_id = '*'", resourceID)

@ -31,43 +31,21 @@ func TestMain(m *testing.M) {
} }
func getTestAuth() Auth { func getTestAuth() Auth {
// 每次获取一个新的实例名,避免测试间冲突(虽然内存库是共享的,但数据清理可能不完全) // 每次获取一个新的 scope避免测试间冲突虽然内存库是共享的但数据清理可能不完全
// 为了简单起见,我们在 TestMain 中只初始化一次 DB但可以通过应用名区分 // 为了简单起见,我们在 TestMain 中只初始化一次 DB但可以通过 scope 区分
appKey := fmt.Sprintf("test_app_%d", time.Now().UnixNano()) scope := fmt.Sprintf("test_scope_%d", time.Now().UnixNano())
a := Factory.New(appKey, models.AppConfig{ a := Factory.New(scope)
Name: "Test App",
DefaultRoles: []models.RoleDefinition{ // 添加角色定义
{ a.AddRole("admin", "Administrator", "*:*")
Code: "admin", a.AddRole("editor", "Editor",
Name: "Administrator", "article:create",
Policies: []string{"*:*"}, "article:read",
}, "article:update",
{ )
Code: "editor", a.AddRole("viewer", "Viewer", "article:read")
Name: "Editor", a.AddRole("deleter", "Deleter", "article:delete")
Policies: []string{
"article:create",
"article:read",
"article:update",
},
},
{
Code: "viewer",
Name: "Viewer",
Policies: []string{
"article:read",
},
},
{
Code: "deleter",
Name: "Deleter",
Policies: []string{
"article:delete",
},
},
},
})
// 初始化 // 初始化
if err := a.(*appAuth).init(); err != nil { if err := a.(*appAuth).init(); err != nil {
@ -228,10 +206,10 @@ func TestResourcePermission(t *testing.T) {
resID := "doc_123" resID := "doc_123"
// 需要先创建权限定义,因为 GrantResourcePerm 会检查权限是否存在 // 需要先创建权限定义,因为 GrantResourcePerm 会检查权限是否存在
permID := fmt.Sprintf("%s:doc:read", a.(*appAuth).appKey) permID := fmt.Sprintf("%s:doc:read", a.(*appAuth).scope)
perm := models.Permission{ perm := models.Permission{
ID: permID, ID: permID,
AppKey: a.(*appAuth).appKey, Scope: a.(*appAuth).scope,
Resource: "doc", Resource: "doc",
Action: "read", Action: "read",
Description: "Read Doc", Description: "Read Doc",

@ -5,7 +5,10 @@
package auth package auth
import ( import (
"encoding/json"
"fmt"
"strings" "strings"
"time"
"github.com/veypi/vbase/cfg" "github.com/veypi/vbase/cfg"
"github.com/veypi/vbase/libs/cache" "github.com/veypi/vbase/libs/cache"
@ -14,9 +17,16 @@ import (
"github.com/veypi/vigo" "github.com/veypi/vigo"
) )
// orgMemberCache 组织成员身份缓存结构
type orgMemberCache struct {
IsMember bool `json:"is_member"`
RoleCodes []string `json:"role_codes"`
}
// AuthMiddleware 统一认证中间件 // AuthMiddleware 统一认证中间件
// 1. JWT认证: 解析token验证有效性设置用户信息 // 1. JWT认证: 解析token验证有效性设置用户信息
// 2. 组织上下文: 如果请求包含org_id验证用户成员身份设置组织信息 // 2. 组织上下文: 如果请求包含org_id验证用户成员身份设置组织信息
// 使用Redis缓存组织成员身份和角色信息减少数据库查询
func AuthMiddleware() func(*vigo.X) error { func AuthMiddleware() func(*vigo.X) error {
return func(x *vigo.X) error { return func(x *vigo.X) error {
// === 1. JWT 认证部分 === // === 1. JWT 认证部分 ===
@ -59,21 +69,57 @@ func AuthMiddleware() func(*vigo.X) error {
return nil return nil
} }
// 验证用户是否为组织成员 // 尝试从缓存获取组织成员信息
var member models.OrgMember var roleCodes []string
if err := cfg.DB().Where("org_id = ? AND user_id = ? AND status = ?", var isMember bool
orgID, claims.UserID, models.MemberStatusActive).First(&member).Error; err != nil {
if cache.IsEnabled() {
cacheKey := fmt.Sprintf("auth:org_member:%s:%s", claims.UserID, orgID)
cachedData, err := cache.Get(cacheKey)
if err == nil && cachedData != "" {
var cached orgMemberCache
if err := json.Unmarshal([]byte(cachedData), &cached); err == nil {
isMember = cached.IsMember
roleCodes = cached.RoleCodes
}
}
}
// 缓存未命中,查询数据库
if roleCodes == nil {
// 验证用户是否为组织成员
var member models.OrgMember
err := cfg.DB().Where("org_id = ? AND user_id = ? AND status = ?",
orgID, claims.UserID, models.MemberStatusActive).First(&member).Error
isMember = err == nil
if isMember {
// 查询用户的角色
cfg.DB().Model(&models.UserRole{}).
Joins("JOIN roles ON user_roles.role_id = roles.id").
Where("user_roles.user_id = ? AND user_roles.org_id = ?", claims.UserID, orgID).
Pluck("roles.code", &roleCodes)
}
// 写入缓存
if cache.IsEnabled() {
cacheData := orgMemberCache{
IsMember: isMember,
RoleCodes: roleCodes,
}
if data, err := json.Marshal(cacheData); err == nil {
cacheKey := fmt.Sprintf("auth:org_member:%s:%s", claims.UserID, orgID)
// 缓存5分钟
cache.Set(cacheKey, string(data), 5*time.Minute)
}
}
}
if !isMember {
return vigo.ErrForbidden.WithString("you are not a member of this organization") return vigo.ErrForbidden.WithString("you are not a member of this organization")
} }
x.Set("org_id", orgID) x.Set("org_id", orgID)
// 从 UserRole 表查询用户的角色
var roleCodes []string
cfg.DB().Model(&models.UserRole{}).
Joins("JOIN roles ON user_roles.role_id = roles.id").
Where("user_roles.user_id = ? AND user_roles.org_id = ?", claims.UserID, orgID).
Pluck("roles.code", &roleCodes)
x.Set("org_roles", roleCodes) x.Set("org_roles", roleCodes)
return nil return nil

@ -77,29 +77,19 @@ VBase 提供了强大的 RBAC (基于角色的访问控制) 权限系统。
在您的应用中,使用 `vbase.Auth.New` 创建应用专属的权限实例。 在您的应用中,使用 `vbase.Auth.New` 创建应用专属的权限实例。
```go ```go
import ( import "github.com/veypi/vbase"
"github.com/veypi/vbase"
"github.com/veypi/vbase/models"
)
// 定义您的应用权限实例 // 定义您的应用权限实例
var AppAuth = vbase.Auth.New("my_app", models.AppConfig{ var AppAuth = vbase.Auth.New("my_app")
Name: "My Application",
Description: "我的应用描述", func init() {
// 定义应用的默认角色 // 定义应用的默认角色
DefaultRoles: []models.RoleDefinition{ AppAuth.AddRole("admin", "管理员", "*:*") // 拥有所有权限
{ AppAuth.AddRole("editor", "编辑",
Code: "admin", "article:create",
Name: "管理员", "article:update",
Policies: []string{"*:*"}, // 拥有所有权限 )
}, }
{
Code: "editor",
Name: "编辑",
Policies: []string{"article:create", "article:update"},
},
},
})
``` ```
#### 2.2.2 使用权限中间件 #### 2.2.2 使用权限中间件

@ -20,12 +20,12 @@ const (
) )
// Permission 权限定义表(权限字典) // Permission 权限定义表(权限字典)
// ID 格式: app:resource:action (例如: crm:customer:read) // ID 格式: scope:resource:action (例如: vb:user:read)
type Permission struct { type Permission struct {
ID string `json:"id" gorm:"primaryKey;size:100" desc:"权限ID格式: app:resource:action"` ID string `json:"id" gorm:"primaryKey;size:100" desc:"权限ID格式: scope:resource:action"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
AppKey string `json:"app_key" gorm:"index;size:50" desc:"应用标识"` Scope string `json:"scope" gorm:"index;size:50" desc:"权限域标识"`
Resource string `json:"resource" gorm:"index;size:50" desc:"资源类型"` Resource string `json:"resource" gorm:"index;size:50" desc:"资源类型"`
Action string `json:"action" gorm:"index;size:50" desc:"操作类型"` Action string `json:"action" gorm:"index;size:50" desc:"操作类型"`
Description string `json:"description" desc:"权限描述"` Description string `json:"description" desc:"权限描述"`
@ -107,21 +107,6 @@ func (UserPermission) TableName() string {
return "user_permissions" return "user_permissions"
} }
// AppConfig 应用配置(用于权限初始化)
type AppConfig struct {
Name string `json:"name" desc:"应用名称"`
Description string `json:"description" desc:"应用描述"`
DefaultRoles []RoleDefinition `json:"default_roles" desc:"预设角色"`
}
// RoleDefinition 角色定义(配置用)
type RoleDefinition struct {
Code string `json:"code" desc:"角色代码"`
Name string `json:"name" desc:"角色名称"`
Description string `json:"description" desc:"角色描述"`
Policies []string `json:"policies" desc:"权限列表: ["customer:read", "*:*"]"`
}
// GrantRoleRequest 授予角色请求 // GrantRoleRequest 授予角色请求
type GrantRoleRequest struct { type GrantRoleRequest struct {
UserID string `json:"user_id" desc:"用户ID"` UserID string `json:"user_id" desc:"用户ID"`

Loading…
Cancel
Save