From 26bd0bfd29371f740fa6931b0fcd065dbd44181b Mon Sep 17 00:00:00 2001 From: veypi Date: Sat, 14 Feb 2026 22:35:30 +0800 Subject: [PATCH] update --- api/init.go | 5 +- api/middleware/org.go | 48 - api/middleware/perm.go | 155 --- api/middleware/ratelimit.go | 45 - api/oauth/init.go | 15 +- api/org/create.go | 11 + api/org/init.go | 27 +- api/user/init.go | 8 +- auth/auth.go | 29 +- api/middleware/auth.go => auth/middleware.go | 32 +- doc/README.md | 128 --- doc/api.md | 741 -------------- doc/architecture.md | 906 ----------------- doc/developer_guide.md | 565 ----------- doc/integration.md | 157 --- doc/permission.md | 998 ------------------- doc/permission_design.md | 285 ------ doc/tsdb.md | 26 - docs/integration.md | 160 +++ init.go | 4 +- 20 files changed, 263 insertions(+), 4082 deletions(-) delete mode 100644 api/middleware/org.go delete mode 100644 api/middleware/perm.go delete mode 100644 api/middleware/ratelimit.go rename api/middleware/auth.go => auth/middleware.go (56%) delete mode 100644 doc/README.md delete mode 100644 doc/api.md delete mode 100644 doc/architecture.md delete mode 100644 doc/developer_guide.md delete mode 100644 doc/integration.md delete mode 100644 doc/permission.md delete mode 100644 doc/permission_design.md delete mode 100644 doc/tsdb.md create mode 100644 docs/integration.md diff --git a/api/init.go b/api/init.go index d56d14f..e0cd0ea 100644 --- a/api/init.go +++ b/api/init.go @@ -9,10 +9,10 @@ package api import ( apiAuth "github.com/veypi/vbase/api/auth" - "github.com/veypi/vbase/api/middleware" "github.com/veypi/vbase/api/oauth" "github.com/veypi/vbase/api/org" "github.com/veypi/vbase/api/user" + "github.com/veypi/vbase/auth" "github.com/veypi/vigo" "github.com/veypi/vigo/contrib/common" ) @@ -21,8 +21,7 @@ var Router = vigo.NewRouter() func init() { // 注册全局中间件 - Router.Use(middleware.AuthRequired()) - Router.Use(middleware.OrgContext()) + Router.Use(auth.AuthMiddleware()) Router.After(common.JsonResponse, common.JsonErrorResponse) // 子路由挂载 diff --git a/api/middleware/org.go b/api/middleware/org.go deleted file mode 100644 index f170c8e..0000000 --- a/api/middleware/org.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) 2024 veypi -// 2025-03-04 16:08:06 -// Distributed under terms of the MIT license. - -package middleware - -import ( - "github.com/veypi/vbase/cfg" - "github.com/veypi/vbase/models" - "github.com/veypi/vigo" -) - -// OrgContext 组织上下文中间件 -// 从header或query参数中获取org_id,并验证用户是否为该组织成员 -func OrgContext() func(*vigo.X) error { - return func(x *vigo.X) error { - orgID := x.Request.Header.Get("X-Org-ID") - if orgID == "" { - orgID = x.Request.URL.Query().Get("org_id") - } - - if orgID == "" { - // 没有指定组织,跳过 - return nil - } - - userID := "" - if uid, ok := x.Get("user_id").(string); ok { - userID = uid - } - - if userID == "" { - return vigo.ErrNotAuthorized - } - - // 验证用户是否为组织成员 - var member models.OrgMember - if err := cfg.DB().Where("org_id = ? AND user_id = ? AND status = ?", - orgID, userID, models.MemberStatusActive).First(&member).Error; err != nil { - return vigo.ErrForbidden.WithString("you are not a member of this organization") - } - - x.Set("org_id", orgID) - x.Set("org_roles", member.RoleIDs) - - return nil - } -} diff --git a/api/middleware/perm.go b/api/middleware/perm.go deleted file mode 100644 index 68b2899..0000000 --- a/api/middleware/perm.go +++ /dev/null @@ -1,155 +0,0 @@ -// -// Copyright (C) 2024 veypi -// 2025-03-04 16:08:06 -// Distributed under terms of the MIT license. -// - -package middleware - -import ( - "strings" - - "github.com/veypi/vbase/cfg" - "github.com/veypi/vbase/models" - "github.com/veypi/vigo" -) - -// Checker 权限检查器 -type Checker struct { - userID string - orgID string - roles []string - org *models.Org -} - -// NewChecker 创建权限检查器 -func NewChecker(x *vigo.X) *Checker { - c := &Checker{} - - if uid, ok := x.Get("user_id").(string); ok { - c.userID = uid - } - if oid, ok := x.Get("org_id").(string); ok { - c.orgID = oid - } - if roles, ok := x.Get("org_roles").(string); ok && roles != "" { - c.roles = strings.Split(roles, ",") - } - - return c -} - -// IsOrgOwner 检查用户是否是组织所有者 -func (c *Checker) IsOrgOwner() bool { - if c.orgID == "" || c.userID == "" { - return false - } - - if c.org != nil { - return c.org.OwnerID == c.userID - } - - var org models.Org - if err := cfg.DB().First(&org, "id = ?", c.orgID).Error; err != nil { - return false - } - c.org = &org - - return org.OwnerID == c.userID -} - -// IsOrgAdmin 检查用户是否是组织管理员 -func (c *Checker) IsOrgAdmin() bool { - if c.IsOrgOwner() { - return true - } - - for _, roleID := range c.roles { - var role models.Role - if err := cfg.DB().First(&role, "id = ?", roleID).Error; err != nil { - continue - } - if role.Code == models.RoleCodeAdmin { - return true - } - } - - return false -} - -// HasRole 检查用户是否有指定角色 -func (c *Checker) HasRole(roleCode string) bool { - for _, roleID := range c.roles { - var role models.Role - if err := cfg.DB().First(&role, "id = ?", roleID).Error; err != nil { - continue - } - if role.Code == roleCode { - return true - } - } - return false -} - -// RequireAdmin 要求管理员权限 -func (c *Checker) RequireAdmin() error { - if !c.IsOrgAdmin() { - return vigo.ErrForbidden.WithString("admin permission required") - } - return nil -} - -// RequireOwner 要求所有者权限 -func (c *Checker) RequireOwner() error { - if !c.IsOrgOwner() { - return vigo.ErrForbidden.WithString("owner permission required") - } - return nil -} - -// RequireOrg 要求必须在组织上下文中 -func (c *Checker) RequireOrg() error { - if c.orgID == "" { - return vigo.ErrArgInvalid.WithString("organization context required") - } - return nil -} - -// UserID 获取用户ID -func (c *Checker) UserID() string { - return c.userID -} - -// OrgID 获取组织ID -func (c *Checker) OrgID() string { - return c.orgID -} - -// GetUserRoles 获取用户角色列表 -func (c *Checker) GetUserRoles() []string { - return c.roles -} - -// RequireAdmin 中间件:要求管理员权限 -func RequireAdmin() func(*vigo.X) error { - return func(x *vigo.X) error { - checker := NewChecker(x) - return checker.RequireAdmin() - } -} - -// RequireOwner 中间件:要求组织所有者权限 -func RequireOwner() func(*vigo.X) error { - return func(x *vigo.X) error { - checker := NewChecker(x) - return checker.RequireOwner() - } -} - -// RequireOrgContext 中间件:要求必须在组织上下文中 -func RequireOrgContext() func(*vigo.X) error { - return func(x *vigo.X) error { - checker := NewChecker(x) - return checker.RequireOrg() - } -} diff --git a/api/middleware/ratelimit.go b/api/middleware/ratelimit.go deleted file mode 100644 index 2d3807d..0000000 --- a/api/middleware/ratelimit.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (C) 2024 veypi -// 2025-03-04 16:08:06 -// Distributed under terms of the MIT license. - -package middleware - -import ( - "fmt" - "time" - - "github.com/veypi/vbase/libs/cache" - "github.com/veypi/vigo" -) - -// RateLimiter 限流中间件 -func RateLimiter(requests int, window time.Duration) func(*vigo.X) error { - return func(x *vigo.X) error { - // 获取标识(优先用户ID,其次IP) - identifier := "" - if uid, ok := x.Get("user_id").(string); ok && uid != "" { - identifier = uid - } else { - identifier = x.GetRemoteIP() - } - - key := fmt.Sprintf("ratelimit:%s:%s", identifier, x.Request.URL.Path) - - count, err := cache.Incr(key) - if err != nil { - // Redis未启用,跳过限流 - return nil - } - - // 第一次请求设置过期时间 - if count == 1 { - cache.Expire(key, window) - } - - if count > int64(requests) { - return vigo.ErrForbidden.WithString("rate limit exceeded") - } - - return nil - } -} diff --git a/api/oauth/init.go b/api/oauth/init.go index 6a33dd7..508970f 100644 --- a/api/oauth/init.go +++ b/api/oauth/init.go @@ -4,7 +4,10 @@ package oauth -import "github.com/veypi/vigo" +import ( + "github.com/veypi/vbase/auth" + "github.com/veypi/vigo" +) var Router = vigo.NewRouter() @@ -18,9 +21,9 @@ func init() { // === OAuth 客户端管理(需要认证)=== clientRouter := Router.SubRouter("/clients") - clientRouter.Get("/", "OAuth客户端列表", listClients) - clientRouter.Post("/", "创建OAuth客户端", createClient) - clientRouter.Get("/{client_id}", "获取客户端详情", getClient) - clientRouter.Patch("/{client_id}", "更新OAuth客户端", updateClient) - clientRouter.Delete("/{client_id}", "删除OAuth客户端", deleteClient) + clientRouter.Get("/", "OAuth客户端列表", auth.VBaseAuth.Perm("oauth:client:read"), listClients) + clientRouter.Post("/", "创建OAuth客户端", auth.VBaseAuth.Perm("oauth:client:create"), createClient) + clientRouter.Get("/{client_id}", "获取客户端详情", auth.VBaseAuth.Perm("oauth:client:read"), getClient) + clientRouter.Patch("/{client_id}", "更新OAuth客户端", auth.VBaseAuth.Perm("oauth:client:update"), updateClient) + clientRouter.Delete("/{client_id}", "删除OAuth客户端", auth.VBaseAuth.Perm("oauth:client:delete"), deleteClient) } diff --git a/api/org/create.go b/api/org/create.go index 0bc5d38..7841ce0 100644 --- a/api/org/create.go +++ b/api/org/create.go @@ -5,6 +5,7 @@ package org import ( + "github.com/veypi/vbase/auth" "github.com/veypi/vbase/cfg" "github.com/veypi/vbase/models" "github.com/veypi/vigo" @@ -46,6 +47,16 @@ func create(x *vigo.X, req *CreateRequest) (*models.Org, error) { return nil, vigo.ErrInternalServer.WithError(err) } + // 授予创建者 admin 角色 + if err := auth.VBaseAuth.GrantRole(x.Context(), models.GrantRoleRequest{ + UserID: ownerID, + OrgID: org.ID, + RoleCode: "admin", + }); err != nil { + // 最好回滚,这里简化处理 + return nil, vigo.ErrInternalServer.WithError(err) + } + return org, nil } diff --git a/api/org/init.go b/api/org/init.go index d6e0bea..b62182d 100644 --- a/api/org/init.go +++ b/api/org/init.go @@ -4,16 +4,27 @@ package org -import "github.com/veypi/vigo" +import ( + "github.com/veypi/vbase/auth" + "github.com/veypi/vigo" +) var Router = vigo.NewRouter() func init() { - Router.Get("/", "组织列表", list) - Router.Post("/", "创建组织", create) - Router.Get("/{org_id}", "获取组织详情", get) - Router.Patch("/{org_id}", "更新组织", patch) - Router.Delete("/{org_id}", "删除组织", del) - Router.Get("/tree", "组织树", tree) - Router.Get("/{org_id}/members", "组织成员列表", listMembers) + Router.Get("/", "组织列表", auth.VBaseAuth.Perm("org:read"), list) + Router.Post("/", "创建组织", auth.VBaseAuth.Perm("org:create"), create) + Router.Get("/{org_id}", "获取组织详情", setOrgID, auth.VBaseAuth.Perm("org:read"), get) + Router.Patch("/{org_id}", "更新组织", setOrgID, auth.VBaseAuth.Perm("org:update"), patch) + Router.Delete("/{org_id}", "删除组织", setOrgID, auth.VBaseAuth.Perm("org:delete"), del) + Router.Get("/tree", "组织树", auth.VBaseAuth.Perm("org:read"), tree) + Router.Get("/{org_id}/members", "组织成员列表", setOrgID, auth.VBaseAuth.Perm("org:read"), listMembers) +} + +func setOrgID(x *vigo.X) error { + orgID := x.PathParams.Get("org_id") + if orgID != "" { + x.Set("org_id", orgID) + } + return nil } diff --git a/api/user/init.go b/api/user/init.go index 36329b9..b341a31 100644 --- a/api/user/init.go +++ b/api/user/init.go @@ -16,8 +16,8 @@ var Router = vigo.NewRouter() func init() { Router.Get("/", "用户列表", auth.VBaseAuth.Perm("user:read"), list) Router.Post("/", "创建用户", auth.VBaseAuth.Perm("user:admin"), create) - Router.Get("/{user_id}", "获取用户详情", get) - Router.Patch("/{user_id}", "更新用户", patch) - Router.Delete("/{user_id}", "删除用户", del) - Router.Patch("/{user_id}/status", "更新用户状态", updateStatus) + Router.Get("/{user_id}", "获取用户详情", auth.VBaseAuth.PermWithOwner("user:read", "user_id"), get) + Router.Patch("/{user_id}", "更新用户", auth.VBaseAuth.PermWithOwner("user:update", "user_id"), patch) + Router.Delete("/{user_id}", "删除用户", auth.VBaseAuth.Perm("user:admin"), del) + Router.Patch("/{user_id}/status", "更新用户状态", auth.VBaseAuth.Perm("user:admin"), updateStatus) } diff --git a/auth/auth.go b/auth/auth.go index 6af82a8..7d92d1a 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -72,7 +72,11 @@ var ( Description: "VBase 基础设施", DefaultRoles: []models.RoleDefinition{ {Code: "admin", Name: "管理员", Policies: []string{"*:*"}}, - {Code: "user", Name: "普通用户", Policies: []string{"user:read", "user:update"}}, + {Code: "user", Name: "普通用户", Policies: []string{ + "user:read", "user:update", + "org:read", "org:create", + "oauth:client:read", "oauth:client:create", "oauth:client:update", "oauth:client:delete", + }}, }, }) ) @@ -275,6 +279,9 @@ func (a *appAuth) PermWithOwner(permissionID, ownerKey string) func(*vigo.X) err // 检查是否是所有者或管理员 ownerID, _ := x.Get(ownerKey).(string) + if ownerID == "" { + ownerID = x.PathParams.Get(ownerKey) + } if ownerID == userID { return nil } @@ -356,7 +363,15 @@ func (a *appAuth) GrantRole(ctx context.Context, req models.GrantRoleRequest) er } if err := query.First(&role).Error; err != nil { - return fmt.Errorf("role not found: %s", req.RoleCode) + // 如果指定了 OrgID 但没找到,尝试查找全局角色 + if req.OrgID != "" { + query = cfg.DB().Where("code = ? AND org_id = ''", req.RoleCode) + if err := query.First(&role).Error; err != nil { + return fmt.Errorf("role not found: %s", req.RoleCode) + } + } else { + return fmt.Errorf("role not found: %s", req.RoleCode) + } } // 检查是否已存在 @@ -381,6 +396,7 @@ func (a *appAuth) GrantRole(ctx context.Context, req models.GrantRoleRequest) er func (a *appAuth) RevokeRole(ctx context.Context, userID, orgID, roleCode string) error { var role models.Role + // 优先查找组织特定角色 query := cfg.DB().Where("code = ?", roleCode) if orgID != "" { query = query.Where("org_id = ?", orgID) @@ -389,7 +405,14 @@ func (a *appAuth) RevokeRole(ctx context.Context, userID, orgID, roleCode string } if err := query.First(&role).Error; err != nil { - return nil // 角色不存在,无需撤销 + // 如果没找到,尝试查找全局角色 + if orgID != "" { + if err := cfg.DB().Where("code = ? AND org_id = ''", roleCode).First(&role).Error; err != nil { + return nil // 角色不存在,无需撤销 + } + } else { + return nil + } } return cfg.DB().Where("user_id = ? AND org_id = ? AND role_id = ?", userID, orgID, role.ID). diff --git a/api/middleware/auth.go b/auth/middleware.go similarity index 56% rename from api/middleware/auth.go rename to auth/middleware.go index 96122ec..623005e 100644 --- a/api/middleware/auth.go +++ b/auth/middleware.go @@ -2,19 +2,24 @@ // 2025-03-04 16:08:06 // Distributed under terms of the MIT license. -package middleware +package auth import ( "strings" + "github.com/veypi/vbase/cfg" "github.com/veypi/vbase/libs/cache" "github.com/veypi/vbase/libs/jwt" + "github.com/veypi/vbase/models" "github.com/veypi/vigo" ) -// AuthRequired JWT认证中间件 -func AuthRequired() func(*vigo.X) error { +// AuthMiddleware 统一认证中间件 +// 1. JWT认证: 解析token,验证有效性,设置用户信息 +// 2. 组织上下文: 如果请求包含org_id,验证用户成员身份,设置组织信息 +func AuthMiddleware() func(*vigo.X) error { return func(x *vigo.X) error { + // === 1. JWT 认证部分 === tokenString := extractToken(x) if tokenString == "" { return vigo.ErrNotAuthorized.WithString("missing token") @@ -43,6 +48,27 @@ func AuthRequired() func(*vigo.X) error { x.Set("user_orgs", claims.Orgs) x.Set("token_claims", claims) + // === 2. 组织上下文部分 === + orgID := x.Request.Header.Get("X-Org-ID") + if orgID == "" { + orgID = x.Request.URL.Query().Get("org_id") + } + + if orgID == "" { + // 没有指定组织,仅完成用户认证 + return nil + } + + // 验证用户是否为组织成员 + var member models.OrgMember + if err := cfg.DB().Where("org_id = ? AND user_id = ? AND status = ?", + orgID, claims.UserID, models.MemberStatusActive).First(&member).Error; err != nil { + return vigo.ErrForbidden.WithString("you are not a member of this organization") + } + + x.Set("org_id", orgID) + x.Set("org_roles", member.RoleIDs) + return nil } } diff --git a/doc/README.md b/doc/README.md deleted file mode 100644 index 9d2795e..0000000 --- a/doc/README.md +++ /dev/null @@ -1,128 +0,0 @@ -# VBase 文档 - -欢迎来到 VBase 文档中心。 - -## 快速导航 - -| 文档 | 说明 | -|------|------| -| [API 文档](./api.md) | 完整的 API 接口文档,包含请求/响应格式 | -| [架构设计](./architecture.md) | 系统架构、模块设计、数据模型、BaaS 资源管理 | -| [部署指南](./deployment.md) | 环境配置、Docker 部署、运维指南 | -| [权限系统](./permission.md) | RBAC+ABAC 权限模型、三级权限体系使用指南 | - -## 项目定位:可私有化部署的 IAM + BaaS 产品 - -VBase 是一个 **产品化的 IAM + BaaS 平台**,你可以将其**售卖/部署**给终端客户: - -### 三层级架构 - -| 层级 | 角色 | 说明 | -|------|------|------| -| **平台层** | 平台运营商(你) | 管理 VBase 产品,提供部署和运维服务 | -| **实例层** | 客户(2C/2B/混合) | 独立部署 VBase 实例,完全隔离 | -| **业务层** | 终端用户 | 使用客户的产品,与你无直接关系 | - -### 两种交付模式 - -1. **托管部署**:你在自己的云基础设施上为客户部署 VBase 实例,负责运维 -2. **客户自托管**:客户在自己的环境中部署,数据完全自主可控 - -### 客户类型 - -- **2C 产品公司**(社交 App、电商平台):用 VBase 管理用户注册、登录、个人数据 -- **2B 产品公司**(SaaS、企业软件):用 VBase 做多租户隔离、组织架构、权限管理 -- **混合产品公司**(电商平台、O2O):同时管理 C 端用户和 B 端企业 - -## 核心特性 - -- **项目-资源隔离**: 多项目数据隔离,支持个人/团队场景 -- **三级权限体系**: 平台级 → 项目级 → 资源级 -- **RBAC + ABAC**: 角色与策略结合的细粒度权限控制 -- **Redis 缓存**: 权限缓存 1 分钟 TTL,平衡性能与实时性 -- **资源级权限**: 数据库、存储、函数、API 密钥的细粒度控制 - -## 快速开始 - -### 1. 启动服务 - -```bash -# 使用 Docker Compose -docker-compose up -d - -# 或使用源码 -go run ./cli/main.go -``` - -### 2. 获取初始管理员密码 - -查看日志获取自动生成的管理员密码: - -```bash -docker logs vbase_app_1 | grep -A 5 "Initial admin" -``` - -### 3. 调用 API - -```bash -# 登录 -curl -X POST http://localhost:8080/auth/login \ - -H "Content-Type: application/json" \ - -d '{"username":"admin","password":"your-password"}' - -# 访问用户列表 -curl http://localhost:8080/users \ - -H "Authorization: Bearer your-access-token" -``` - -## 配置 OAuth 登录 - -### Google 登录 - -1. 访问 [Google Cloud Console](https://console.cloud.google.com/) -2. 创建 OAuth 2.0 客户端 -3. 配置环境变量: - -```bash -OAUTH_GOOGLE_ENABLED=true -OAUTH_GOOGLE_CLIENT_ID=your-client-id -OAUTH_GOOGLE_CLIENT_SECRET=your-client-secret -``` - -### GitHub 登录 - -1. 访问 [GitHub Settings](https://github.com/settings/developers) -2. 创建 OAuth App -3. 配置环境变量: - -```bash -OAUTH_GITHUB_ENABLED=true -OAUTH_GITHUB_CLIENT_ID=your-client-id -OAUTH_GITHUB_CLIENT_SECRET=your-client-secret -``` - -## 常见问题 - -### Q: 首次启动时管理员密码是什么? - -A: 如果未设置 `INIT_ADMIN_PASSWORD`,系统会自动生成随机密码并打印在日志中。 - -### Q: 如何重置管理员密码? - -A: 直接修改数据库中的 `users` 表,使用 bcrypt 生成新密码哈希。 - -### Q: 支持哪些数据库? - -A: 支持 MySQL 5.7+、PostgreSQL 12+、SQLite 3。 - -### Q: 如何实现单点登录(SSO)? - -A: VBase 可以作为 OAuth2.0/OIDC 服务端,其他应用配置为 OAuth 客户端即可实现 SSO。 - -## 贡献指南 - -欢迎提交 Issue 和 PR。 - -## 许可证 - -MIT License diff --git a/doc/api.md b/doc/api.md deleted file mode 100644 index 8f2f95f..0000000 --- a/doc/api.md +++ /dev/null @@ -1,741 +0,0 @@ -# VBase API 文档 - -## 概述 - -VBase 是一个多租户身份认证与访问管理(IAM)系统,支持用户管理、组织管理、RBAC权限控制和OAuth2.0服务。 - -**基础信息** -- 基础URL: `https://api.example.com` -- 请求格式: `application/json` -- 响应格式: `application/json` -- 认证方式: Bearer Token (JWT) - -**通用响应格式** -```json -{ - "code": 200, - "message": "success", - "data": {} -} -``` - -**错误响应** -```json -{ - "code": 400, - "message": "error message", - "data": null -} -``` - ---- - -## 认证相关 - -### 用户登录 - -**POST** `/auth/login` - -用户登录获取访问令牌。 - -**请求参数** - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| username | string | 是 | 用户名/邮箱/手机号 | -| password | string | 是 | 密码 | -| remember | bool | 否 | 记住登录 | - -**响应示例** -```json -{ - "code": 200, - "data": { - "access_token": "eyJhbGciOiJIUzI1NiIs...", - "refresh_token": "eyJhbGciOiJIUzI1NiIs...", - "token_type": "Bearer", - "expires_in": 3600, - "user": { - "id": "user-id", - "username": "admin", - "nickname": "管理员", - "email": "admin@example.com", - "avatar": "https://..." - } - } -} -``` - ---- - -### 用户注册 - -**POST** `/auth/register` - -注册新用户。 - -**请求参数** - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| username | string | 是 | 用户名(2-50字符) | -| password | string | 是 | 密码(至少8位) | -| email | string | 否 | 邮箱 | -| phone | string | 否 | 手机号 | -| nickname | string | 否 | 昵称 | - ---- - -### 刷新Token - -**POST** `/auth/refresh` - -使用刷新令牌获取新的访问令牌。 - -**请求参数** - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| refresh_token | string | 是 | 刷新令牌 | - ---- - -### 用户登出 - -**POST** `/auth/logout` - -用户登出,使当前Token失效。 - -**请求头** -- Authorization: Bearer {access_token} - ---- - -### 支持的第三方登录提供商 - -**GET** `/auth/providers` - -获取系统支持的第三方登录提供商列表。 - -**响应示例** -```json -{ - "code": 200, - "data": [ - { - "name": "google", - "display_name": "Google", - "icon": "google", - "enabled": true - }, - { - "name": "github", - "display_name": "GitHub", - "icon": "github", - "enabled": true - } - ] -} -``` - ---- - -### 第三方登录授权 - -**GET** `/auth/authorize/thirdparty` - -获取第三方登录授权URL。 - -**请求参数** - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| provider | string | 是 | 提供商: google/github/wechat | -| redirect | string | 否 | 登录成功后重定向地址 | -| bind_mode | bool | 否 | 是否为绑定模式 | - -**响应示例** -```json -{ - "code": 200, - "data": { - "auth_url": "https://github.com/login/oauth/authorize?...", - "state": "random-state-string" - } -} -``` - ---- - -### 第三方登录回调 - -**GET** `/auth/callback/{provider}` - -处理第三方登录回调。 - -**路径参数** -- provider: 提供商名称 - -**查询参数** -- code: 授权码 -- state: 状态值 - -**响应示例(已绑定)** -```json -{ - "code": 200, - "data": { - "need_bind": false, - "access_token": "eyJhbGciOiJIUzI1NiIs...", - "refresh_token": "eyJhbGciOiJIUzI1NiIs...", - "token_type": "Bearer", - "expires_in": 3600, - "user": {...} - } -} -``` - -**响应示例(未绑定)** -```json -{ - "code": 200, - "data": { - "need_bind": true, - "provider": "github", - "provider_id": "12345", - "auth_url": "/auth/bind?token=xxx" - } -} -``` - ---- - -### 绑定第三方账号 - -**POST** `/auth/bind` - -将第三方账号绑定到已有账号。 - -**请求参数** - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| temp_token | string | 是 | 临时绑定令牌 | -| username | string | 是 | 用户名/邮箱/手机号 | -| password | string | 是 | 密码 | - ---- - -## 当前用户 - -### 获取当前用户信息 - -**GET** `/me` - -获取当前登录用户的信息。 - -**请求头** -- Authorization: Bearer {access_token} - -**响应示例** -```json -{ - "code": 200, - "data": { - "id": "user-id", - "username": "admin", - "nickname": "管理员", - "email": "admin@example.com", - "avatar": "https://...", - "status": 1, - "email_verified": true - } -} -``` - ---- - -### 更新当前用户信息 - -**PATCH** `/me` - -更新当前用户的基本信息。 - -**请求参数** - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| nickname | string | 否 | 昵称 | -| avatar | string | 否 | 头像URL | -| email | string | 否 | 邮箱 | - ---- - -### 修改密码 - -**POST** `/me/change-password` - -修改当前用户密码。 - -**请求参数** - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| old_password | string | 是 | 旧密码 | -| new_password | string | 是 | 新密码(至少8位) | - ---- - -### 获取第三方绑定列表 - -**GET** `/me/bindings` - -获取当前用户绑定的第三方账号列表。 - -**响应示例** -```json -{ - "code": 200, - "data": [ - { - "provider": "github", - "provider_name": "GitHub User", - "avatar": "https://...", - "email": "user@example.com", - "created_at": "2024-01-01 10:00:00" - } - ] -} -``` - ---- - -### 解绑第三方账号 - -**DELETE** `/me/bindings/{provider}` - -解除指定提供商的账号绑定。 - -**路径参数** -- provider: 提供商名称 (google/github/wechat) - ---- - -## 用户管理 - -### 用户列表 - -**GET** `/users` - -获取用户列表(需要权限)。 - -**请求参数** - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| page | int | 否 | 页码,默认1 | -| page_size | int | 否 | 每页数量,默认10 | -| keyword | string | 否 | 搜索关键词 | -| status | int | 否 | 状态筛选 | - -**响应示例** -```json -{ - "code": 200, - "data": { - "items": [...], - "total": 100, - "page": 1, - "page_size": 10, - "total_pages": 10 - } -} -``` - ---- - -### 创建用户 - -**POST** `/users` - -创建新用户(需要权限)。 - -**请求参数** - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| username | string | 是 | 用户名 | -| password | string | 是 | 密码 | -| nickname | string | 否 | 昵称 | -| email | string | 否 | 邮箱 | -| phone | string | 否 | 手机号 | -| status | int | 否 | 状态 | - ---- - -### 获取用户详情 - -**GET** `/users/{user_id}` - -获取指定用户的详细信息。 - -**路径参数** -- user_id: 用户ID - ---- - -### 更新用户 - -**PATCH** `/users/{user_id}` - -更新指定用户的信息。 - -**路径参数** -- user_id: 用户ID - -**请求参数** - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| nickname | string | 否 | 昵称 | -| avatar | string | 否 | 头像 | -| email | string | 否 | 邮箱 | -| phone | string | 否 | 手机号 | -| status | int | 否 | 状态 | - ---- - -### 删除用户 - -**DELETE** `/users/{user_id}` - -删除指定用户(软删除)。 - ---- - -### 更新用户状态 - -**PATCH** `/users/{user_id}/status` - -更新用户状态。 - -**请求参数** - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| status | int | 是 | 0:禁用 1:正常 2:未激活 | - ---- - -## 组织管理 - -### 组织列表 - -**GET** `/orgs` - -获取当前用户的组织列表。 - -**请求参数** - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| page | int | 否 | 页码 | -| page_size | int | 否 | 每页数量 | -| keyword | string | 否 | 搜索关键词 | - -**响应示例** -```json -{ - "code": 200, - "data": { - "items": [ - { - "id": "org-id", - "name": "技术部", - "code": "tech", - "owner_id": "user-id", - "path": "/tech", - "level": 0, - "status": 1, - "my_roles": ["admin"], - "my_status": 1 - } - ], - "total": 10 - } -} -``` - ---- - -### 创建组织 - -**POST** `/orgs` - -创建新组织。 - -**请求参数** - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| name | string | 是 | 组织名称 | -| code | string | 是 | 组织编码(唯一) | -| parent_id | string | 否 | 父组织ID | -| description | string | 否 | 描述 | -| max_members | int | 否 | 最大成员数 | - ---- - -### 获取组织详情 - -**GET** `/orgs/{org_id}` - -获取组织详细信息。 - ---- - -### 更新组织 - -**PATCH** `/orgs/{org_id}` - -更新组织信息(仅所有者)。 - ---- - -### 删除组织 - -**DELETE** `/orgs/{org_id}` - -删除组织(仅所有者,需无子组织)。 - ---- - -### 组织树 - -**GET** `/orgs/tree` - -获取当前用户的组织树结构。 - -**响应示例** -```json -{ - "code": 200, - "data": [ - { - "id": "org-id", - "name": "总公司", - "children": [ - { - "id": "sub-org-id", - "name": "技术部" - } - ] - } - ] -} -``` - ---- - -### 组织成员列表 - -**GET** `/orgs/{org_id}/members` - -获取组织成员列表。 - -**请求参数** - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| page | int | 否 | 页码 | -| page_size | int | 否 | 每页数量 | -| status | int | 否 | 状态筛选 | - ---- - -## OAuth2.0 服务端 - -### 授权端点 - -**GET** `/oauth/authorize` - -OAuth2.0 授权端点,获取授权码。 - -**请求参数** - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| response_type | string | 是 | code/token | -| client_id | string | 是 | 客户端ID | -| redirect_uri | string | 是 | 回调地址 | -| scope | string | 否 | 权限范围 | -| state | string | 是 | CSRF防护 | -| code_challenge | string | 否 | PKCE挑战码 | -| code_challenge_method | string | 否 | S256/plain | - ---- - -### 令牌端点 - -**POST** `/oauth/token` - -OAuth2.0 令牌端点,交换授权码获取访问令牌。 - -**请求参数** - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| grant_type | string | 是 | authorization_code/refresh_token/client_credentials | -| code | string | 条件 | 授权码 | -| redirect_uri | string | 条件 | 回调地址 | -| client_id | string | 是 | 客户端ID | -| client_secret | string | 是 | 客户端密钥 | -| refresh_token | string | 条件 | 刷新令牌 | -| code_verifier | string | 条件 | PKCE验证器 | - ---- - -### 撤销令牌 - -**POST** `/oauth/revoke` - -撤销访问令牌或刷新令牌。 - ---- - -### 令牌内省 - -**POST** `/oauth/introspect` - -查询令牌信息(RFC 7662)。 - ---- - -### 用户信息 - -**GET** `/oauth/userinfo` - -OIDC 用户信息端点。 - ---- - -### OIDC 发现文档 - -**GET** `/.well-known/openid-configuration` - -获取 OIDC 配置信息。 - ---- - -### JWKS - -**GET** `/oauth/jwks` - -获取 JWT 签名公钥。 - ---- - -## OAuth 客户端管理 - -### 客户端列表 - -**GET** `/oauth/clients` - -获取 OAuth 客户端列表。 - ---- - -### 创建客户端 - -**POST** `/oauth/clients` - -创建新的 OAuth 客户端。 - -**请求参数** - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| name | string | 是 | 应用名称 | -| description | string | 否 | 描述 | -| redirect_uris | []string | 是 | 回调地址列表 | -| grant_types | []string | 否 | 授权类型 | -| allowed_scopes | []string | 否 | 允许的范围 | - -**响应示例** -```json -{ - "code": 200, - "data": { - "id": "client-id", - "name": "My App", - "client_id": "abc123...", - "client_secret": "secret...", - "redirect_uris": ["https://app.com/callback"], - "grant_types": ["authorization_code", "refresh_token"] - } -} -``` - ---- - -### 获取客户端详情 - -**GET** `/oauth/clients/{client_id}` - -获取 OAuth 客户端详情。 - ---- - -### 更新客户端 - -**PATCH** `/oauth/clients/{client_id}` - -更新 OAuth 客户端信息。 - ---- - -### 删除客户端 - -**DELETE** `/oauth/clients/{client_id}` - -删除 OAuth 客户端。 - ---- - -### 重新生成密钥 - -**POST** `/oauth/clients/{client_id}/regenerate-secret` - -重新生成客户端密钥。 - ---- - -## 请求头说明 - -### 认证头 - -``` -Authorization: Bearer {access_token} -``` - -### 组织上下文 - -``` -X-Org-ID: {organization_id} -``` - -用于指定当前操作的组织上下文。 - ---- - -## 状态码说明 - -| 状态码 | 说明 | -|--------|------| -| 200 | 成功 | -| 400 | 请求参数错误 | -| 401 | 未认证 | -| 403 | 无权限 | -| 404 | 资源不存在 | -| 429 | 请求过于频繁 | -| 500 | 服务器内部错误 | diff --git a/doc/architecture.md b/doc/architecture.md deleted file mode 100644 index 5f40251..0000000 --- a/doc/architecture.md +++ /dev/null @@ -1,906 +0,0 @@ -# VBase 架构设计文档 - -## 1. 系统概述 - -VBase 是一个类似 **Supabase** 的 **Backend-as-a-Service (BaaS)** 平台,为个人开发者(2C)和团队/企业(2B)提供后端资源托管和权限管理服务。采用 Go 语言开发,基于 Vigo 框架构建。 - -### 1.1 产品定位:可私有化部署的 IAM + BaaS 平台 - -VBase 是一个**产品化**的解决方案,你可以将 VBase **售卖/部署**给终端客户,客户在自己的环境中独立运行 VBase 实例,完全隔离地管理自己的业务。 - -#### 三层级架构模型 - -``` -┌─────────────────────────────────────────────────────────────┐ -│ Layer 1: VBase 平台运营层 (你的 SaaS 平台) │ -│ - 你是 VBase 产品的提供商 │ -│ - 功能:实例管理、计费、监控、版本分发 │ -│ - 部署模式:托管部署 or 客户自托管 │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ 部署/交付 -┌─────────────────────────────────────────────────────────────┐ -│ Layer 2: VBase 实例层 (客户的独立部署) │ -│ - 每个客户拥有一个完全独立的 VBase 实例 │ -│ - 独立数据库、独立用户体系、独立配置 │ -│ - 客户可以是:2C 产品 / 2B 产品 / 混合产品 │ -│ - 管理员:客户的技术负责人/运维 │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ 使用 -┌─────────────────────────────────────────────────────────────┐ -│ Layer 3: 客户业务层 (与你无关) │ -│ - 客户用 VBase 管理自己的终端用户和业务项目 │ -│ ├─ 2C 场景:App 用户注册登录、个人数据管理 │ -│ ├─ 2B 场景:企业内部系统、团队协作 │ -│ └─ 混合场景:既有终端用户又有内部团队 │ -│ - 用户数据完全属于客户,与你无关 │ -└─────────────────────────────────────────────────────────────┘ -``` - -#### 目标客户类型 - -| 客户类型 | 场景说明 | VBase 提供的价值 | -| ---------------- | ------------------------------ | -------------------------------- | -| **2C 产品公司** | 社交 App、电商平台、内容社区等 | 用户认证、个人数据管理、权限控制 | -| **2B 产品公司** | SaaS 软件、企业管理系统 | 多租户隔离、组织架构、RBAC 权限 | -| **混合产品公司** | 既有 C 端用户又有 B 端管理 | 统一身份管理、分级权限控制 | - -### 1.2 核心特性 - -- **项目-资源管理**: 用户可以创建多个项目,每个项目包含独立的后端资源 -- **灵活的成员协作**: 支持邀请其他用户加入项目,分配不同角色权限 -- **多层级权限控制**: 平台级 → 项目级 → 资源级的三级权限体系 -- **RBAC + ABAC 权限模型**: 角色与策略结合的细粒度权限控制 -- **OAuth2.0/OIDC 服务端**: 支持作为身份提供商,为应用提供认证服务 -- **第三方登录集成**: Google、GitHub、微信等快速登录 -- **JWT 认证**: 无状态的 Token 认证机制 - -### 1.3 技术栈 - -| 层级 | 技术 | -| -------- | ------------------------------- | -| 框架 | Vigo (Go Web Framework) | -| ORM | GORM | -| 数据库 | MySQL / PostgreSQL / SQLite | -| 缓存 | Redis | -| 认证 | JWT (github.com/golang-jwt/jwt) | -| 密码哈希 | bcrypt | - -### 1.4 概念说明 - -#### 三层级角色体系 - -| 层级 | 角色 | 说明 | 权限范围 | -| ---------- | ---------- | -------------------------- | ------------------------- | -| **平台层** | 平台管理员 | VBase 产品提供商(你) | 管理所有 VBase 实例 | -| **实例层** | 实例管理员 | 客户的技术负责人 | 管理自己的 VBase 实例配置 | -| **业务层** | 终端用户 | 客户的 C 端用户或 B 端成员 | 使用客户的产品功能 | - -#### VBase 实例内部概念 - -在一个独立的 VBase 实例内部,包含以下核心概念: - -**用户 (User)**: VBase 实例中的注册用户 - -- 可以创建多个项目 -- 可以被邀请加入其他项目 -- 在不同项目中可以有不同的角色 - -**项目/组织 (Project/Org)**: 资源管理的基本单元 - -- 对 2C 场景:对应一个 App 或产品 -- 对 2B 场景:对应一个企业或部门 -- 企业可以创建多个项目,也可以使用组织层级管理 - -**资源 (Resource)**: 项目内的后端资源 - -- 数据库、存储、函数、API 等 -- 属于特定项目 -- 通过项目权限控制访问 - -**角色 (Role)**: 项目内的权限集合 - -- 所有者 (Owner): 项目全部权限 -- 管理员 (Admin): 管理项目和成员 -- 开发者 (Developer): 读写资源 -- 访客 (Viewer): 只读访问 - ---- - -## 2. 系统架构 - -### 2.1 整体架构图 - -``` -┌─────────────────────────────────────────────────────────────┐ -│ Client Layer │ -│ (Web App / Mobile App / Third-party Services) │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ API Gateway │ -│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ -│ │ Auth Filter │ │ Rate Limiter │ │ Org Context │ │ -│ └──────────────┘ └──────────────┘ └──────────────┘ │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ Application Layer │ -│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ -│ │ Auth │ │ User │ │ Org │ │ OAuth │ │ -│ │ Handler │ │ Handler │ │ Handler │ │ Handler │ │ -│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ -│ │ -│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ -│ │ Role │ │ Policy │ │Permission│ │ -│ │ Handler │ │ Handler │ │ Service │ │ -│ └──────────┘ └──────────┘ └──────────┘ │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ Service Layer │ -│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ -│ │ Permission │ │ JWT │ │ Crypto │ │ -│ │ Checker │ │ Service │ │ Service │ │ -│ └──────────────┘ └──────────────┘ └──────────────┘ │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ Data Layer │ -│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ -│ │ User │ │ Org │ │ OAuth │ ... │ -│ │ Model │ │ Model │ │ Model │ │ -│ └──────────┘ └──────────┘ └──────────┘ │ -│ │ -│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ -│ │ MySQL/PostgreSQL │ Redis │ Repository │ -│ └──────────────┘ └──────────────┘ └──────────────┘ │ -└─────────────────────────────────────────────────────────────┘ -``` - ---- - -## 3. 部署模式:VBase 作为产品交付 - -VBase 设计为可私有化部署的产品,支持两种交付模式: - -### 3.1 模式一:托管部署(你帮客户部署运维) - -``` -┌─────────────────────────────────────────────────────────────┐ -│ 你的云基础设施 │ -│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ -│ │ 客户A实例 │ │ 客户B实例 │ │ 客户C实例 │ │ -│ │ (2C产品) │ │ (2B产品) │ │ (混合产品) │ │ -│ │ │ │ │ │ │ │ -│ │ DB: A_db │ │ DB: B_db │ │ DB: C_db │ │ -│ │ Redis: A_1 │ │ Redis: B_1 │ │ Redis: C_1 │ │ -│ └─────────────┘ └─────────────┘ └─────────────┘ │ -└─────────────────────────────────────────────────────────────┘ -``` - -**特点:** - -- 你在自己的云基础设施上为客户部署独立的 VBase 实例 -- 每个实例完全隔离(独立数据库、独立 Redis) -- 你负责运维,客户只管使用 -- 可按照实例数量、资源使用量计费 - -**适用客户:** 没有运维能力的小团队,希望快速使用 - -### 3.2 模式二:客户自托管(客户自己部署) - -``` -┌─────────────────────────────────────────────────────────────┐ -│ 客户 A 的基础设施 │ -│ ┌─────────────────────────────────────────────────────┐ │ -│ │ VBase 实例 │ │ -│ │ ┌──────────────┐ ┌──────────────┐ │ │ -│ │ │ 用户认证 │ │ 权限管理 │ │ │ -│ │ │ (2C用户) │ │ (2B组织) │ │ │ -│ │ └──────────────┘ └──────────────┘ │ │ -│ │ │ │ -│ │ ┌──────────────┐ ┌──────────────┐ │ │ -│ │ │ 数据库资源 │ │ 存储资源 │ │ │ -│ │ └──────────────┘ └──────────────┘ │ │ -│ └─────────────────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────────────────┘ -``` - -**特点:** - -- 客户在自己的环境中部署 VBase(云服务器、私有云、K8s) -- 数据完全属于客户,与你无关 -- 客户自己运维,或者购买你的运维支持服务 -- 通常按许可(License)或订阅模式收费 - -**适用客户:** 有数据合规要求的企业,有自己运维能力的团队 - -### 3.3 两种模式的对比 - -| 维度 | 托管部署 | 客户自托管 | -| ------------ | ---------------- | --------------- | -| **部署位置** | 你的云基础设施 | 客户的基础设施 | -| **数据归属** | 客户拥有,你代管 | 客户完全拥有 | -| **运维责任** | 你负责 | 客户负责 | -| **计费模式** | 按用量/实例数 | 按 License/订阅 | -| **定制化** | 受限 | 完全可定制 | -| **网络要求** | 需要公网访问 | 可内网部署 | - -这种设计确保: - -1. 客户 A 的数据与客户 B 完全隔离 -2. 一个实例的故障不影响其他实例 -3. 客户可以独立升级、备份、迁移自己的实例 - ---- - -## 4. 目录结构(Vigo 规范) - -``` -vbase/ -├── api/ # API 层(按资源模块组织) -│ ├── init.go # API根路由,注册全局中间件 -│ ├── auth/ # 认证模块 -│ │ ├── init.go # 路由注册 -│ │ ├── login.go # 登录/登出/刷新 -│ │ ├── register.go # 用户注册 -│ │ ├── me.go # 当前用户信息 -│ │ └── thirdparty.go# 第三方登录 -│ ├── user/ # 用户管理 -│ │ ├── init.go -│ │ ├── get.go -│ │ ├── list.go -│ │ ├── create.go -│ │ ├── patch.go -│ │ ├── del.go -│ │ └── status.go -│ ├── org/ # 组织/项目管理 -│ │ ├── init.go -│ │ ├── get.go -│ │ ├── list.go -│ │ ├── create.go -│ │ ├── patch.go -│ │ ├── del.go -│ │ ├── tree.go -│ │ └── member.go -│ ├── oauth/ # OAuth2.0/OIDC 服务端 -│ │ ├── init.go -│ │ ├── authorize.go # 授权端点 -│ │ ├── token.go # 令牌端点 -│ │ ├── revoke.go # 撤销令牌 -│ │ ├── oidc.go # OIDC 支持 -│ │ └── client.go # 客户端管理 -│ ├── policy/ # 策略管理(RBAC) -│ │ ├── init.go -│ │ ├── list.go -│ │ ├── get.go -│ │ ├── create.go -│ │ ├── patch.go -│ │ └── del.go -│ ├── role/ # 角色管理(RBAC) -│ │ ├── init.go -│ │ ├── list.go -│ │ ├── get.go -│ │ ├── create.go -│ │ ├── patch.go -│ │ ├── del.go -│ │ └── policy.go # 角色策略关联 -│ └── middleware/ # 中间件 -│ ├── auth.go # JWT 认证 -│ ├── org.go # 组织上下文 -│ └── ratelimit.go # 限流 -│ -├── models/ # 数据模型层(GORM) -│ ├── init.go # 模型注册、迁移 -│ ├── base.go # 基础模型 -│ ├── user.go # User/Identity/Session -│ ├── org.go # Org/OrgMember -│ ├── policy.go # Policy/Role -│ └── oauth.go # OAuthClient/OAuthToken -│ -├── cfg/ # 配置(Vigo 自动解析) -│ ├── cfg.go # Options 结构体 -│ ├── db.go # 数据库连接 -│ └── sms.go # SMS 配置 -│ -├── cli/ # 独立运行入口 -│ └── main.go # 启动服务器/数据库迁移 -│ -├── libs/ # 工具库 -│ ├── cache/ # Redis 缓存封装 -│ ├── jwt/ # JWT 工具 -│ ├── crypto/ # 加密工具 -│ ├── sms_providers/ # 短信服务商 -│ ├── utils/ # 通用工具 -│ └── webdav/ # WebDAV 实现 -│ -├── init.go # 项目根路由(vbase.Router) -├── doc/ # 文档 -└── ui/ # 前端 -``` - -### 使用方式 - -**独立运行:** - -```bash -go run cli/main.go -``` - -**集成到其他项目:** - -```go -import "github.com/veypi/vbase" - -// 在你的路由中挂载 vbase -router.Extend("vb", vbase.Router) // 所有 vbase API 将通过 /vb/api/... 访问 -``` - -### Vigo 路由规范 - -**父路由挂载子路由模块** - 使用 `Extend`: - -```go -// api/init.go -Router.Extend("/auth", auth.Router) // 将 auth.Router 挂载到 /api/auth -``` - -**同级创建子路由** - 使用 `SubRouter`: - -```go -// api/oauth/init.go -clientRouter := Router.SubRouter("/clients") // 创建 /oauth/clients 子路由 -clientRouter.Get("/", "列表", listClients) -``` - -**跳过中间件** - 使用 `vigo.SkipBefore`: - -```go -// 公开端点跳过认证中间件 -Router.Post("/login", "登录", vigo.SkipBefore, login) -``` - -### 上下文键名 - -中间件设置的上下文键名: - -| 键名 | 类型 | 说明 | -| -------------- | -------------- | --------------------------------- | -| `user_id` | string | 当前用户 ID | -| `user_name` | string | 当前用户名 | -| `user_orgs` | []jwt.OrgClaim | 用户所属组织列表 | -| `token_claims` | \*jwt.Claims | 完整的 JWT 声明 | -| `org_id` | string | 当前组织 ID(如果请求指定了组织) | -| `org_roles` | string | 当前组织角色 ID 列表 | - ---- - -## 4. 核心模块设计 - -### 4.1 认证模块 - -#### JWT Token 结构 - -```go -// Claims JWT 声明 -type Claims struct { - jwt.RegisteredClaims - UserID string `json:"user_id"` - Username string `json:"username"` - Nickname string `json:"nickname"` - Avatar string `json:"avatar"` - Email string `json:"email"` - Orgs []OrgClaim `json:"orgs"` // 用户所属组织 - Type string `json:"type"` // access/refresh -} -``` - -#### 认证流程 - -1. **登录**: 验证用户名密码 → 生成 Token 对 → 保存会话 -2. **访问**: 验证 Token → 解析用户上下文 → 组织上下文 -3. **刷新**: 验证 Refresh Token → 生成新 Token 对 → 失效旧 Token -4. **登出**: Token 加入黑名单 → 标记会话失效 - -### 4.2 权限模块 - -#### 权限层级 - -VBase 采用三级权限体系: - -``` -┌─────────────────────────────────────────────────────────────┐ -│ 1. 平台级权限 (Platform) │ -│ - 用户管理自己的账号 │ -│ - 创建项目 │ -│ - 管理第三方登录绑定 │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 2. 项目级权限 (Project) │ -│ - 项目设置管理 │ -│ - 成员邀请和管理 │ -│ - 资源创建和管理 │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 3. 资源级权限 (Resource) │ -│ - 数据库读写 │ -│ - 存储操作 │ -│ - 函数调用 │ -└─────────────────────────────────────────────────────────────┘ -``` - -#### 权限模型: RBAC + ABAC - -**策略模型:** - -```go -type Policy struct { - Resource string // 资源: user/org/resource/* - Action string // 操作: create/read/update/delete/* - Effect string // 效果: allow/deny - Condition string // 条件: "owner", "org_member", 或复杂表达式 - Scope string // 作用域: platform/org/resource -} -``` - -**权限检查流程:** - -``` -┌─────────────┐ -│ 请求进入 │ -└──────┬──────┘ - ▼ -┌─────────────┐ 命中 ┌─────────────┐ -│ 检查缓存 │──────────→│ 返回结果 │ -└──────┬──────┘ └─────────────┘ - │ 未命中 - ▼ -┌─────────────┐ -│ 检查组织所有者│──────────→│ 允许(缓存) │ -└──────┬──────┘ └─────────────┘ - │ 不是所有者 - ▼ -┌─────────────┐ -│ 获取用户角色 │ -└──────┬──────┘ - ▼ -┌─────────────┐ -│ 获取角色策略 │ -└──────┬──────┘ - ▼ -┌─────────────┐ Deny ┌─────────────┐ -│ 检查Deny策略 │───────────→│ 拒绝(缓存) │ -└──────┬──────┘ └─────────────┘ - │ 无Deny - ▼ -┌─────────────┐ Allow ┌─────────────┐ -│ 检查Allow策略│───────────→│ 允许(缓存) │ -└──────┬──────┘ └─────────────┘ - │ 无Allow - ▼ -┌─────────────┐ -│ 默认拒绝 │───────────→│ 拒绝(缓存) │ -└─────────────┘ └─────────────┘ -``` - -#### 缓存策略 - -- **缓存键**: `perm:{user_id}:{org_id}:{resource}:{action}` -- **TTL**: 1 分钟 -- **缓存失效**: 角色/策略变更时清除相关缓存 - -### 4.3 项目/组织模块 - -VBase 中的 "Org" 概念灵活适配不同场景: - -#### 场景 1: 个人项目 (2C) - -``` -个人开发者 -└── 我的项目A (Org: owner=个人) -└── 我的项目B (Org: owner=个人) -``` - -- 每个项目是一个独立的 Org -- 资源完全隔离 -- 可以邀请其他用户协作 - -#### 场景 2: 团队协作 (2B) - -``` -创业公司团队 -├── Web应用项目 (Org) -│ ├── 所有者: CEO -│ ├── 管理员: CTO -│ └── 开发者: 员工A, 员工B -└── 移动应用项目 (Org) - ├── 所有者: CEO - └── ... -``` - -#### 场景 3: 企业多层级 (2B Enterprise) - -``` -企业层级 (可选功能) -总公司 (level=0) -├── 技术中台 (level=1, Org) -├── 业务线A (level=1) -│ ├── 项目1 (level=2, Org) -│ └── 项目2 (level=2, Org) -└── 业务线B (level=1) - └── 项目3 (level=2, Org) -``` - -### 4.4 典型客户场景示例 - -以下示例展示 VBase 作为产品交付的完整链路: - -#### 示例 1: 2C 产品公司(社交 App) - -``` -你(VBase 提供商) -│ -├─ 售卖 VBase 实例给「社交App公司」 -│ -▼ -客户:社交App公司(2C产品) -│ -├─ 部署 VBase 实例到自己的服务器(自托管模式) -│ -├─ 技术负责人配置 VBase: -│ ├─ 开启 App 用户注册/登录 -│ ├─ 配置数据库存储用户资料 -│ └─ 设置存储桶保存用户头像 -│ -▼ -终端用户:社交 App 的普通用户 - ├─ 注册账号(存储在 VBase) - ├─ 登录获取 JWT Token - ├─ 上传头像到 VBase 存储 - └─ 查询好友列表(VBase 数据库) -``` - -**关键点:** - -- 社交 App 公司的用户数据完全在自己的 VBase 实例中 -- 你作为 VBase 提供商,接触不到终端用户数据 -- 社交 App 公司按需购买 License 或订阅服务 - -#### 示例 2: 2B 产品公司(SaaS 软件) - -``` -你(VBase 提供商) -│ -├─ 托管部署 VBase 实例给「企业SaaS公司」 -│ (部署在你的云服务器上) -│ -▼ -客户:企业SaaS公司(2B产品) -│ -├─ 使用 VBase 管理自己的企业客户 -│ ├─ 每个企业客户 = VBase 中的一个 Org -│ ├─ 企业内的部门 = Org 下的子组织 -│ └─ 企业员工 = OrgMember -│ -▼ -终端用户:SaaS 软件的企业用户 - ├─ 企业 A 的员工使用 VBase 登录 SaaS - ├─ 按角色分配权限(管理员/普通员工) - └─ 企业间的数据通过 VBase 完全隔离 -``` - -**关键点:** - -- SaaS 公司用 VBase 做租户隔离(多租户架构) -- 你帮 SaaS 公司运维 VBase 实例,按实例数/用户量收费 -- SaaS 公司的终端企业用户与你无直接关系 - -#### 示例 3: 混合产品公司(2C + 2B) - -``` -你(VBase 提供商) -│ -├─ 售卖两个 VBase 实例给「电商平台公司」 -│ (一个给 C 端,一个给 B 端管理后台) -│ -▼ -客户:电商平台公司(混合产品) -│ -├─ 实例1:C 端用户系统 -│ ├─ 消费者注册/登录 -│ ├─ 购物车、订单数据 -│ └─ 用户画像存储 -│ -└─ 实例2:B 端商家管理系统 - ├─ 商家入驻/审核 - ├─ 商家员工权限管理 - └─ 运营数据分析 -``` - -**关键点:** - -- 同一客户的不同业务线可以用独立 VBase 实例 -- 完全物理隔离,符合数据合规要求 -- 你提供统一的管理控制台给客户技术团队 - -#### 数据隔离 - -- **项目隔离**: 每个 Org 是独立的资源边界 -- **用户-项目关联**: 通过 `OrgMember` 表维护多对多关系 -- **资源归属**: 所有资源通过 `org_id` 归属到具体项目 -- **跨项目访问**: 需要显式授权,默认隔离 - -### 4.5 BaaS 资源管理模块 - -作为 Supabase-like BaaS 平台,VBase 提供以下后端资源管理能力: - -#### 资源类型 - -| 资源类型 | 说明 | 典型操作 | -| ------------ | --------------- | ------------------------------ | -| **Database** | 数据库表、集合 | 创建表、查询、修改结构、删除表 | -| **Storage** | 文件存储 | 上传、下载、删除、管理存储桶 | -| **Function** | 边缘函数/云函数 | 部署、调用、更新、删除 | -| **API Key** | 访问密钥 | 创建、删除、刷新、权限绑定 | - -#### 资源权限模型 - -``` -项目 (Org) -├── 资源组 (Resource Group) -│ ├── 数据库资源 -│ │ ├── table_1 (owner: user_a) -│ │ └── table_2 (owner: user_b) -│ ├── 存储资源 -│ │ ├── bucket_images (public) -│ │ └── bucket_private (private) -│ └── 函数资源 -│ ├── function_api (callable) -│ └── function_cron (internal) -``` - -#### 资源访问控制 - -```go -// 资源操作前检查权限 -func AccessResource(userID, orgID, resourceType, action string) error { - checker := service.NewPermissionChecker() - result, err := checker.Check(userID, orgID, resourceType, action, nil) - if err != nil || !result.Allowed { - return vigo.ErrForbidden.WithString(result.Reason) - } - - // 额外检查资源级别的细粒度权限 - // 例如:只能修改自己创建的表 - return nil -} -``` - -### 4.5 OAuth2.0 服务端 - -#### 支持的授权流程 - -| 流程 | 说明 | 适用场景 | -| ------------------ | ---------- | -------------------- | -| Authorization Code | 授权码模式 | 服务端应用,最安全 | -| Implicit | 简化模式 | SPA/移动端(不推荐) | -| Client Credentials | 客户端凭证 | 服务间调用 | -| Refresh Token | 刷新令牌 | 延长会话 | - -#### 安全特性 - -- **PKCE**: 防止授权码拦截攻击 -- **State**: CSRF 防护 -- **Redirect URI 白名单**: 严格匹配 -- **Token 过期**: Access Token 1 小时,Refresh Token 30 天 - ---- - -## 5. 数据模型 - -### 5.1 实体关系图 (ER Diagram) - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ 平台层 (Platform) │ -├─────────────┐ ┌─────────────┐ ┌─────────────┐ │ -│ User │ │ Identity │ │ Session │ │ -│ (平台用户) │ │ (第三方绑定)│ │ (登录会话) │ │ -├─────────────┤ ├─────────────┤ ├─────────────┤ │ -│ id (PK) │◄──────┤ user_id │ │ id (PK) │ │ -│ username │ │ provider │ │ user_id │ │ -│ password │ │ provider_uid│ │ token_id │ │ -│ email │ │ access_token│ │ type │ │ -│ status │ └─────────────┘ │ expires_at │ │ -└──────┬──────┘ └─────────────┘ │ - │ │ - │ 多对多关系(用户-项目) │ - │ ┌─────────────┐ ┌─────────────┐ │ - │ │ OrgMember │ │ Org │ │ - │ │ (项目成员) │ │ (项目) │ │ - │ ├─────────────┤ ├─────────────┤ │ - └────────►│ user_id │◄──────┤ id (PK) │ │ - │ org_id │──────►│ name │ │ - │ role_ids │ │ code │ │ - │ status │ │ owner_id │ │ - └─────────────┘ │ parent_id* │ │ - │ path* │ │ - └─────────────┘ │ - │ │ - │ 1:N │ - ▼ │ - ┌─────────────┐ │ - │ Resource │ │ - │ (项目资源) │ │ - │ (db/storage)│ │ - └─────────────┘ │ -└─────────────────────────────────────────────────────────────────┘ - -┌─────────────────────────────────────────────────────────────────┐ -│ 权限层 (Permission) │ -├─────────────┐ ┌─────────────┐ ┌─────────────┐ │ -│ Role │ │ RolePolicy │ │ Policy │ │ -│ (项目角色) │ │ (角色策略) │ │ (策略) │ │ -├─────────────┤ ├─────────────┤ ├─────────────┤ │ -│ id (PK) │◄──────┤ role_id │ │ id (PK) │ │ -│ org_id │ │ policy_id │──────►│ code │ │ -│ name │ └─────────────┘ │ resource │ │ -│ policy_ids │ │ action │ │ -│ scope │ │ effect │ │ -└─────────────┘ │ condition │ │ - │ scope │ │ - └─────────────┘ │ -└─────────────────────────────────────────────────────────────────┘ - -┌─────────────────────────────────────────────────────────────────┐ -│ OAuth层 (Identity) │ -├─────────────┐ ┌─────────────┐ ┌─────────────┐ │ -│OAuthClient │ │OAuthAuthz │ │ OAuthToken │ │ -│ (OAuth应用) │ │ (授权码) │ │ (访问令牌) │ │ -├─────────────┤ ├─────────────┤ ├─────────────┤ │ -│ id (PK) │◄──────┤ client_id │ │ id (PK) │ │ -│ client_id │ │ user_id │◄──────┤ client_id │ │ -│ client_secret│ │ code │ │ user_id │ │ -│ redirect_uris│ │ expires_at │ │ access_token│ │ -│ owner_id │ └─────────────┘ │ refresh_token│ │ -│ org_id │ │ org_id │ │ -└─────────────┘ └─────────────┘ │ -└─────────────────────────────────────────────────────────────────┘ -``` - -### 5.2 关键模型说明 - -**User (用户)** - -- 平台级唯一用户账号 -- 可同时参与多个项目 -- 支持第三方账号绑定 - -**Org (项目/组织)** - -- 资源管理的基本单元 -- 对 2C 场景: 一个项目 = 一个 Org -- 对 2B 场景: 可以是项目或企业部门 -- 支持层级结构(可选) - -**OrgMember (项目成员)** - -- 维护用户与项目的关联 -- 记录角色分配 -- 支持多种成员状态(待审核、正常、禁用) - -**Role (角色)** - -- 项目内定义的权限集合 -- 系统预置角色: owner, admin, developer, viewer -- 支持自定义角色 - ---- - -## 6. 接口设计原则 - -### 6.1 RESTful API - -- 使用 HTTP 方法表示操作: GET/POST/PATCH/DELETE -- 使用复数名词表示资源: /users, /orgs -- 使用路径参数表示 ID: /users/{user_id} - -### 6.2 请求/响应规范 - -**成功响应:** - -```json -{ - "code": 200, - "message": "success", - "data": { ... } -} -``` - -**错误响应:** - -```json -{ - "code": 400, - "message": "error description", - "data": null -} -``` - -### 6.3 分页规范 - -```json -{ - "items": [...], - "total": 100, - "page": 1, - "page_size": 10, - "total_pages": 10 -} -``` - ---- - -## 7. 安全设计 - -### 7.1 认证安全 - -- **密码策略**: bcrypt 哈希,成本因子 12 -- **JWT 安全**: HS256 算法,密钥最小 32 字节 -- **Token 过期**: 短时效 Access Token + 长时效 Refresh Token -- **Token 撤销**: 黑名单机制 - -### 7.2 接口安全 - -- **限流**: 基于 IP 和用户 ID 的滑动窗口限流 -- **CSRF 防护**: State 参数、SameSite Cookie -- **SQL 注入**: GORM 参数化查询 -- **XSS 防护**: 响应头设置 Content-Type - -### 7.3 OAuth 安全 - -- **PKCE**: 所有移动端/SPA 必须使用 -- **Redirect URI 严格匹配**: 包括路径和查询参数 -- **Scope 限制**: 每个客户端有允许的范围白名单 - ---- - -## 8. 扩展性设计 - -### 8.1 水平扩展 - -- **无状态服务**: 不依赖本地存储,可水平扩展 -- **共享缓存**: Redis 集群支持 -- **数据库读写分离**: GORM 支持读写分离配置 - -### 8.2 插件化 - -- **中间件链**: 可插拔的中间件设计 -- **策略引擎**: 支持自定义条件表达式 -- **第三方登录**: 易于添加新的 OAuth 提供商 - ---- - -## 9. 监控与日志 - -### 9.1 日志规范 - -- 请求日志: 方法、路径、耗时、状态码 -- 错误日志: 堆栈追踪、上下文信息 -- 审计日志: 敏感操作记录 - -### 9.2 指标监控 - -- QPS、响应时间、错误率 -- 缓存命中率 -- 数据库连接池状态 diff --git a/doc/developer_guide.md b/doc/developer_guide.md deleted file mode 100644 index 5052dcd..0000000 --- a/doc/developer_guide.md +++ /dev/null @@ -1,565 +0,0 @@ -# VBase 开发者使用手册 - -## 架构概述 - -``` -┌─────────────────────────────────────────────────────────────┐ -│ vbase/Auth 包 │ -│ ├─ New(appKey, config) - 创建权限管理实例 │ -│ ├─ Init() - 启动时检查冲突并同步数据库 │ -│ └─ Auth 接口 - 权限检查和管理方法 │ -└─────────────────────────────────────────────────────────────┘ - │ - ├─────────────────────┼─────────────────────┐ - ▼ ▼ ▼ - 注册权限配置 接口权限检查 编程式授权 - (代码中声明) (中间件使用) (运行时调用) -``` - -## 快速开始 - -```go -package main - -import ( - "github.com/veypi/vbase" - "github.com/veypi/vbase/api" - "github.com/veypi/vigo" -) - -// 全局权限管理实例 -crmAuth := vbase.Auth.New("crm", vbase.AppConfig{ - Name: "客户关系管理", - Description: "CRM系统", -}) - -func main() { - r := vigo.NewRouter() - - // 1. 挂载 VBase 基础设施 - r.Extend("/api/vb", api.Router) - - // 2. 程序启动时初始化所有权限配置 - if err := vbase.Auth.Init(); err != nil { - log.Fatal("权限初始化失败:", err) - } - - // 3. 创建业务路由 - crm := r.SubRouter("/api/crm") - crm.Use(middleware.AuthRequired()) - crm.Use(middleware.OrgContext()) - - // 4. 使用 crmAuth 配置接口权限 - crm.Get("/customers", crmAuth.Perm("customer", "list"), "客户列表", listCustomers) - crm.Post("/customers", crmAuth.Perm("customer", "create"), "创建客户", createCustomer) - crm.Get("/customers/{id}", crmAuth.Perm("customer", "read"), "客户详情", getCustomer) - crm.Patch("/customers/{id}", crmAuth.Perm("customer", "update"), "更新客户", updateCustomer) - crm.Delete("/customers/{id}", crmAuth.Perm("customer", "delete"), "删除客户", deleteCustomer) - - // 5. 资源所有者权限 - crm.Patch("/customers/{id}/private", - crmAuth.PermWithOwner("customer", "update", "owner_id"), - "仅限所有者", - privateUpdate) - - vigo.Run(r) -} -``` - ---- - -## 1. 权限管理包 vbase/Auth - -### 1.1 包结构 - -```go -package auth - -// Auth 权限管理接口 -type Auth interface { - // ========== 中间件生成 ========== - // 基础权限检查 - Perm(resource, action string) func(*vigo.X) error - - // 资源所有者权限 - PermWithOwner(resource, action, ownerKey string) func(*vigo.X) error - - // 满足任一权限 - PermAny(permissions [][2]string) func(*vigo.X) error - - // 满足所有权限 - PermAll(permissions [][2]string) func(*vigo.X) error - - // ========== 权限管理 ========== - // 授予角色 - GrantRole(ctx context.Context, req GrantRoleRequest) error - - // 撤销角色 - RevokeRole(ctx context.Context, userID, orgID, roleCode string) error - - // 授予特定资源权限 - GrantResourcePerm(ctx context.Context, req GrantResourcePermRequest) error - - // 撤销特定资源权限 - RevokeResourcePerm(ctx context.Context, userID, orgID, resource, resourceID string) error - - // 撤销用户所有权限 - RevokeAll(ctx context.Context, userID, orgID string) error - - // ========== 权限查询 ========== - // 检查权限 - CheckPermission(ctx context.Context, req CheckPermRequest) (bool, error) - - // 列出用户权限 - ListUserPermissions(ctx context.Context, userID, orgID string) ([]UserPermission, error) - - // 列出资源授权用户 - ListResourceUsers(ctx context.Context, orgID, resource, resourceID string) ([]ResourceUser, error) -} - -// 全局 Auth 工厂 -var Auth = &authFactory{} - -type authFactory struct { - apps map[string]*appAuth // appKey -> auth实例 -} - -// New 创建权限管理实例(注册应用) -func (f *authFactory) New(appKey string, config AppConfig) Auth - -// Init 初始化所有注册的权限配置 -// - 检查不同 app 之间是否有冲突 -// - 同步 Policy 到数据库 -// - 建立路由匹配缓存 -func (f *authFactory) Init() error -``` - -### 1.2 使用流程 - -```go -// 阶段1:程序初始化(全局变量/ init 函数) -package main - -// CRM 模块权限 -crmAuth := vbase.Auth.New("crm", vbase.AppConfig{ - Name: "客户关系管理", - DefaultRoles: []RoleDefinition{ - {Code: "admin", Name: "管理员", Policies: []string{"*:*"}}, - {Code: "sales", Name: "销售", Policies: []string{"customer:*", "order:read"}}, - }, -}) - -// ERP 模块权限 -erpAuth := vbase.Auth.New("erp", vbase.AppConfig{ - Name: "企业资源计划", - DefaultRoles: []RoleDefinition{ - {Code: "admin", Name: "管理员", Policies: []string{"*:*"}}, - {Code: "buyer", Name: "采购", Policies: []string{"supplier:read", "purchase:*"}}, - }, -}) - -func main() { - // 阶段2:程序启动时初始化 - if err := vbase.Auth.Init(); err != nil { - log.Fatal(err) - } - - // 阶段3:运行时使用 - // crmAuth.Perm(...) - // erpAuth.GrantRole(...) -} -``` - ---- - -## 2. AppConfig 配置 - -```go -type AppConfig struct { - Name string // 应用名称 - Description string // 应用描述 - DefaultRoles []RoleDefinition // 预设角色 -} - -type RoleDefinition struct { - Code string // 角色代码: admin/manager/sales/viewer - Name string // 角色名称 - Policies []string // 权限列表: ["customer:read", "customer:create", "*:*"] -} -``` - -### 多模块隔离示例 - -```go -// 模块1:CRM -crmAuth := vbase.Auth.New("crm", vbase.AppConfig{ - Name: "客户关系管理", - DefaultRoles: []vbase.RoleDefinition{ - {Code: "admin", Name: "管理员", Policies: []string{"customer:*", "contract:*"}}, - {Code: "sales", Name: "销售", Policies: []string{"customer:read", "customer:create", "customer:update"}}, - }, -}) - -// 模块2:ERP(资源名可以重复,通过 appKey 隔离) -erpAuth := vbase.Auth.New("erp", vbase.AppConfig{ - Name: "企业资源计划", - DefaultRoles: []vbase.RoleDefinition{ - {Code: "admin", Name: "管理员", Policies: []string{"customer:*", "supplier:*"}}, - // erp:customer:read 和 crm:customer:read 是两个不同权限 - }, -}) - -// 启动时统一检查 -crmAuth, erpAuth 会被 Init() 同时处理 -``` - ---- - -## 3. 接口权限配置 - -### 3.1 基础权限 - -```go -func main() { - // ... vbase.Auth.Init() ... - - api := r.SubRouter("/api/crm") - api.Use(middleware.AuthRequired()) - api.Use(middleware.OrgContext()) - - // 标准 CRUD - api.Get("/customers", crmAuth.Perm("customer", "list"), "客户列表", listHandler) - api.Post("/customers", crmAuth.Perm("customer", "create"), "创建客户", createHandler) - api.Get("/customers/{id}", crmAuth.Perm("customer", "read"), "客户详情", getHandler) - api.Patch("/customers/{id}", crmAuth.Perm("customer", "update"), "更新客户", updateHandler) - api.Delete("/customers/{id}", crmAuth.Perm("customer", "delete"), "删除客户", deleteHandler) - - // 自定义操作 - api.Post("/customers/{id}/export", crmAuth.Perm("customer", "export"), "导出客户", exportHandler) - api.Post("/customers/{id}/transfer", crmAuth.Perm("customer", "transfer"), "转交客户", transferHandler) - - // 通配权限(拥有 customer 任意操作即可) - api.Get("/customers/stats", crmAuth.Perm("customer", "*"), "客户统计", statsHandler) -} -``` - -### 3.2 资源所有者权限 - -```go -func getCustomer(x *vigo.X, req *GetReq) (*Customer, error) { - customer := query(req.ID) - x.Set("owner_id", customer.OwnerID) // 必须设置 owner_id - return customer, nil -} - -// 只有所有者或管理员能更新 -api.Patch("/customers/{id}", - crmAuth.PermWithOwner("customer", "update", "owner_id"), - "更新客户", - updateHandler, -) - -// 只有所有者能删除(管理员也不行) -api.Delete("/customers/{id}/danger", - crmAuth.PermWithOwner("customer", "delete", "owner_id"), - "危险删除", - dangerDeleteHandler, -) -``` - -### 3.3 组合权限 - -```go -// 满足任一权限即可 -api.Patch("/customers/{id}", - crmAuth.PermAny([][2]string{ - {"customer", "update"}, - {"customer", "admin"}, - }), - "更新客户", - updateHandler, -) - -// 必须同时满足所有权限 -api.Post("/customers/batch-import", - crmAuth.PermAll([][2]string{ - {"customer", "create"}, - {"customer", "batch"}, - }), - "批量导入", - batchImportHandler, -) -``` - ---- - -## 4. 编程式权限管理 - -开发者在自己的业务逻辑中调用这些方法管理权限。 - -### 4.1 授予角色 - -```go -func inviteMember(ctx context.Context, req InviteReq) error { - // 检查当前用户是否有邀请权限 - ok, _ := crmAuth.CheckPermission(ctx, CheckPermRequest{ - UserID: req.CurrentUserID, - OrgID: req.OrgID, - Resource: "org_member", - Action: "invite", - }) - if !ok { - return fmt.Errorf("无权邀请") - } - - // 创建用户 - user, _ := vbase.User.FindOrCreateByEmail(req.Email) - - // 加入组织并授予角色 - if err := crmAuth.GrantRole(ctx, GrantRoleRequest{ - UserID: user.ID, - OrgID: req.OrgID, - Role: req.Role, // "admin" / "sales" / "viewer" - }); err != nil { - return err - } - - return nil -} -``` - -### 4.2 授予特定资源权限 - -```go -// 场景:将客户转交给销售 B 跟进 -func transferCustomer(ctx context.Context, customerID, fromUserID, toUserID, orgID string) error { - // 1. 撤销 A 的权限 - if err := crmAuth.RevokeResourcePerm(ctx, fromUserID, orgID, "customer", customerID); err != nil { - return err - } - - // 2. 授予 B 权限 - if err := crmAuth.GrantResourcePerm(ctx, GrantResourcePermRequest{ - UserID: toUserID, - OrgID: orgID, - Resource: "customer", - ResourceID: customerID, - Action: "*", // 所有操作 - }); err != nil { - return err - } - - return nil -} - -// 场景:临时授权(3天后过期) -func tempGrant(ctx context.Context, userID, orgID string) error { - return crmAuth.GrantResourcePerm(ctx, GrantResourcePermRequest{ - UserID: userID, - OrgID: orgID, - Resource: "report", - ResourceID: "*", - Action: "export", - ExpireAt: time.Now().Add(3 * 24 * time.Hour), - }) -} -``` - -### 4.3 查询权限 - -```go -// 检查用户是否能操作某客户 -func canEditCustomer(ctx context.Context, userID, orgID, customerID string) bool { - ok, _ := crmAuth.CheckPermission(ctx, CheckPermRequest{ - UserID: userID, - OrgID: orgID, - Resource: "customer", - ResourceID: customerID, - Action: "update", - }) - return ok -} - -// 获取用户在组织的所有权限 -perms, _ := crmAuth.ListUserPermissions(ctx, "user-123", "org-456") -// 返回: -// [ -// {Resource: "customer", ResourceID: "*", Actions: ["read","list"]}, -// {Resource: "customer", ResourceID: "c-789", Actions: ["*"]}, -// {Resource: "order", ResourceID: "*", Actions: ["read"]} -// ] - -// 获取某客户的所有授权用户 -users, _ := crmAuth.ListResourceUsers(ctx, "org-456", "customer", "c-789") -// 返回: -// [ -// {UserID: "u1", Actions: ["admin"]}, -// {UserID: "u2", Actions: ["read","update"]} -// ] -``` - -### 4.4 撤销权限 - -```go -// 撤销角色 -crmAuth.RevokeRole(ctx, "user-123", "org-456", "sales") - -// 撤销特定资源权限 -crmAuth.RevokeResourcePerm(ctx, "user-123", "org-456", "customer", "c-789") - -// 撤销用户在组织的所有权限(离职) -crmAuth.RevokeAll(ctx, "user-123", "org-456") -``` - ---- - -## 5. 完整示例:多模块应用 - -```go -package main - -import ( - "github.com/veypi/vbase" - "github.com/veypi/vbase/api" - "github.com/veypi/vbase/api/middleware" - "github.com/veypi/vigo" -) - -// ========== 阶段1:定义权限(全局)========== - -// CRM 模块 -crmAuth := vbase.Auth.New("crm", vbase.AppConfig{ - Name: "客户关系管理", - DefaultRoles: []vbase.RoleDefinition{ - {Code: "admin", Name: "管理员", Policies: []string{"*:*"}}, - {Code: "manager", Name: "主管", Policies: []string{"customer:*", "contract:*", "report:*"}}, - {Code: "sales", Name: "销售", Policies: []string{"customer:read", "customer:create", "customer:update"}}, - {Code: "viewer", Name: "查看者", Policies: []string{"customer:read"}}, - }, -}) - -// 合同模块(独立,但共用 crm 用户体系) -contractAuth := vbase.Auth.New("contract", vbase.AppConfig{ - Name: "合同管理", - DefaultRoles: []vbase.RoleDefinition{ - {Code: "admin", Name: "管理员", Policies: []string{"*:*"}}, - {Code: "legal", Name: "法务", Policies: []string{"contract:read", "contract:audit", "contract:approve"}}, - {Code: "manager", Name: "业务经理", Policies: []string{"contract:read", "contract:create", "contract:sign"}}, - }, -}) - -func main() { - // ========== 阶段2:初始化 ========== - if err := vbase.Auth.Init(); err != nil { - log.Fatal("权限初始化失败:", err) - } - - r := vigo.NewRouter() - - // 挂载 VBase - r.Extend("/api/vb", api.Router) - - // ========== 阶段3:配置路由 ========== - - // CRM 路由 - crm := r.SubRouter("/api/crm") - crm.Use(middleware.AuthRequired()) - crm.Use(middleware.OrgContext()) - - crm.Get("/customers", crmAuth.Perm("customer", "list"), "客户列表", listCustomers) - crm.Post("/customers", crmAuth.Perm("customer", "create"), "创建客户", createCustomer) - crm.Get("/customers/{id}", crmAuth.Perm("customer", "read"), "客户详情", getCustomer) - crm.Patch("/customers/{id}", crmAuth.PermWithOwner("customer", "update", "owner_id"), "更新客户", updateCustomer) - crm.Delete("/customers/{id}", crmAuth.Perm("customer", "delete"), "删除客户", deleteCustomer) - - // 合同路由(独立模块) - contract := r.SubRouter("/api/contract") - contract.Use(middleware.AuthRequired()) - contract.Use(middleware.OrgContext()) - - contract.Get("/contracts", contractAuth.Perm("contract", "list"), "合同列表", listContracts) - contract.Post("/contracts", contractAuth.Perm("contract", "create"), "创建合同", createContract) - contract.Post("/contracts/{id}/audit", contractAuth.Perm("contract", "audit"), "审核合同", auditContract) - contract.Post("/contracts/{id}/sign", contractAuth.Perm("contract", "sign"), "签署合同", signContract) - - vigo.Run(r) -} -``` - ---- - -## 6. API 参考 - -### 6.1 全局函数 - -```go -// 创建权限管理实例 -func (f *authFactory) New(appKey string, config AppConfig) Auth - -// 初始化所有权限配置 -func (f *authFactory) Init() error -``` - -### 6.2 Auth 接口 - -```go -// ========== 中间件 ========== -Perm(resource, action string) func(*vigo.X) error -PermWithOwner(resource, action, ownerKey string) func(*vigo.X) error -PermAny(permissions [][2]string) func(*vigo.X) error -PermAll(permissions [][2]string) func(*vigo.X) error - -// ========== 授权管理 ========== -GrantRole(ctx context.Context, req GrantRoleRequest) error -RevokeRole(ctx context.Context, userID, orgID, roleCode string) error -GrantResourcePerm(ctx context.Context, req GrantResourcePermRequest) error -RevokeResourcePerm(ctx context.Context, userID, orgID, resource, resourceID string) error -RevokeAll(ctx context.Context, userID, orgID string) error - -// ========== 权限查询 ========== -CheckPermission(ctx context.Context, req CheckPermRequest) (bool, error) -ListUserPermissions(ctx context.Context, userID, orgID string) ([]UserPermission, error) -ListResourceUsers(ctx context.Context, orgID, resource, resourceID string) ([]ResourceUser, error) -``` - -### 6.3 请求/响应结构 - -```go -type GrantRoleRequest struct { - UserID string // 用户ID - OrgID string // 组织ID - Role string // 角色代码 -} - -type GrantResourcePermRequest struct { - UserID string // 用户ID - OrgID string // 组织ID - Resource string // 资源类型 - ResourceID string // 资源实例ID,"*" 表示所有 - Action string // 操作类型,"*" 表示所有 - ExpireAt time.Time // 过期时间(可选) -} - -type CheckPermRequest struct { - UserID string // 用户ID - OrgID string // 组织ID - Resource string // 资源类型 - ResourceID string // 资源实例ID(可选) - Action string // 操作类型 -} - -type UserPermission struct { - Resource string // 资源类型 - ResourceID string // 资源ID - Actions []string // 允许的操作 -} - -type ResourceUser struct { - UserID string // 用户ID - Actions []string // 允许的操作 -} -``` - ---- - -**确认此设计后,我将实现底层代码。** diff --git a/doc/integration.md b/doc/integration.md deleted file mode 100644 index 3a11732..0000000 --- a/doc/integration.md +++ /dev/null @@ -1,157 +0,0 @@ -# VBase 集成指南 - -## 1. 引入路由 - -```go -import "github.com/veypi/vbase/api" - -func main() { - // 挂载 vbase 路由到 /api/vb - rootRouter.Extend("/api/vb", api.Router) -} -``` - -## 2. 配置权限中间件 - -```go -import "github.com/veypi/vbase/api/middleware" - -// 全局启用路由权限检查(推荐) -router.Use(middleware.Perm("your-domain")) - -// 未配置的接口默认拒绝 -// 无权限时返回 X-Required-URL 响应头 -``` - -## 3. 注册路由权限 - -### 3.1 自动注册(推荐) - -```go -// 启动时自动扫描并注册所有路由 -middleware.InitVBaseRoutePolicies() - -// 首次启动后,在数据库中修改权限配置即可 -``` - -### 3.2 手动注册 - -```go -// 公开接口 -middleware.RegisterRoutePolicy("myapp", "/api", "/public", "GET", "public", "read", true) - -// 需要权限的接口 -middleware.RegisterRoutePolicy("myapp", "/api", "/users", "GET", "user", "list", false) -middleware.RegisterRoutePolicy("myapp", "/api", "/users/{id}", "PATCH", "user", "update", false) - -// 重新加载缓存 -middleware.ReloadRoutePolicies() -``` - -## 4. 数据库配置权限 - -### 4.1 路由权限表 (route_policies) - -| 字段 | 说明 | 示例 | -|------|------|------| -| domain | 领域标识 | "myapp" | -| prefix | 路由前缀 | "/api" | -| pattern | 路由模式 | "/users/{id}" | -| method | HTTP方法 | "GET" | -| resource | 资源类型 | "user" | -| action | 操作类型 | "read" | -| effect | 效果 | "allow"/"deny" | -| condition | 条件 | "owner"/"admin" | -| required_url | 申请地址 | "/apply-perm?r=user&a=read" | -| is_public | 是否公开 | true/false | - -### 4.2 用户权限表 (policies) - -```sql --- 用户拥有 user:read 权限 -INSERT INTO policies (code, resource, action, effect) -VALUES ('user-read', 'user', 'read', 'allow'); - --- 关联到角色 -INSERT INTO roles (code, name, policy_ids) -VALUES ('viewer', '查看者', 'policy-id-1,policy-id-2'); -``` - -## 5. 在 Handler 中设置资源所有者 - -```go -func getUser(x *vigo.X, req *GetUserReq) (*User, error) { - user := queryUser(req.ID) - - // 设置所有者到上下文,用于 condition=owner 检查 - x.Set("owner_id", user.ID) - - return user, nil -} -``` - -## 6. 完整示例 - -```go -package main - -import ( - "github.com/veypi/vbase/api" - "github.com/veypi/vbase/api/middleware" - "github.com/veypi/vigo" -) - -func main() { - r := vigo.NewRouter() - - // 1. 挂载 vbase - r.Extend("/api/vb", api.Router) - - // 2. 业务路由 - api := r.SubRouter("/api/v1") - - // 3. 启用权限检查 - api.Use(middleware.AuthRequired()) - api.Use(middleware.OrgContext()) - api.Use(middleware.Perm("myapp")) - - // 4. 注册权限(只需一次,后续在数据库中配置) - middleware.RegisterRoutePolicy("myapp", "/api/v1", "/projects", "GET", "project", "list", false) - middleware.RegisterRoutePolicy("myapp", "/api/v1", "/projects", "POST", "project", "create", false) - middleware.RegisterRoutePolicy("myapp", "/api/v1", "/projects/{id}", "GET", "project", "read", false) - middleware.RegisterRoutePolicy("myapp", "/api/v1", "/projects/{id}", "PATCH", "project", "update", false) - middleware.RegisterRoutePolicy("myapp", "/api/v1", "/projects/{id}", "DELETE", "project", "delete", false) - - // 5. 无需在 handler 中加权限代码 - api.Get("/projects", "项目列表", listProjects) - api.Post("/projects", "创建项目", createProject) - api.Get("/projects/{id}", "项目详情", getProject) - api.Patch("/projects/{id}", "更新项目", updateProject) - api.Delete("/projects/{id}", "删除项目", deleteProject) - - vigo.Run(r) -} -``` - -## 7. 权限检查流程 - -``` -请求 → AuthRequired(认证) → OrgContext(组织) → Perm(权限检查) → Handler - ↓ - 查找 route_policies - ↓ - 匹配 domain + method + path - ↓ - 检查 resource:action 权限 - ↓ - 无权限 → 返回 X-Required-URL -``` - -## 8. 配置热更新 - -```go -// 修改数据库后,调用 API 刷新缓存 -middleware.ReloadRoutePolicies() -``` - -无需重启服务,权限配置实时生效。 diff --git a/doc/permission.md b/doc/permission.md deleted file mode 100644 index b0f3a36..0000000 --- a/doc/permission.md +++ /dev/null @@ -1,998 +0,0 @@ -# VBase 权限系统文档 - -## 1. 权限模型概述 - -VBase 是一个类似 **Supabase** 的 BaaS 平台,为 2C(个人开发者)和 2B(团队/企业)用户提供后端资源托管服务。权限系统采用 **RBAC + ABAC** 混合模型,支持三级权限体系: - -- **平台级 (Platform)**: 用户管理自己的账号和数据 -- **项目级 (Project)**: 项目内的成员管理和资源控制 -- **资源级 (Resource)**: 具体资源的读写操作 - ---- - -## 2. 核心概念 - -### 2.1 权限层级 - -``` -┌─────────────────────────────────────────────────────────────┐ -│ 平台级权限 (Platform) │ -│ - 管理自己的用户信息 │ -│ - 创建新项目 │ -│ - 管理第三方账号绑定 │ -│ - 访问被邀请的项目 │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ (进入项目上下文) -┌─────────────────────────────────────────────────────────────┐ -│ 项目级权限 (Project) │ -│ - 项目设置管理 (所有者/管理员) │ -│ - 成员邀请和管理 │ -│ - 资源创建和管理 │ -│ - 查看项目统计信息 │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ (访问具体资源) -┌─────────────────────────────────────────────────────────────┐ -│ 资源级权限 (Resource) │ -│ - 数据库读写操作 │ -│ - 存储文件上传下载 │ -│ - 函数调用和管理 │ -│ - API 密钥管理 │ -└─────────────────────────────────────────────────────────────┘ -``` - -### 2.2 实体关系 - -``` -User (平台用户) - ├── Platform Permission (平台级权限) - │ └── 默认: 管理自己的数据 - │ - └── OrgMember (项目成员关系) - ├── Org/Project (项目) - │ ├── Resource (项目资源) - │ └── Resource (项目资源) - │ - └── Role (项目角色) - └── Policy (策略) -``` - -### 2.3 使用场景示例 - -**场景1: 个人开发者 (2C)** -``` -小明 (User) -├── 个人信息管理 (平台级) -├── 我的项目A (Org) -│ ├── 数据库资源 -│ └── 存储资源 -└── 我的项目B (Org) - └── ... -``` - -**场景2: 团队协作 (2B)** -``` -CEO (User) -├── 拥有的项目: WebApp, MobileApp -└── 角色: 所有者 (Owner) - -CTO (User) -├── 参与的项目: WebApp (Admin), MobileApp (Developer) -└── 角色: 管理员/开发者 - -员工A (User) -├── 参与的项目: WebApp (Developer) -└── 角色: 开发者 -``` - -### 2.2 策略 (Policy) - -策略是权限控制的最小单元,定义了对特定资源的操作权限。 - -**策略结构:** - -```go -type Policy struct { - Code string // 策略编码,唯一标识 - Name string // 策略名称 - Description string // 策略描述 - Resource string // 资源类型: user/org/member/policy/role/* - Action string // 操作: create/read/update/delete/* - Effect string // 效果: allow/deny - Condition string // 条件表达式 - Priority int // 优先级: 数字越大优先级越高 - IsSystem bool // 是否系统策略 -} -``` - -**策略分类:** - -**平台级策略** (无 org_id 上下文) -| 策略 | 资源 | 操作 | 效果 | 条件 | 说明 | -|------|------|------|------|------|------| -| user:read:own | user | read | allow | owner | 读取自己的用户信息 | -| user:update:own | user | update | allow | owner | 更新自己的用户信息 | -| binding:manage | binding | * | allow | - | 管理第三方账号绑定 | -| org:create | org | create | allow | - | 创建新项目 | - -**项目级策略** (需要 org_id 上下文) -| 策略 | 资源 | 操作 | 效果 | 条件 | 说明 | -|------|------|------|------|------|------| -| org:admin | * | * | allow | org_owner | 项目所有者拥有所有权限 | -| org:update | org | update | allow | admin | 更新项目设置 | -| member:read | member | read | allow | member | 读取项目成员列表 | -| member:manage | member | * | allow | admin | 管理项目成员 | -| resource:create | resource | create | allow | developer | 创建项目资源 | -| resource:delete | resource | delete | allow | admin | 删除项目资源 | - -### 2.4 角色 (Role) - -角色是一组策略的集合,在项目级别定义。 - -**角色结构:** - -```go -type Role struct { - ID string // 角色ID - OrgID string // 所属项目 - Name string // 角色名称 - Description string // 角色描述 - PolicyIDs []string // 关联的策略ID列表 - Scope string // 作用域: project/resource - IsSystem bool // 是否系统角色 -} -``` - -**系统内置角色:** - -| 角色 | 适用对象 | 权限范围 | 说明 | -|------|----------|----------|------| -| **owner** | 项目所有者 | 全部权限 | 项目的创建者,拥有所有权限,可删除项目 | -| **admin** | 管理员 | 项目级管理 | 管理项目设置、成员、资源,但不能删除项目 | -| **developer** | 开发者 | 资源级操作 | 创建和管理资源(数据库、存储、函数) | -| **viewer** | 访客 | 只读访问 | 只能查看项目和资源信息,不能修改 | - -### 2.4 条件 (Condition) - -条件用于实现 ABAC,支持以下类型: - -| 条件 | 说明 | 使用场景 | -|------|------|----------| -| "" | 无条件,始终匹配 | 通用权限 | -| "owner" | 资源所有者 | 只能操作自己的数据 | -| "org_member" | 项目成员 | 项目内数据访问 | -| "org_owner" | 项目所有者 | 项目所有者权限判断 | - ---- - -## 3. 权限检查流程 - -### 3.1 权限检查流程 - -根据是否有项目上下文 (`org_id`),权限检查分为两种方式: - -#### 流程 A: 平台级权限检查 (无项目上下文) - -``` -┌─────────────────────────────┐ -│ 请求进入 (无 org_id) │ -│ user, resource, action │ -└────────────┬────────────────┘ - │ - ▼ -┌─────────────────────────────┐ 命中 ┌─────────────────┐ -│ 检查缓存 │────────────→│ 返回缓存结果 │ -└────────────┬────────────────┘ └─────────────────┘ - │ 未命中 - ▼ -┌─────────────────────────────┐ -│ 1. 检查是否是资源所有者 │ -│ owner_id == user_id │ -└────────────┬────────────────┘ - │ 是 - ▼ - ┌──────────┐ - │ 返回 Allow│ - └──────────┘ - │ 否 - ▼ -┌─────────────────────────────┐ -│ 2. 加载平台级默认策略 │ -│ personal:* 策略 │ -└────────────┬────────────────┘ - │ - ▼ -┌─────────────────────────────┐ -│ 3. 评估策略 (Deny优先) │ -└────────────┬────────────────┘ - │ - ┌─────┴─────┐ - ▼ ▼ - ┌───────┐ ┌───────┐ - │ Deny │ │ Allow │ - └───────┘ └───────┘ -``` - -#### 流程 B: 项目级权限检查 (有项目上下文) - -``` -┌─────────────────────────────┐ -│ 请求进入 (有 org_id) │ -│ user, org_id, resource... │ -└────────────┬────────────────┘ - │ - ▼ -┌─────────────────────────────┐ 命中 ┌─────────────────┐ -│ 检查缓存 │────────────→│ 返回缓存结果 │ -└────────────┬────────────────┘ └─────────────────┘ - │ 未命中 - ▼ -┌─────────────────────────────┐ -│ 1. 检查是否是项目所有者 │ -│ org.owner_id == user_id │────────┐ -└────────────┬────────────────┘ │ - │ 是 │ - ▼ │ - ┌──────────┐ │ - │ 返回 Allow│───────────────────┘ - └──────────┘ - │ 否 - ▼ -┌─────────────────────────────┐ -│ 2. 获取用户在项目的角色 │ -│ 从 OrgMember 表查询 │ -└────────────┬────────────────┘ - │ - ▼ -┌─────────────────────────────┐ -│ 3. 获取角色关联的策略 │ -└────────────┬────────────────┘ - │ - ▼ -┌─────────────────────────────┐ -│ 4. 评估策略 (Deny优先) │ -│ Deny策略 → Allow策略 │ -└────────────┬────────────────┘ - │ - ┌─────┴─────┐ - ▼ ▼ - ┌───────┐ ┌───────┐ - │ Deny │ │ Allow │ - └───────┘ └───────┘ -``` - -### 3.2 决策规则 - -1. **Deny 优先**: 如果匹配到 Deny 策略,直接拒绝 -2. **显式 Allow**: 必须匹配到 Allow 策略才允许 -3. **默认拒绝**: 未匹配到任何策略时拒绝 -4. **优先级**: 高优先级策略优先匹配 - ---- - -## 4. 使用指南 - -### 4.1 平台级权限检查 - -**场景**:用户管理自己的账号、创建新项目 - -```go -// 检查用户是否可以更新自己的信息 -// orgID 传空字符串,表示平台级检查 -result, err := checker.Check(userID, "", "user", "update", map[string]any{ - "owner_id": userID, // 传入自己的ID -}) -// 结果:允许(满足 owner 条件) - -// 检查用户是否可以创建新项目 -result, err := checker.Check(userID, "", "org", "create", nil) -// 结果:允许(平台级默认策略允许创建项目) - -// 检查用户是否可以更新其他用户信息 -result, err := checker.Check(userID, "", "user", "update", map[string]any{ - "owner_id": otherUserID, // 传入他人ID -}) -// 结果:拒绝(不满足 owner 条件) -``` - -### 4.2 项目级权限检查 - -**场景**:在项目内操作资源 - -```go -// 检查用户在项目内是否有管理员权限 -result, err := checker.Check(userID, "project-123", "member", "invite", nil) -// 结果取决于用户在 project-123 中的角色 - -// 项目所有者检查(快速路径) -// 如果 userID == org.OwnerID,直接返回允许 - -// 普通成员检查 -// 1. 查询 OrgMember 获取角色 -// 2. 查询 Role 获取策略 -// 3. 评估策略 -``` - -### 4.3 完整示例:更新用户信息 - -```go -func UpdateUser(x *vigo.X, req *UpdateRequest) error { - userID := middleware.CurrentUser(x) - targetUserID := req.UserID - - checker := service.NewPermissionChecker() - - // 场景1: 更新自己的信息(平台级) - if targetUserID == userID { - result, err := checker.Check(userID, "", "user", "update", map[string]any{ - "owner_id": userID, - }) - if err != nil || !result.Allowed { - return vigo.ErrForbidden.WithString(result.Reason) - } - // 执行更新... - return nil - } - - // 场景2: 管理员更新其他用户信息(需要平台级管理员权限) - result, err := checker.Check(userID, "", "user", "update", nil) - if err != nil || !result.Allowed { - return vigo.ErrForbidden.WithString("only admin can update other users") - } - // 执行更新... - return nil -} -``` - -### 4.4 完整示例:项目内操作 - -```go -func DeleteProjectResource(x *vigo.X, req *DeleteResourceRequest) error { - userID := middleware.CurrentUser(x) - orgID := middleware.CurrentOrg(x) // 从请求头 X-Org-ID 获取 - - if orgID == "" { - return vigo.ErrArgInvalid.WithString("org_id required") - } - - checker := service.NewPermissionChecker() - - // 检查用户在项目内是否有删除资源的权限 - result, err := checker.Check(userID, orgID, "resource", "delete", map[string]any{ - "resource_id": req.ResourceID, - }) - - if err != nil || !result.Allowed { - return vigo.ErrForbidden.WithString(result.Reason) - } - - // 执行删除... - return nil -} -``` - -### 4.5 创建自定义策略 - -```go -// 项目级策略(需要关联到具体项目) -policy := &model.Policy{ - Code: "custom:db:admin", - Name: "数据库管理员", - Description: "管理项目数据库", - Resource: "database", - Action: "*", // 所有操作 - Effect: model.EffectAllow, - Condition: "", // 无条件限制 - Scope: "project", // 项目级策略 - OrgID: "project-123", // 所属项目 -} -cfg.DB().Create(policy) - -// 将策略添加到角色 -role := &models.Role{ - OrgID: "project-123", - Name: "DBAdmin", - PolicyIDs: "custom:db:admin", -} -cfg.DB().Create(role) - -// 分配角色给用户 -member := &models.OrgMember{ - OrgID: "project-123", - UserID: "user-456", - RoleIDs: role.ID, -} -cfg.DB().Create(member) -``` - ---- - -## 5. 系统内置策略 - -### 5.1 用户相关 - -```go -// 读取自己 -SysPolicyUserReadOwn = "sys:user:read:own" -Resource: "user" -Action: "read" -Condition: "owner" - -// 更新自己 -SysPolicyUserUpdateOwn = "sys:user:update:own" -Resource: "user" -Action: "update" -Condition: "owner" - -// 删除自己 -SysPolicyUserDeleteOwn = "sys:user:delete:own" -Resource: "user" -Action: "delete" -Condition: "owner" -``` - -### 5.2 组织相关 - -```go -// 组织管理员 -SysPolicyOrgAdmin = "sys:org:admin" -Resource: "*" -Action: "*" -Condition: "org_owner" -Priority: 100 - -// 读取组织 -SysPolicyOrgRead = "sys:org:read" -Resource: "org" -Action: "read" -Condition: "org_member" -``` - -### 5.3 成员相关 - -```go -// 读取成员 -SysPolicyMemberRead = "sys:member:read" -Resource: "member" -Action: "read" -Condition: "org_member" - -// 管理成员 -SysPolicyMemberManage = "sys:member:manage" -Resource: "member" -Action: "*" -Condition: "" -``` - -### 5.4 角色策略相关 - -```go -// 读取角色 -SysPolicyRoleRead = "sys:role:read" -Resource: "role" -Action: "read" -Condition: "org_member" - -// 管理角色 -SysPolicyRoleManage = "sys:role:manage" -Resource: "role" -Action: "*" -Condition: "admin" - -// 读取策略 -SysPolicyPolicyRead = "sys:policy:read" -Resource: "policy" -Action: "read" -Condition: "org_member" - -// 管理策略 -SysPolicyPolicyManage = "sys:policy:manage" -Resource: "policy" -Action: "*" -Condition: "admin" -``` - -### 5.5 资源级策略(BaaS 核心资源) - -**数据库资源 (database)** - -```go -// 查询数据库 -SysPolicyDBRead = "sys:db:read" -Resource: "database" -Action: "read" -Condition: "org_member" - -// 管理数据库(创建表、修改结构) -SysPolicyDBManage = "sys:db:manage" -Resource: "database" -Action: "*" -Condition: "developer" -``` - -**存储资源 (storage)** - -```go -// 读取存储文件 -SysPolicyStorageRead = "sys:storage:read" -Resource: "storage" -Action: "read" -Condition: "org_member" - -// 上传/删除文件 -SysPolicyStorageWrite = "sys:storage:write" -Resource: "storage" -Action: "create,update,delete" -Condition: "developer" - -// 管理存储桶 -SysPolicyStorageAdmin = "sys:storage:admin" -Resource: "storage" -Action: "*" -Condition: "admin" -``` - -**函数资源 (function)** - -```go -// 调用函数 -SysPolicyFunctionCall = "sys:function:call" -Resource: "function" -Action: "call" -Condition: "org_member" - -// 管理函数(部署、删除) -SysPolicyFunctionManage = "sys:function:manage" -Resource: "function" -Action: "create,update,delete" -Condition: "developer" -``` - -**API 密钥管理 (apikey)** - -```go -// 读取 API 密钥(只能读取自己创建的密钥) -SysPolicyAPIKeyRead = "sys:apikey:read" -Resource: "apikey" -Action: "read" -Condition: "owner" - -// 管理 API 密钥 -SysPolicyAPIKeyManage = "sys:apikey:manage" -Resource: "apikey" -Action: "*" -Condition: "developer" -``` - -### 5.6 平台超级管理员策略 - -```go -// 超级管理员 - 拥有所有权限 -SysPolicySuperAdmin = "sys:super:admin" -Resource: "*" -Action: "*" -Condition: "is_super_admin" -Priority: 999 -``` - ---- - -## 6. 权限缓存 - -### 6.1 缓存机制 - -``` -缓存键: perm:{user_id}:{org_id}:{resource}:{action} -TTL: 1分钟 -值: "allow" 或 "deny" -``` - -### 6.2 缓存失效 - -以下操作会触发缓存清除: - -- 用户角色变更 -- 角色策略变更 -- 策略内容变更 -- 组织成员变更 - -```go -// 清除用户权限缓存 -service.ClearUserPermissionCache(userID) - -// 清除组织权限缓存 -service.ClearOrgPermissionCache(orgID) -``` - -### 6.3 缓存命中率优化 - -- 权限变更频率低,1分钟 TTL 平衡了性能和实时性 -- 批量操作时建议先清除缓存,操作完成后统一刷新 - ---- - -## 7. 最佳实践 - -### 7.1 策略设计 - -1. **最小权限原则**: 只授予完成任务所需的最小权限 -2. **职责分离**: 敏感操作需要多个角色共同完成 -3. **策略命名规范**: `{resource}:{action}:{condition}` - -### 7.2 BaaS 平台角色设计 - -VBase 作为 Supabase-like BaaS 平台,角色设计覆盖 **2C个人开发者** 和 **2B团队/企业** 场景: - -#### 2C 个人开发者场景 - -``` -个人项目角色体系: -├── owner (项目所有者) -│ └── 全部权限(可以删除项目) -└── developer (开发者) - └── 读写资源、调用 API -``` - -个人开发者通常自己就是项目所有者,不需要复杂的角色体系。 - -#### 2B 团队协作场景 - -``` -团队项目角色体系: -├── owner (所有者) -│ └── 全部权限、可删除项目、管理账单 -├── admin (管理员) -│ └── 管理成员、角色、资源设置 -├── developer (开发者) -│ ├── 数据库: 创建表、查询、修改结构 -│ ├── 存储: 上传/下载文件、管理存储桶 -│ ├── 函数: 部署、调用、删除函数 -│ └── API密钥: 创建、管理 -├── viewer (访客/只读) -│ └── 查看数据、只读访问 -└── auditor (审计员 - 可选) - └── 查看日志、审计记录 -``` - -#### 角色权限对照表 - -| 角色 | 成员管理 | 资源管理 | 数据读写 | API密钥 | 项目设置 | -|------|----------|----------|----------|---------|----------| -| **owner** | ✓ | ✓ | ✓ | ✓ | ✓ | -| **admin** | ✓ | ✓ | ✓ | ✓ | ✗ | -| **developer** | ✗ | ✓ | ✓ | ✓ | ✗ | -| **viewer** | ✗ | ✗ | 只读 | ✗ | ✗ | - -#### 系统角色配置示例 - -```go -// Owner 角色 - 绑定所有权限 -var RoleOwner = model.Role{ - Name: "owner", - Description: "项目所有者", - PolicyIDs: "sys:org:admin,sys:member:manage,sys:role:manage,sys:policy:manage", - IsSystem: true, -} - -// Admin 角色 - 管理权限 -var RoleAdmin = model.Role{ - Name: "admin", - Description: "项目管理员", - PolicyIDs: "sys:member:manage,sys:role:read,sys:db:manage,sys:storage:admin,sys:function:manage", - IsSystem: true, -} - -// Developer 角色 - 开发权限 -var RoleDeveloper = model.Role{ - Name: "developer", - Description: "开发者", - PolicyIDs: "sys:db:manage,sys:storage:write,sys:function:manage,sys:apikey:manage", - IsSystem: true, -} - -// Viewer 角色 - 只读权限 -var RoleViewer = model.Role{ - Name: "viewer", - Description: "访客", - PolicyIDs: "sys:org:read,sys:db:read,sys:storage:read,sys:function:call", - IsSystem: true, -} -``` - -### 7.3 权限检查位置 - -**Controller 层检查:** -```go -func Handler(x *vigo.X) error { - // 先检查权限 - if !hasPermission(x, "resource", "action") { - return vigo.ErrForbidden - } - // 执行业务逻辑 -} -``` - -**Service 层检查(数据级):** -```go -func UpdateUser(userID string, data map[string]any) error { - // 检查是否是本人或管理员 - if !isOwner(userID) && !hasAdminRole() { - return ErrForbidden - } - // 执行更新 -} -``` - -### 7.4 BaaS 平台权限场景示例 - -**场景1: 个人开发者创建项目** - -```go -// 小明是个人开发者,注册后创建项目 -// 1. 创建项目(平台级权限检查) -result, _ := checker.Check("xiaoming_user_id", "", "org", "create", nil) -// 结果: 允许(personal:org:create 策略) - -// 2. 小明成为项目所有者 -// org.owner_id = "xiaoming_user_id" - -// 3. 小明在项目内操作数据库(项目级权限) -result, _ = checker.Check("xiaoming_user_id", "my-project", "database", "create", nil) -// 结果: 允许(小明是项目所有者) -``` - -**场景2: 团队协作开发** - -```go -// CEO 创建 WebApp 项目,邀请 CTO 为管理员,员工为开发者 -// - CEO: owner -// - CTO: admin -// - 员工A: developer -// - 外包人员: viewer - -// CTO 邀请成员(有 member:manage 权限) -result, _ := checker.Check("cto_user_id", "webapp-project", "member", "create", nil) -// 结果: 允许(admin 角色有 member:manage 策略) - -// 员工A 创建数据库表(有 db:manage 权限) -result, _ = checker.Check("employee_a_id", "webapp-project", "database", "create", nil) -// 结果: 允许(developer 角色有 db:manage 策略) - -// 外包人员 尝试删除表(viewer 没有 delete 权限) -result, _ = checker.Check("contractor_id", "webapp-project", "database", "delete", nil) -// 结果: 拒绝(viewer 只有 db:read 权限) -``` - -**场景3: API 密钥访问控制** - -```go -// 开发者创建 API 密钥用于后端服务 -// 只能查看自己创建的密钥 - -result, _ := checker.Check("developer_id", "project-123", "apikey", "read", map[string]any{ - "owner_id": "developer_id", -}) -// 结果: 允许(满足 owner 条件) - -// 尝试查看他人创建的密钥 -result, _ = checker.Check("developer_id", "project-123", "apikey", "read", map[string]any{ - "owner_id": "other_developer_id", -}) -// 结果: 拒绝(不满足 owner 条件) -``` - -**场景4: 跨项目资源访问** - -```go -// 用户小明在 project-A 是开发者,在 project-B 是访客 - -// 在 project-A 上传文件 -result, _ := checker.Check("xiaoming_id", "project-A", "storage", "create", nil) -// 结果: 允许(developer 角色有 storage:write 权限) - -// 在 project-B 上传文件 -result, _ = checker.Check("xiaoming_id", "project-B", "storage", "create", nil) -// 结果: 拒绝(viewer 角色只有 storage:read 权限) -``` - -### 7.5 权限测试 - -```go -func TestPermissionCheck(t *testing.T) { - tests := []struct { - name string - userID string - orgID string - resource string - action string - data map[string]any - want bool - }{ - { - name: "owner can update own profile", - userID: "user1", - orgID: "", - resource: "user", - action: "update", - data: map[string]any{"owner_id": "user1"}, - want: true, - }, - { - name: "user cannot update others profile", - userID: "user1", - orgID: "", - resource: "user", - action: "update", - data: map[string]any{"owner_id": "user2"}, - want: false, - }, - } - - checker := service.NewPermissionChecker() - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result, _ := checker.Check(tt.userID, tt.orgID, tt.resource, tt.action, tt.data) - if result.Allowed != tt.want { - t.Errorf("Check() = %v, want %v", result.Allowed, tt.want) - } - }) - } -} -``` - ---- - -## 8. 故障排查 - -### 8.1 权限检查失败 - -**检查步骤:** - -**平台级权限检查失败(org_id 为空):** - -1. 确认用户已登录 -2. 确认操作的是自己的资源(检查 owner_id 条件) -3. 检查用户是否有平台级管理员权限 -4. 检查默认个人策略是否生效 - -**项目级权限检查失败(有 org_id):** - -1. 确认用户是项目成员(OrgMember 状态为 active) -2. 检查用户角色是否正确分配 -3. 检查角色绑定的策略 -4. 检查策略的 Effect 和 Condition -5. 确认资源所属的项目是否正确 - -**调试日志:** - -```go -// 开启调试日志 -checker := service.NewPermissionChecker() -result, err := checker.Check(userID, orgID, resource, action, data) -log.Printf("Permission check: user=%s, resource=%s, action=%s, allowed=%v, reason=%s", - userID, resource, action, result.Allowed, result.Reason) -``` - -### 8.2 缓存问题 - -```bash -# 清除 Redis 权限缓存 -redis-cli KEYS "perm:*" | xargs redis-cli DEL - -# 或重启应用(开发环境) -``` - -### 8.3 权限不生效 - -1. 检查策略是否保存成功 -2. 检查角色是否正确绑定策略 -3. 检查用户是否正确分配角色 -4. 检查缓存是否已清除 - ---- - -## 9. 扩展开发 - -### 9.1 添加自定义条件 - -```go -// 在 permission.go 中扩展 evaluateCondition -func (pc *PermissionChecker) evaluateCondition(p *model.Policy, userID, orgID string, resourceData map[string]any) bool { - switch p.Condition { - case "owner": - // 现有逻辑... - case "custom_condition": - // 自定义条件逻辑 - return checkCustomCondition(userID, orgID, resourceData) - default: - return true - } -} -``` - -### 9.2 添加 BaaS 资源类型 - -**步骤1: 定义资源常量** - -```go -// 在 policy.go 中添加资源常量 -const ( - ResourceUser = "user" - ResourceOrg = "org" - ResourceMember = "member" - ResourceDatabase = "database" - ResourceStorage = "storage" - ResourceFunction = "function" - ResourceAPIKey = "apikey" - ResourceCustom = "custom_resource" // 新增资源类型 -) -``` - -**步骤2: 创建资源级策略** - -```go -// 为新增资源创建策略 -func (pc *PermissionChecker) getCustomResourcePolicies() []model.Policy { - return []model.Policy{ - { - Code: "custom:read", - Name: "读取自定义资源", - Resource: ResourceCustom, - Action: "read", - Effect: model.EffectAllow, - Condition: "org_member", - }, - { - Code: "custom:manage", - Name: "管理自定义资源", - Resource: ResourceCustom, - Action: "create,update,delete", - Effect: model.EffectAllow, - Condition: "developer", - }, - } -} -``` - -**步骤3: 在角色中使用** - -```go -// 创建新角色使用自定义策略 -customRole := &model.Role{ - OrgID: orgID, - Name: "custom_manager", - Description: "自定义资源管理员", - PolicyIDs: "custom:read,custom:manage", - Scope: "resource", -} -``` - -### 9.3 多租户资源隔离 - -在 BaaS 平台中,资源需要严格的项目隔离: - -```go -// 查询时自动添加 org_id 过滤 -func ListResources(userID, orgID string) ([]Resource, error) { - // 检查用户在项目中的权限 - checker := service.NewPermissionChecker() - result, err := checker.Check(userID, orgID, "custom_resource", "read", nil) - if err != nil || !result.Allowed { - return nil, vigo.ErrForbidden - } - - // 查询该项目的资源(自动隔离) - var resources []Resource - err = cfg.DB().Where("org_id = ?", orgID).Find(&resources).Error - return resources, err -} -``` - ---- - -## 10. 参考 - -- [RBAC 模型](https://en.wikipedia.org/wiki/Role-based_access_control) -- [ABAC 模型](https://en.wikipedia.org/wiki/Attribute-based_access_control) -- [NIST RBAC 标准](https://csrc.nist.gov/projects/role-based-access-control) diff --git a/doc/permission_design.md b/doc/permission_design.md deleted file mode 100644 index 6f6411f..0000000 --- a/doc/permission_design.md +++ /dev/null @@ -1,285 +0,0 @@ -# VBase 权限模型设计文档 - -## 核心原则 - -1. **Policy(策略)**:定义"什么权限可以访问哪些接口" -2. **Role(角色)**:Policy 的集合,简化授权 -3. **授权关系**:用户通过 Role 或直接与 Policy 关联,并携带数据范围 - ---- - -## 表结构关系 - -``` -┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ -│ Policy │◄────┤ PolicyRoute │ │ Role │ -│ (策略定义) │ │ (策略路由绑定) │ │ (角色定义) │ -└────────┬────────┘ └──────────────────┘ └────────┬────────┘ - │ │ - │ Many-to-Many │ Many-to-Many - │ │ - ▼ ▼ -┌─────────────────────────────────────────────────────────────────┐ -│ UserPermission (用户授权) │ -│ ├─ user_id: 用户ID │ -│ ├─ policy_code: 策略代码 (或直接关联 Policy) │ -│ ├─ role_id: 角色ID (可选,与 policy_code 二选一) │ -│ ├─ resource: 资源类型 (project/org/...) │ -│ ├─ resource_id: 具体资源ID (proj-123, * 表示所有) │ -│ ├─ scope: 权限范围 (owner/admin/member) │ -│ └─ expire_at: 过期时间 (可选) │ -└─────────────────────────────────────────────────────────────────┘ -``` - ---- - -## 详细表设计 - -### 1. Policy(策略表) - -定义权限的本质:对某资源可以执行什么操作 - -| 字段 | 类型 | 说明 | -|------|------|------| -| id | string | UUID | -| code | string | **唯一标识**,如 `project:read`, `project:write`, `project:admin` | -| resource | string | 资源类型:`user`, `org`, `project`, `*`(所有) | -| action | string | 操作类型:`create`, `read`, `update`, `delete`, `list`, `*`, `admin` | -| effect | string | `allow` / `deny` | -| scope | string | 作用域:`platform`(平台级) / `org`(组织级) | -| description | string | 描述 | - -**示例数据:** - -```sql --- 项目相关权限 -('p1', 'project:create', 'project', 'create', 'allow', 'org', '创建项目') -('p2', 'project:read', 'project', 'read', 'allow', 'org', '查看项目') -('p3', 'project:write', 'project', 'write', 'allow', 'org', '编辑项目(包含read)') -('p4', 'project:delete', 'project', 'delete', 'allow', 'org', '删除项目') -('p5', 'project:admin', 'project', 'admin', 'allow', 'org', '项目管理员(包含所有)') - --- 通配权限 -('p9', 'project:*', 'project', '*', 'allow', 'org', '所有项目权限') -('p0', '*:*', '*', '*', 'allow', 'platform', '超级管理员') -``` - -**层级关系:** -``` -project:admin > project:write > project:read - │ │ └─ project:create, project:delete - └──────────────┴────────────────────────────────────────────── -``` - ---- - -### 2. PolicyRoute(策略路由绑定表) - -Policy 与具体接口的绑定关系。一个 Policy 可以绑定多个路由。 - -| 字段 | 类型 | 说明 | -|------|------|------| -| id | string | UUID | -| policy_id | string | 关联 Policy.id | -| domain | string | 应用域,如 `vbase`, `myapp` | -| prefix | string | 路由前缀,如 `/api/v1` | -| pattern | string | 路由模式,如 `/projects`, `/projects/{id}` | -| method | string | HTTP 方法:`GET`, `POST`, `*`, ... | -| resource_id_param | string | 从 URL 提取资源ID的参数名,如 `id` | -| description | string | 接口描述 | - -**示例数据:** - -```sql --- project:read 权限可以访问以下接口 -('r1', 'p2', 'myapp', '/api/v1', '/projects', 'GET', '', '项目列表') -('r2', 'p2', 'myapp', '/api/v1', '/projects/{id}', 'GET', 'id', '项目详情') -('r3', 'p2', 'myapp', '/api/v1', '/projects/{id}/logs', 'GET', 'id', '项目日志') - --- project:write 权限 -('r4', 'p3', 'myapp', '/api/v1', '/projects/{id}', 'PATCH', 'id', '更新项目') -('r5', 'p3', 'myapp', '/api/v1', '/projects/{id}/env', 'PUT', 'id', '更新环境变量') - --- project:create 权限 -('r6', 'p1', 'myapp', '/api/v1', '/projects', 'POST', '', '创建项目') - --- project:admin 权限(包含所有) -('r7', 'p5', 'myapp', '/api/v1', '/projects/{id}/members', 'GET', 'id', '成员管理') -('r8', 'p5', 'myapp', '/api/v1', '/projects/{id}/settings', '*', 'id', '设置管理') -``` - ---- - -### 3. Role(角色表) - -Policy 的集合,用于批量授权。 - -| 字段 | 类型 | 说明 | -|------|------|------| -| id | string | UUID | -| org_id | string | 组织ID(系统角色为空) | -| code | string | 角色代码:`admin`, `developer`, `viewer`, `owner` | -| name | string | 角色名称 | -| policy_codes | string | 关联的 Policy code 列表,逗号分隔 | -| is_system | bool | 是否系统预设角色 | - -**示例数据:** - -```sql --- 系统预设角色 -('r1', '', 'owner', '所有者', 'project:admin,org:admin,*:*', true) -('r2', '', 'admin', '管理员', 'project:write,org:read,user:read', true) -('r3', '', 'developer', '开发者', 'project:write,resource:read', true) -('r4', '', 'viewer', '只读用户', 'project:read', true) - --- 组织自定义角色 -('r5', 'org-123', 'pm', '项目经理', 'project:admin', false) -``` - ---- - -### 4. UserPermission(用户授权表) - -核心授权表,记录"谁对什么资源有什么权限"。 - -| 字段 | 类型 | 说明 | -|------|------|------| -| id | string | UUID | -| user_id | string | 用户ID | -| org_id | string | 组织ID(可选) | -| **policy_code** | string | **策略代码**(如 `project:read`) | -| **resource** | string | **资源类型**(如 `project`) | -| **resource_id** | string | **具体资源ID**(如 `proj-123` 或 `*`) | -| granted_by | string | 授权来源:`role`(通过角色) / `direct`(直接授权) | -| source_id | string | 来源ID(Role ID 或留空) | -| expire_at | timestamp | 过期时间(可选,永久有效为空) | -| created_at | timestamp | 创建时间 | - -**关键设计:** -- `resource_id = *` 表示对该类型所有资源的权限 -- 数据级权限通过具体的 `resource_id` 实现 -- 用户最终权限 = 直接授权 + 角色授权 的并集 - -**示例数据:** - -```sql --- 场景:用户 A --- 1. 是项目 1 的 admin(直接授权) -('up1', 'user-a', 'org-1', 'project:admin', 'project', 'proj-1', 'direct', '', null) - --- 2. 是项目 2 的只读成员(直接授权) -('up2', 'user-a', 'org-1', 'project:read', 'project', 'proj-2', 'direct', '', null) - --- 3. 通过 developer 角色获得对所有项目的 write 权限 -('up3', 'user-a', 'org-1', 'project:write', 'project', '*', 'role', 'role-dev-id', null) - --- 4. 临时授权,3天后过期 -('up4', 'user-a', 'org-1', 'project:admin', 'project', 'proj-3', 'direct', '', '2025-02-20 00:00:00') -``` - ---- - -## 权限检查流程 - -``` -请求: GET /api/v1/projects/proj-123/settings - Header: X-Org-ID: org-1 - - │ - ▼ -┌─────────────────────────────────────┐ -│ 1. 路由匹配 │ -│ domain=myapp + method=GET + │ -│ pattern=/projects/{id}/settings │ -│ │ -│ 找到: PolicyRoute.r8 │ -│ 对应: Policy.p5 (project:admin) │ -│ 提取: resource_id = "proj-123" │ -└─────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────┐ -│ 2. 获取用户权限列表 │ -│ SELECT * FROM user_permissions │ -│ WHERE user_id = 'user-a' │ -│ AND resource = 'project' │ -│ AND (resource_id = 'proj-123' │ -│ OR resource_id = '*') │ -└─────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────┐ -│ 3. 权限层级匹配 │ -│ 需要的权限: project:admin │ -│ │ -│ 用户有的权限: │ -│ - project:admin (proj-1) ❌ │ -│ - project:read (proj-2) ❌ │ -│ - project:write (*) ❌ │ -│ (write < admin) │ -│ │ -│ 结果: 拒绝 ❌ │ -└─────────────────────────────────────┘ - │ - ▼ -拒绝,返回 X-Required-URL: /apply-perm?resource=project&action=admin&id=proj-123 -``` - ---- - -## 场景覆盖 - -### 场景1:用户是项目 owner,拥有所有权限 -```sql -INSERT INTO user_permissions (user_id, policy_code, resource, resource_id) -VALUES ('user-a', 'project:admin', 'project', 'proj-1') --- 自动包含 project:read/write/delete/create -``` - -### 场景2:平台管理员,拥有所有资源的所有权限 -```sql --- 方案一:通配符 -INSERT INTO user_permissions (user_id, policy_code, resource, resource_id) -VALUES ('admin', '*:*', '*', '*') - --- 方案二:绑定 owner 角色 --- Role.owner 包含 policy_codes: '*:*' -``` - -### 场景3:组织管理员,拥有组织内所有项目权限 -```sql --- 组织 admin 角色包含 project:* --- 在组织上下文内生效 -INSERT INTO user_permissions (user_id, org_id, policy_code, resource, resource_id, granted_by) -VALUES ('user-a', 'org-1', 'project:*', 'project', '*', 'role') -``` - -### 场景4:用户加入多个项目,权限不同 -```sql --- 项目1: admin -INSERT INTO user_permissions VALUES ('u1', 'project:admin', 'project', 'proj-1', ...) - --- 项目2: read -INSERT INTO user_permissions VALUES ('u1', 'project:read', 'project', 'proj-2', ...) - --- 项目3: write -INSERT INTO user_permissions VALUES ('u1', 'project:write', 'project', 'proj-3', ...) -``` - ---- - -## 与旧方案对比 - -| 维度 | 旧方案 (RoutePolicy) | 新方案 (Policy + UserPermission) | -|------|---------------------|--------------------------------| -| 核心思想 | 接口白名单 | 资源操作权限 | -| 配置粒度 | 每个接口单独配置 | 按 Resource + Action 配置 | -| 数据级权限 | 难实现 | 通过 resource_id 字段 | -| 通配授权 | 不支持 | 支持 `*` | -| 层级权限 | 无 | admin > write > read | -| 新增接口成本 | 高(需给所有角色加策略) | 低(绑定到 Policy 即可)| -| 角色数量 | 多(每项目配角色) | 少(复用系统角色 + 数据范围)| - ---- - -请确认此设计后,我开始修改代码。 diff --git a/doc/tsdb.md b/doc/tsdb.md deleted file mode 100644 index 5158c6c..0000000 --- a/doc/tsdb.md +++ /dev/null @@ -1,26 +0,0 @@ -# TSBD - -```bash - -docker run -dit --name=tsdb -v /Users/veypi/test/vdb:/victoria-metrics-data -p 8428:8428 victoriametrics/victoria-metrics -search.latencyOffset=1s -retentionPeriod=10y -search.cacheTimestampOffset=1h - - -# 查询所有数据 -curl http://localhost:8428/prometheus/api/v1/series -d 'match[]={__name__=~".*"}' - -# 删除所有数据 -curl http://localhost:8428/prometheus/api/v1/series\?start=1593100800\&end=1719308095\&step=2d22h40m50s -d 'match[]={__name__=~".*"}' -curl http://localhost:8428/api/v1/admin/tsdb/delete_series -d 'match[]={__name__=~".*"}' - -# 查询所有标签 -curl http://localhost:8428/prometheus/api/v1/labels - -# 插入数据 -curl -X POST http://localhost:8428/api/v1/import/prometheus -d 'abd{foo="bar",a="15"} 25' -curl -X POST http://localhost:8428/api/v1/import -d '{"metric":{"__name__":"foo","job":"node_exporter"},"values":[4],"timestamps":[1719819673000]}' - -curl -G 'http://localhost:8428/api/v1/export' -d 'match={__name__="abd"}' -curl -G 'http://localhost:8428/api/v1/export' -d 'match[]={__name__="abd"}' - - -``` diff --git a/docs/integration.md b/docs/integration.md new file mode 100644 index 0000000..ec538f2 --- /dev/null +++ b/docs/integration.md @@ -0,0 +1,160 @@ +# VBase 集成文档 + +本文档介绍如何将 VBase 集成到您的 Golang (Vigo) 项目中,快速实现用户管理、权限控制和组织架构功能。 + +## 1. 快速集成 (Quick Start) + +集成 VBase 仅需简单几步: + +### 1.1 引入依赖 + +在您的项目中引入 VBase: + +```go +import "github.com/veypi/vbase" +``` + +### 1.2 挂载路由 + +在您的项目根路由初始化处,挂载 VBase 路由: + +```go +// 您的项目初始化代码 +func init() { + // 挂载 vbase 路由到 /vbase 路径下 + // 这样 vbase 的 API 将可以通过 /vb/api/auth/login 等访问 + MyRouter.Extend("/vb", vbase.Router) +} +``` + +### 1.3 配置数据库 + +确保您的项目已经配置了 GORM 数据库连接,VBase 将复用该连接。 + +```go +// VBase 会自动读取您的配置,前提是您使用了兼容的配置结构或手动设置 +vbase.Config.SetDB(yourDB) +// 或者直接集成到您的项目配置中,由vigo.Flags 自动加载 +type AppConfig struct { + Vbase any +} +var Config = *AppConfig{ + Vbase: vbase.Config, +} +``` + +--- + +## 2. 详细集成方案 + +### 2.1 路由集成 + +VBase 提供了一个标准的 `vigo.Router` 实例,包含所有用户、组织、权限相关的 API。 + +```go +package main + +import ( + "github.com/veypi/vbase" + "github.com/veypi/vigo" +) + +var Router = vigo.NewRouter() + +func init() { + // 1. 挂载 VBase 路由 + // 建议挂载在独立的前缀下,避免路由冲突 + Router.Extend("/vb", vbase.Router) +} +``` + +### 2.2 权限系统集成 + +VBase 提供了强大的 RBAC (基于角色的访问控制) 权限系统。 + +#### 2.2.1 初始化应用权限 + +在您的应用中,使用 `vbase.Auth.New` 创建应用专属的权限实例。 + +```go +import ( + "github.com/veypi/vbase" + "github.com/veypi/vbase/models" +) + +// 定义您的应用权限实例 +var AppAuth = vbase.Auth.New("my_app", models.AppConfig{ + Name: "My Application", + Description: "我的应用描述", + // 定义应用的默认角色 + DefaultRoles: []models.RoleDefinition{ + { + Code: "admin", + Name: "管理员", + Policies: []string{"*:*"}, // 拥有所有权限 + }, + { + Code: "editor", + Name: "编辑", + Policies: []string{"article:create", "article:update"}, + }, + }, +}) +``` + +#### 2.2.2 使用权限中间件 + +使用 `vbase.AuthMiddleware` 进行认证,并结合 `AppAuth.Perm` 进行权限检查。 + +```go +func init() { + // 1. 全局认证中间件 (解析 Token, 注入 user_id, org_id) + Router.Use(vbase.AuthMiddleware()) + Router.Get("/info", "获取app信息", vigo.SkipBefore, getInfo) + // 2. 路由权限控制 + // 检查 "article:create" 权限 + Router.Post("/articles", "发布文章", AppAuth.Perm("article:create"), createArticle) + + // 检查资源所有权 (确保用户只能修改自己的文章) + // 检查 "article:update" 权限,且文章的 owner_id 必须等于当前用户 ID + Router.Patch("/articles/{id}", "更新文章", AppAuth.PermWithOwner("article:update", "id"), updateArticle) +} +``` + +### 2.3 配置管理 + +VBase 允许自定义部分配置。 + +```go +import "github.com/veypi/vbase" + +func main() { + // 获取配置单例 + cfg := vbase.Config + + // 启用调试模式 + cfg.Debug = true + + // 设置 JWT 密钥 (建议从环境变量读取) + cfg.JwtSecret = "your-secret-key" + + // 自定义短信服务提供商 (可选) + // vbase.Config.SetSmsProvider(...) +} +``` + +### 2.4 上下文使用 + +通过 `vbase.AuthMiddleware` 认证后,您可以在 Handler 中通过 `*vigo.X` 获取用户信息。 + +```go +func MyHandler(x *vigo.X) error { + // 获取当前用户 ID + userID := x.Get("user_id").(string) + + // 获取当前组织 ID (如果有) + orgID := x.Get("org_id").(string) + + // ... 业务逻辑 +} +``` diff --git a/init.go b/init.go index aea4bfe..2a5d7b1 100644 --- a/init.go +++ b/init.go @@ -9,13 +9,15 @@ package vbase import ( "github.com/veypi/vbase/api" "github.com/veypi/vbase/auth" + "github.com/veypi/vbase/cfg" "github.com/veypi/vigo" ) var Router = vigo.NewRouter() -// Auth 全局权限工厂 var Auth = auth.Factory +var Config = cfg.Config +var AuthMiddleware = auth.AuthMiddleware func init() { // 挂载 API 路由