v3
veypi 4 months ago
parent 8fa01c4c52
commit 26bd0bfd29

@ -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)
// 子路由挂载

@ -1,48 +0,0 @@
// Copyright (C) 2024 veypi <i@veypi.com>
// 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
}
}

@ -1,155 +0,0 @@
//
// Copyright (C) 2024 veypi <i@veypi.com>
// 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()
}
}

@ -1,45 +0,0 @@
// Copyright (C) 2024 veypi <i@veypi.com>
// 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
}
}

@ -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)
}

@ -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
}

@ -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
}

@ -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)
}

@ -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).

@ -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
}
}

@ -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

@ -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 | 服务器内部错误 |

@ -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 端管理后台)
客户:电商平台公司(混合产品)
├─ 实例1C 端用户系统
│ ├─ 消费者注册/登录
│ ├─ 购物车、订单数据
│ └─ 用户画像存储
└─ 实例2B 端商家管理系统
├─ 商家入驻/审核
├─ 商家员工权限管理
└─ 运营数据分析
```
**关键点:**
- 同一客户的不同业务线可以用独立 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、响应时间、错误率
- 缓存命中率
- 数据库连接池状态

@ -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
// 模块1CRM
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"}},
},
})
// 模块2ERP资源名可以重复通过 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 // 允许的操作
}
```
---
**确认此设计后,我将实现底层代码。**

@ -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()
```
无需重启服务,权限配置实时生效。

@ -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)

@ -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 | 来源IDRole 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 即可)|
| 角色数量 | 多(每项目配角色) | 少(复用系统角色 + 数据范围)|
---
请确认此设计后,我开始修改代码。

@ -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"}'
```

@ -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)
// ... 业务逻辑
}
```

@ -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 路由

Loading…
Cancel
Save