diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..f6db5e9
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,34 @@
+# Changelog
+
+## v1.2.0 (2026-06-06)
+
+### Features
+
+- **auth**: 替换 user-level token version 为 session-based 认证机制
+- **auth**: 支持短信/邮箱验证码注册
+- **auth**: 登录时递增 token version 以吊销旧会话
+- **auth**: Token 传送方式迁移至 HttpOnly Cookie,支持 version-based 吊销
+- **api**: 启用 API 文档端点
+- **role**: 新增角色用户批量管理 API
+- **role**: 支持增量式权限授予和移除
+- **cfg**: 新增用户创建钩子(user creation hook)
+- **ui**: 支持 URL 路由中的 `@` 前缀
+
+### Changed
+
+- **cfg**: 默认数据库切换为 SQLite
+- **settings**: 用 GORM 结构体查询替换原始 SQL 条件
+- **ui**: 简化角色管理界面和认证流程
+- **ui**: 用 fetch 替换所有页面中的 axios,提取认证布局
+- **ui**: 重构 VBase 认证客户端,支持自动刷新和 onAuthSuccess 回调
+- **ui**: 前端统一将 `$vbase` 重命名为 `$auth`
+
+### Fixed
+
+- **ui**: 修复用户获取失败时的优雅降级处理
+- **ui**: 修复 vhtml script src 使用作用域路径前缀
+- **db**: 修复 longtext 字段类型问题
+
+### Removed
+
+- 移除过期的 todo 和 UI 设计文档
diff --git a/CLAUDE.md b/CLAUDE.md
index d872f9f..44e4698 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -9,14 +9,14 @@ VBase is a Go-based identity authentication and permission management framework
- **Language**: Go 1.24+
- **Framework**: Vigo (onion-model middleware)
- **ORM**: GORM (MySQL, PostgreSQL, SQLite supported)
-- **Authentication**: JWT + OAuth2
+- **Authentication**: Session + JWT (Access/Refresh Token) + Cookie-based delivery + OAuth2
- **Permissions**: Scoped RBAC (Role-Based Access Control)
- **Frontend**: vhtml (embedded HTML-based UI at `/vb/`)
## Common Commands
```bash
-# Run development server (default port 4001)
+# Run development server (default port 4000)
make run
# Database operations
@@ -45,8 +45,10 @@ Response <- [Global After Middleware] <--------+
│ ├── oauth/ # OAuth2 provider endpoints
│ ├── role/ # Role management
│ ├── user/ # User management
+│ ├── settings/ # System settings
+│ ├── verification/ # SMS/email verification code
│ └── init.go # Router aggregation
-├── auth/ # Core Scoped RBAC permission system
+├── auth/ # Scoped RBAC permission system + Session management
│ ├── auth.go # Permission checking implementation
│ └── design.md # Permission system design doc
├── cfg/ # Configuration (DB, Redis, JWT settings)
@@ -74,8 +76,8 @@ The system uses a scoped permission model where permissions are isolated by `Sco
**Key Interfaces**:
- `auth.Factory.New(scope)`: Get scoped auth instance.
-- `auth.PermRead(code)`, `auth.PermWrite(code)`: Middleware checks.
-- `auth.Grant(ctx, userID, permID, level)`: Grant permission.
+- `cfg.Auth.RequireRead(code)`, `cfg.Auth.RequireWrite(code)`: Middleware checks.
+- `cfg.Auth.Grant(ctx, userID, permID, level)`: Grant permission.
### API Response Format
diff --git a/README.md b/README.md
index 7cab127..bd085f7 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
-
+
@@ -22,18 +22,19 @@
## 简介
-**VBase** 是一个基于 [Vigo](https://github.com/veypi/vigo) 框架开发的高性能后端基础框架,提供一套标准化的用户管理、作用域权限控制(Scoped RBAC)和 OAuth2 认证服务。可作为独立服务部署,也可作为基础库集成到你的 Go 项目中。
+**VBase** 是一个基于 [Vigo](https://github.com/veypi/vigo) 框架开发的高性能后端基础框架,提供一套标准化的用户管理、作用域权限控制(Scoped RBAC)和 OAuth2 认证服务。支持 Session + JWT 双 Token 认证、Cookie 令牌传送和短信/邮箱验证码注册。可作为独立服务部署,也可作为基础库集成到你的 Go 项目中。
## 核心特性
-- 🔐 **完整认证体系** - JWT Token 认证(Access + Refresh)、多因素认证(短信验证码)
-- 🛡️ **Scoped RBAC 权限控制** - 基于作用域(Scope)的角色访问控制,支持资源级权限和通配符权限
-- � **层级权限管理** - 支持资源类型(奇数层)和实例(偶数层)的精细化权限控制
-- 🔑 **OAuth2 Provider** - 完整的 OAuth2 服务端实现,支持第三方应用接入
-- 🗄️ **多数据库支持** - MySQL、PostgreSQL、SQLite 自动适配
-- 📱 **内置管理界面** - 基于 vhtml 的嵌入式管理后台
-- 🚀 **高性能** - 基于洋葱模型中间件架构,低延迟高并发
-- 🧪 **完整测试** - 集成测试脚本覆盖核心业务流程
+- **完整认证体系** — Session + JWT(Access + Refresh Token),HttpOnly Cookie / Header / Query 多通道传送
+- **Scoped RBAC 权限控制** — 基于作用域(Scope)的角色访问控制,支持多应用权限隔离
+- **层级权限管理** — 支持资源类型(奇数层)和实例(偶数层)的精细化权限控制
+- **OAuth2 Provider** — 完整的 OAuth2 服务端实现,支持第三方应用接入
+- **多因素认证** — 短信/邮箱验证码注册和验证
+- **多数据库支持** — SQLite(默认)、MySQL、PostgreSQL 自动适配
+- **嵌入式管理界面** — 基于 vhtml 的管理后台,开箱即用
+- **API 文档** — 内置 `/_api.json` 接口文档端点
+- **高性能** — 基于洋葱模型中间件架构,Redis 缓存 Session 验证
## 快速开始
@@ -50,18 +51,14 @@ go get github.com/veypi/vbase
git clone https://github.com/veypi/vbase.git
cd vbase
-# 配置数据库(编辑 dev.yaml 或使用环境变量)
-export DSN="root:password@tcp(127.0.0.1:3306)/vbase?charset=utf8&parseTime=True"
-export DB="mysql"
+# 直接启动(默认 SQLite + 端口 4000)
+go run ./cli/*.go -p 4000
-# 启动服务
-go run ./cli/main.go
-
-# 或使用 Makefile(默认端口 4001)
+# 或使用 Makefile
make run
```
-服务默认在 `http://localhost:4000` 启动,管理界面访问 `/vb/` 路径。
+服务默认在 `http://localhost:4000` 启动,管理界面访问 `/vb/` 路径,API 文档访问 `/_api.json`。
### 作为库集成
@@ -69,15 +66,14 @@ make run
package main
import (
- "github.com/veypi/vbase"
- "github.com/veypi/vigo"
+ "github.com/veypi/vbase"
+ "github.com/veypi/vbase/cfg"
+ "github.com/veypi/vigo"
)
-var Router = vigo.NewRouter()
-
-func init() {
- // 挂载 VBase 路由到 /vb 前缀
- Router.Extend("/vb", vbase.Router)
+func main() {
+ app := vigo.New("myapp", vbase.Router, cfg.Global, vigo.WithInit(vbase.Init))
+ panic(app.Run())
}
```
@@ -91,8 +87,10 @@ vbase/
│ ├── auth/ # 认证接口(登录、注册、Token 刷新)
│ ├── oauth/ # OAuth2 Provider 接口
│ ├── role/ # 角色管理
-│ └── user/ # 用户管理
-├── auth/ # 核心权限控制模块(Scoped RBAC 实现)
+│ ├── user/ # 用户管理
+│ ├── settings/ # 系统设置接口
+│ └── verification/ # 验证码接口
+├── auth/ # 核心权限控制模块(Scoped RBAC + Session 管理)
├── cfg/ # 配置管理
├── cli/ # 命令行入口
├── models/ # 数据模型(GORM)
diff --git a/agents.md b/agents.md
index ae99319..3ef29c7 100644
--- a/agents.md
+++ b/agents.md
@@ -2,35 +2,47 @@
## 注意
-如果开发中发现什么开发规则或者技巧,你可以更新在这个文档,供其他人看
-如果下面某些端口或者路径访问失败,请询问用户是否启动应用
+如果开发中发现什么开发规则或者技巧,你可以更新在这个文档,供其他人看。
+如果下面某些端口或者路径访问失败,请询问用户是否启动应用。
## 后端开发
+### 运行
+
+```bash
+# 使用 SQLite 数据库(默认)
+go run ./cli/*.go -p 4000
+
+# 或指定配置文件
+go run ./cli/*.go -f ./cfg/dev.yml -l debug -p 4000
+```
+
### 测试
```bash
-//重置数据库
+# 重置数据库
rm /tmp/vb.sqlite
-go run cli/main.go -db.type=sqlite -db.dsn /tmp/vb.sqlite -p 4000
+go run ./cli/*.go -p 4000
```
-可以通过 查看接口列表
+可以通过 `http://localhost:4000/_api.json` 查看接口列表。
## UI 界面开发指南
- 界面采用 vhtml 框架,该框架可以将一个 html 文件自动加载为一个组件
-- 开始写界面前请阅读全局样式文件 /ui/assets/common.css,组件内必须使用全局中的变量去组合或者直接使用全局中的样式, 保证所有界面的样式一致, 比如只能使用颜色变量或者通过 color-mix 函数去包含至少一个颜色变量
-- 组件内部避免重复的样式定义 如 body 内无需重复定义字体
-- 本项目使用 vhtml-ui 组件库,该组件库可以通过 curl -sS 查看文档 其组件代码都已经映射到了/v/目录下
-- 前端路由文件 /ui/routes.js 该文件定义了所有的路由规则
+- 开始写界面前请阅读全局样式文件 `/ui/assets/common.css`,组件内必须使用全局中的变量去组合或者直接使用全局中的样式,保证所有界面的样式一致,比如只能使用颜色变量或者通过 color-mix 函数去包含至少一个颜色变量
+- 组件内部避免重复的样式定义,如 body 内无需重复定义字体
+- 本项目使用 vhtml-ui 组件库,该组件库可以通过 `curl -sS http://localhost:4000/v/README.md` 查看文档,其组件代码都已映射到了 `/v/` 目录下
+- 前端路由文件 `/ui/routes.js`,该文件定义了所有的路由规则
## vhtml-ui 文档查看方法
-curl 指令可以不用沙盒运行
-获取文档目录 该操作可以查看所有组件的目录结构
-curl -sS
-获取文档全文(较长,一般建议查询目录再查询章节内容)
-curl -sS
-获取章节内容(可以根据第一步获取的目录编号查询内容) 一般使用这个查询章节内容,查询内容时不能带 toc 参数
-curl -sS
+获取文档目录(查看所有组件的目录结构):
+```bash
+curl -sS http://localhost:4000/v/docs/README.md?toc=1
+```
+
+获取章节内容(根据目录编号查询内容,不能带 toc 参数):
+```bash
+curl -sS http://localhost:4000/v/docs/README.md?from=1.2&to=1.2
+```
diff --git a/auth/design.md b/auth/design.md
index ed9bd37..43b744f 100644
--- a/auth/design.md
+++ b/auth/design.md
@@ -110,16 +110,56 @@ app:vbase:role:admin → 第4层 (偶数) - 实例
## 五、Auth 接口设计
+VBase 的权限系统基于 vigo 框架的 `auth` SPI 模式,分为两层:
+
+- **Provider 接口**:由 VBase 实现 `vbaseProvider`,负责实际的权限存储和校验逻辑。
+- **Auth 结构体**:由 vigo 框架提供 (`auth.Auth`),包装 Provider,提供中间件和高层 API,用于业务模块直接调用。
+
+### 5.1 Provider 接口(VBase 实现)
+
```go
-package auth
+// Provider 是实现端需要实现的 SPI
+type Provider interface {
+ UserID(x *vigo.X) string
+ Check(ctx context.Context, userID, permCode string, permLevel int) bool
+ Grant(ctx context.Context, userID, permCode string, permLevel int) error
+ Revoke(ctx context.Context, userID, permCode string) error
+ ListResources(ctx context.Context, userID, resourceType string) (map[string]int, error)
+ ListUsers(ctx context.Context, permCode string) (map[string]int, error)
+ GrantRole(ctx context.Context, userID, roleCode string) error
+ RevokeRole(ctx context.Context, userID, roleCode string) error
+ AddRole(roleCode, roleName string, permPolicies ...string) error
+}
+```
-import (
- "context"
- "github.com/veypi/vigo"
-)
+VBase 通过 `auth.Factory.New(scope)` 创建作用域隔离的 Provider 实例,实现多应用间权限隔离。
-// ========== 权限等级 ==========
+### 5.2 Auth 结构体(业务调用)
+```go
+// cfg.Auth 是全局权限管理实例,由 vigo 框架提供
+// 中间件方法:
+cfg.Auth.Login() // 检查登录(不检查权限)
+cfg.Auth.Require(permExpr, permLevel) // 通用权限检查
+cfg.Auth.RequireCreate(permExpr) // 创建权限 (level 1)
+cfg.Auth.RequireRead(permExpr) // 读取权限 (level 2)
+cfg.Auth.RequireWrite(permExpr) // 写入权限 (level 4)
+cfg.Auth.RequireAdmin(permExpr) // 管理员权限 (level 7)
+
+// 业务调用方法:
+cfg.Auth.UserID(x) // 获取当前用户 ID
+cfg.Auth.Check(ctx, userID, permCode, level) // 静态权限检查
+cfg.Auth.Grant(ctx, userID, permCode, level) // 授予权限
+cfg.Auth.Revoke(ctx, userID, permCode) // 撤销权限
+cfg.Auth.GrantRole(ctx, userID, roleCode) // 授予角色
+cfg.Auth.RevokeRole(ctx, userID, roleCode) // 撤销角色
+cfg.Auth.ListResources(ctx, userID, resourceType) // 列出资源权限
+cfg.Auth.ListUsers(ctx, permCode) // 列出资源协作者
+```
+
+### 5.3 权限等级常量
+
+```go
const (
LevelNone = 0
LevelCreate = 1 // 001 创建 (检查奇数层)
@@ -128,92 +168,19 @@ const (
LevelReadWrite = 6 // 110 读写 (检查偶数层)
LevelAdmin = 7 // 111 管理员 (完全控制)
)
+```
-// PermFunc 权限检查函数类型
-type PermFunc func(x *vigo.X) error
-
-// Auth 权限管理接口
-type Auth interface {
- // ========== 上下文 ==========
-
- // UserID 获取当前用户ID
- UserID(x *vigo.X) string
-
- // ========== 登录检查 ==========
-
- // Login 检查用户是否登录
- Login() PermFunc
-
- // ========== 权限检查 ==========
-
- // Perm 检查权限
- // code: 权限码,支持动态解析
- // - 固定写法: "app:vbase"
- // - 动态解析: "app:{appID}" 从 path 获取
- // "app:{appID@query}" 从 query 获取
- // "app:{appID@header}" 从 header 获取
- // "app:{appID@ctx}" 从 ctx 获取
- // level: 需要的权限等级
- Perm(code string, level int) PermFunc
-
- // ========== 快捷方法 ==========
-
- // PermCreate 检查创建权限 (level 1,检查奇数层)
- PermCreate(code string) PermFunc
-
- // PermRead 检查读取权限 (level 2,检查偶数层)
- PermRead(code string) PermFunc
-
- // PermWrite 检查更新权限 (level 4,检查偶数层)
- PermWrite(code string) PermFunc
-
- // PermAdmin 检查管理员权限 (level 7,检查偶数层)
- PermAdmin(code string) PermFunc
-
- // ========== 权限授予(业务调用) ==========
-
- // Grant 授予权限
- // 在创建资源、被授权等业务逻辑中调用
- // permissionID: 权限码,如 "app:vbase"
- // level: 权限等级
- Grant(ctx context.Context, userID, permissionID string, level int) error
-
- // Revoke 撤销权限
- Revoke(ctx context.Context, userID, permissionID string) error
-
- // ========== 权限查询 ==========
-
- // Check 检查权限 不支持动态解析
- // permissionID: 完整的权限码,如 "app:vbase"
- Check(ctx context.Context, userID, permissionID string, level int) bool
-
- // ========== 资源列表查询 ==========
-
- // ListResources 查询用户在特定资源类型下的详细权限信息
- // 用于解决 "查询我有权限的 app 列表" 等场景
- // userID: 用户ID
- // resourceType: 资源类型 (奇数层),如 "app" 或 "app:{appID}:role"
- // 返回: map[实例ID]权限等级 (如 {"vbase": 2, "other": 7})
- ListResources(ctx context.Context, userID, resourceType string) (map[string]int, error)
-
- // ListUsers 查询特定资源的所有协作者及其权限
- // 用于解决 "查看这个应用有哪些成员" 等场景
- // permissionID: 资源实例权限码,如 "app:vbase"
- // 返回: map[用户ID]权限等级 (如 {"user1": 2, "user2": 7})
- ListUsers(ctx context.Context, permissionID string) (map[string]int, error)
-}
-
-// ========== 数据结构 ==========
+### 5.4 Permission 数据模型
-// Permission 用户权限
+```go
type Permission struct {
- ID string `json:"id"`
- UserID string `json:"user_id"`
- RoleID string `json:"role_id"`
- PermissionID string `json:"permission_id"`
- Level int `json:"level"`
- CreatedAt int64 `json:"created_at"`
- UpdatedAt int64 `json:"updated_at"`
+ ID string `json:"id"`
+ Scope string `json:"scope"` // 作用域
+ UserID *string `json:"user_id"` // 用户ID(直接授权)
+ RoleID *string `json:"role_id"` // 角色ID(角色授权)
+ PermissionID string `json:"permission_id"` // 权限ID,层级结构
+ Level int `json:"level"` // 权限等级
+ ExpireAt *time.Time `json:"expire_at"` // 过期时间(可选)
}
```
@@ -228,10 +195,10 @@ var Router = vigo.NewRouter()
func init() {
// 创建应用 - 需要系统级 app 权限
- Router.Post("/apps", cfg.Auth.PermCreate("app"), CreateApp)
+ Router.Post("/apps", cfg.Auth.RequireCreate("app"), CreateApp)
// 超级管理员接口
- Router.Get("/admin/users", cfg.Auth.PermAdmin("*"), AdminListUsers)
+ Router.Get("/admin/users", cfg.Auth.RequireAdmin("*"), AdminListUsers)
}
```
@@ -241,16 +208,16 @@ func init() {
func init() {
// 从路径参数获取 appID (默认)
// GET /apps/{appID}
- Router.Get("/apps/{appID}", cfg.Auth.PermRead("app:{appID}"), GetApp)
+ Router.Get("/apps/{appID}", cfg.Auth.RequireRead("app:{appID}"), GetApp)
// 从 query 参数获取
// GET /apps?appID=xxx
- Router.Get("/apps", cfg.Auth.PermRead("app:{appID@query}"), GetApp)
+ Router.Get("/apps", cfg.Auth.RequireRead("app:{appID@query}"), GetApp)
// 多层嵌套
// GET /apps/{appID}/roles/{roleID}
Router.Get("/apps/{appID}/roles/{roleID}",
- cfg.Auth.PermRead("app:{appID}:role:{roleID}"),
+ cfg.Auth.RequireRead("app:{appID}:role:{roleID}"),
GetRole,
)
}
@@ -263,21 +230,21 @@ var Router = vigo.NewRouter().Use(cfg.Auth.Login())
func init() {
// 创建应用 - 系统级权限
- Router.Post("/apps", cfg.Auth.PermCreate("app"), CreateApp)
+ Router.Post("/apps", cfg.Auth.RequireCreate("app"), CreateApp)
// 列出我的应用 - 只需登录
Router.Get("/apps", ListMyApps)
// 应用操作 - 从路径获取
- Router.Get("/apps/{appID}", cfg.Auth.PermRead("app:{appID}"), GetApp)
- Router.Put("/apps/{appID}", cfg.Auth.PermWrite("app:{appID}"), UpdateApp)
- Router.Delete("/apps/{appID}", cfg.Auth.PermAdmin("app:{appID}"), DeleteApp)
+ Router.Get("/apps/{appID}", cfg.Auth.RequireRead("app:{appID}"), GetApp)
+ Router.Put("/apps/{appID}", cfg.Auth.RequireWrite("app:{appID}"), UpdateApp)
+ Router.Delete("/apps/{appID}", cfg.Auth.RequireAdmin("app:{appID}"), DeleteApp)
// 角色操作 - 嵌套资源
- Router.Post("/apps/{appID}/roles", cfg.Auth.PermCreate("app:{appID}:role"), CreateRole)
- Router.Get("/apps/{appID}/roles/{roleID}", cfg.Auth.PermRead("app:{appID}:role:{roleID}"), GetRole)
- Router.Put("/apps/{appID}/roles/{roleID}", cfg.Auth.PermWrite("app:{appID}:role:{roleID}"), UpdateRole)
- Router.Delete("/apps/{appID}/roles/{roleID}", cfg.Auth.PermAdmin("app:{appID}:role:{roleID}"), DeleteRole)
+ Router.Post("/apps/{appID}/roles", cfg.Auth.RequireCreate("app:{appID}:role"), CreateRole)
+ Router.Get("/apps/{appID}/roles/{roleID}", cfg.Auth.RequireRead("app:{appID}:role:{roleID}"), GetRole)
+ Router.Put("/apps/{appID}/roles/{roleID}", cfg.Auth.RequireWrite("app:{appID}:role:{roleID}"), UpdateRole)
+ Router.Delete("/apps/{appID}/roles/{roleID}", cfg.Auth.RequireAdmin("app:{appID}:role:{roleID}"), DeleteRole)
}
```
@@ -322,7 +289,7 @@ func CreateApp(x *vigo.X, req *CreateAppReq) (*AppResp, error) {
对于资源列表(List)或搜索接口,推荐以下设计模式:
1. **全量管理接口**(如后台管理系统):
- - 使用 `PermAdmin("*")` 或 `PermAdmin("app:*")`。
+ - 使用 `RequireAdmin("*")` 或 `RequireAdmin("app:*")`。
- 这类接口返回所有数据,必须严格控制权限。
2. **用户侧列表/搜索**(如“我的应用”):
diff --git a/docs/auth.md b/docs/auth.md
index 84414d7..a4e48ac 100644
--- a/docs/auth.md
+++ b/docs/auth.md
@@ -110,110 +110,77 @@ app:vbase:role:admin → 第4层 (偶数) - 实例
## 五、Auth 接口设计
-```go
-package auth
-
-import (
- "context"
- "github.com/veypi/vigo"
-)
-
-// ========== 权限等级 ==========
-
-const (
- LevelNone = 0
- LevelCreate = 1 // 001 创建 (检查奇数层)
- LevelRead = 2 // 010 读取 (检查偶数层)
- LevelWrite = 4 // 100 写入 (检查偶数层)
- LevelReadWrite = 6 // 110 读写 (检查偶数层)
- LevelAdmin = 7 // 111 管理员 (完全控制)
-)
-
-// PermFunc 权限检查函数类型
-type PermFunc func(x *vigo.X) error
-
-// Auth 权限管理接口
-type Auth interface {
- // ========== 上下文 ==========
-
- // UserID 获取当前用户ID
- UserID(x *vigo.X) string
-
- // ========== 登录检查 ==========
-
- // Login 检查用户是否登录
- Login() PermFunc
-
- // ========== 权限检查 ==========
-
- // Perm 检查权限
- // code: 权限码,支持动态解析
- // - 固定写法: "app:vbase"
- // - 动态解析: "app:{appID}" 从 path 获取
- // "app:{appID@query}" 从 query 获取
- // "app:{appID@header}" 从 header 获取
- // "app:{appID@ctx}" 从 ctx 获取
- // level: 需要的权限等级
- Perm(code string, level int) PermFunc
-
- // ========== 快捷方法 ==========
+VBase 的权限系统基于 vigo 框架的 SPI 模式,分为两层:
- // PermCreate 检查创建权限 (level 1,检查奇数层)
- PermCreate(code string) PermFunc
+- **Provider 接口**:由 VBase 实现 (`vbaseProvider`),负责实际的权限存储和校验逻辑。
+- **Auth 结构体**:由 vigo 框架提供 (`auth.Auth`),包装 Provider,提供中间件和高层 API 供业务模块直接使用。
- // PermRead 检查读取权限 (level 2,检查偶数层)
- PermRead(code string) PermFunc
+### 5.1 Provider 接口(VBase 实现)
- // PermWrite 检查更新权限 (level 4,检查偶数层)
- PermWrite(code string) PermFunc
-
- // PermAdmin 检查管理员权限 (level 7,检查偶数层)
- PermAdmin(code string) PermFunc
-
- // ========== 权限授予(业务调用) ==========
-
- // Grant 授予权限
- // 在创建资源、被授权等业务逻辑中调用
- // permissionID: 权限码,如 "app:vbase"
- // level: 权限等级
- Grant(ctx context.Context, userID, permissionID string, level int) error
-
- // Revoke 撤销权限
- Revoke(ctx context.Context, userID, permissionID string) error
+```go
+// Provider 是实现端需要实现的 SPI
+type Provider interface {
+ UserID(x *vigo.X) string
+ Check(ctx context.Context, userID, permCode string, permLevel int) bool
+ Grant(ctx context.Context, userID, permCode string, permLevel int) error
+ Revoke(ctx context.Context, userID, permCode string) error
+ ListResources(ctx context.Context, userID, resourceType string) (map[string]int, error)
+ ListUsers(ctx context.Context, permCode string) (map[string]int, error)
+ GrantRole(ctx context.Context, userID, roleCode string) error
+ RevokeRole(ctx context.Context, userID, roleCode string) error
+ AddRole(roleCode, roleName string, permPolicies ...string) error
+}
+```
- // ========== 权限查询 ==========
+VBase 通过 `auth.Factory.New(scope)` 创建作用域隔离的 Provider 实例。
- // Check 检查权限 不支持动态解析
- // permissionID: 完整的权限码,如 "app:vbase"
- Check(ctx context.Context, userID, permissionID string, level int) bool
+### 5.2 Auth 结构体(业务调用)
- // ========== 资源列表查询 ==========
+```go
+// cfg.Auth 是全局权限管理实例
+// 中间件方法:
+cfg.Auth.Login()
+cfg.Auth.Require(permExpr, permLevel) // 通用权限检查
+cfg.Auth.RequireCreate(permExpr) // 创建 (level 1)
+cfg.Auth.RequireRead(permExpr) // 读取 (level 2)
+cfg.Auth.RequireWrite(permExpr) // 写入 (level 4)
+cfg.Auth.RequireAdmin(permExpr) // 管理员 (level 7)
+
+// 业务调用方法:
+cfg.Auth.UserID(x) // 获取当前用户 ID
+cfg.Auth.Check(ctx, userID, permCode, level) // 静态权限检查
+cfg.Auth.Grant(ctx, userID, permCode, level) // 授予权限
+cfg.Auth.Revoke(ctx, userID, permCode) // 撤销权限
+cfg.Auth.GrantRole(ctx, userID, roleCode) // 授予角色
+cfg.Auth.RevokeRole(ctx, userID, roleCode) // 撤销角色
+cfg.Auth.ListResources(ctx, userID, resourceType) // 列出资源权限
+cfg.Auth.ListUsers(ctx, permCode) // 列出资源协作者
+```
- // ListResources 查询用户在特定资源类型下的详细权限信息
- // 用于解决 "查询我有权限的 app 列表" 等场景
- // userID: 用户ID
- // resourceType: 资源类型 (奇数层),如 "app" 或 "app:{appID}:role"
- // 返回: map[实例ID]权限等级 (如 {"vbase": 2, "other": 7})
- ListResources(ctx context.Context, userID, resourceType string) (map[string]int, error)
+### 5.3 权限等级常量
- // ListUsers 查询特定资源的所有协作者及其权限
- // 用于解决 "查看这个应用有哪些成员" 等场景
- // permissionID: 资源实例权限码,如 "app:vbase"
- // 返回: map[用户ID]权限等级 (如 {"user1": 2, "user2": 7})
- ListUsers(ctx context.Context, permissionID string) (map[string]int, error)
-}
+```go
+const (
+ LevelNone = 0
+ LevelCreate = 1 // 001 创建 (检查奇数层)
+ LevelRead = 2 // 010 读取 (检查偶数层)
+ LevelWrite = 4 // 100 写入 (检查偶数层)
+ LevelReadWrite = 6 // 110 读写 (检查偶数层)
+ LevelAdmin = 7 // 111 管理员 (完全控制)
+)
+```
-// ========== 数据结构 ==========
+### 5.4 Permission 数据模型
-// Permission 用户权限
+```go
type Permission struct {
- ID string `json:"id"`
- UserID string `json:"user_id"`
- RoleID string `json:"role_id"`
- PermissionID string `json:"permission_id"`
- Level int `json:"level"`
- CreatedAt int64 `json:"created_at"`
- UpdatedAt int64 `json:"updated_at"`
+ ID string `json:"id"`
+ Scope string `json:"scope"`
+ UserID *string `json:"user_id"`
+ RoleID *string `json:"role_id"`
+ PermissionID string `json:"permission_id"`
+ Level int `json:"level"`
+ ExpireAt *time.Time `json:"expire_at"`
}
```
@@ -228,10 +195,10 @@ var Router = vigo.NewRouter()
func init() {
// 创建应用 - 需要系统级 app 权限
- Router.Post("/apps", cfg.Auth.PermCreate("app"), CreateApp)
+ Router.Post("/apps", cfg.Auth.RequireCreate("app"), CreateApp)
// 超级管理员接口
- Router.Get("/admin/users", cfg.Auth.PermAdmin("*"), AdminListUsers)
+ Router.Get("/admin/users", cfg.Auth.RequireAdmin("*"), AdminListUsers)
}
```
@@ -241,16 +208,16 @@ func init() {
func init() {
// 从路径参数获取 appID (默认)
// GET /apps/{appID}
- Router.Get("/apps/{appID}", cfg.Auth.PermRead("app:{appID}"), GetApp)
+ Router.Get("/apps/{appID}", cfg.Auth.RequireRead("app:{appID}"), GetApp)
// 从 query 参数获取
// GET /apps?appID=xxx
- Router.Get("/apps", cfg.Auth.PermRead("app:{appID@query}"), GetApp)
+ Router.Get("/apps", cfg.Auth.RequireRead("app:{appID@query}"), GetApp)
// 多层嵌套
// GET /apps/{appID}/roles/{roleID}
Router.Get("/apps/{appID}/roles/{roleID}",
- cfg.Auth.PermRead("app:{appID}:role:{roleID}"),
+ cfg.Auth.RequireRead("app:{appID}:role:{roleID}"),
GetRole,
)
}
@@ -263,21 +230,21 @@ var Router = vigo.NewRouter().Use(cfg.Auth.Login())
func init() {
// 创建应用 - 系统级权限
- Router.Post("/apps", cfg.Auth.PermCreate("app"), CreateApp)
+ Router.Post("/apps", cfg.Auth.RequireCreate("app"), CreateApp)
// 列出我的应用 - 只需登录
Router.Get("/apps", ListMyApps)
// 应用操作 - 从路径获取
- Router.Get("/apps/{appID}", cfg.Auth.PermRead("app:{appID}"), GetApp)
- Router.Put("/apps/{appID}", cfg.Auth.PermWrite("app:{appID}"), UpdateApp)
- Router.Delete("/apps/{appID}", cfg.Auth.PermAdmin("app:{appID}"), DeleteApp)
+ Router.Get("/apps/{appID}", cfg.Auth.RequireRead("app:{appID}"), GetApp)
+ Router.Put("/apps/{appID}", cfg.Auth.RequireWrite("app:{appID}"), UpdateApp)
+ Router.Delete("/apps/{appID}", cfg.Auth.RequireAdmin("app:{appID}"), DeleteApp)
// 角色操作 - 嵌套资源
- Router.Post("/apps/{appID}/roles", cfg.Auth.PermCreate("app:{appID}:role"), CreateRole)
- Router.Get("/apps/{appID}/roles/{roleID}", cfg.Auth.PermRead("app:{appID}:role:{roleID}"), GetRole)
- Router.Put("/apps/{appID}/roles/{roleID}", cfg.Auth.PermWrite("app:{appID}:role:{roleID}"), UpdateRole)
- Router.Delete("/apps/{appID}/roles/{roleID}", cfg.Auth.PermAdmin("app:{appID}:role:{roleID}"), DeleteRole)
+ Router.Post("/apps/{appID}/roles", cfg.Auth.RequireCreate("app:{appID}:role"), CreateRole)
+ Router.Get("/apps/{appID}/roles/{roleID}", cfg.Auth.RequireRead("app:{appID}:role:{roleID}"), GetRole)
+ Router.Put("/apps/{appID}/roles/{roleID}", cfg.Auth.RequireWrite("app:{appID}:role:{roleID}"), UpdateRole)
+ Router.Delete("/apps/{appID}/roles/{roleID}", cfg.Auth.RequireAdmin("app:{appID}:role:{roleID}"), DeleteRole)
}
```
@@ -324,7 +291,7 @@ func CreateApp(x *vigo.X, req *CreateAppReq) (*AppResp, error) {
1. **全量管理接口**(如后台管理系统):
- - 使用 `PermAdmin("*")` 或 `PermAdmin("app:*")`。
+ - 使用 `RequireAdmin("*")` 或 `RequireAdmin("app:*")`。
- 这类接口返回所有数据,必须严格控制权限。
2. **用户侧列表/搜索**(如“我的应用”):
diff --git a/docs/configuration.md b/docs/configuration.md
index dcf9868..c9a0e9f 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -13,66 +13,76 @@
## 一、本地配置(Local Config)
-存储在 `cfg/cfg.go`,仅包含系统启动必需的配置项。
+存储在 `cfg/cfg.go`,仅包含系统启动必需的配置项。默认使用 SQLite 内存数据库,无需任何外部依赖即可运行。
-### 1.1 配置项列表
+### 1.1 YAML 配置示例
```yaml
# config.yaml 示例
-dsn: "root:123456@tcp(127.0.0.1:3306)/vbase?charset=utf8&parseTime=True&loc=Local"
-db: "mysql"
+db:
+ type: "sqlite" # sqlite / mysql / postgres
+ dsn: "/tmp/vbase.db" # 数据库连接字符串
+ prefix: "vb_" # 表名前缀
redis:
- addr: "localhost:6379"
+ addr: "memory" # memory(内存模式)或 redis://localhost:6379
password: ""
db: 0
-# 系统密钥,用于加密敏感数据(JWT、数据库加密字段等)
-# 生产环境务必修改,建议 32 位以上随机字符串
key: "your-secret-key-change-in-production-min-32-characters"
-host: "0.0.0.0"
-port: 8080
+jwt:
+ secret: "" # JWT 密钥(为空则使用 Key)
+ access_expiry: 900 # Access Token 有效期(秒),默认 15m
+ refresh_expiry: 2592000 # Refresh Token 有效期(秒),默认 30d
+ issuer: "vbase" # JWT 签发者
+ cookie_path: "/" # Cookie Path,默认 / 全站
+ cookie_prefix: "vb_" # Cookie Key 前缀
-storage_path: "./data"
-
-# 初始管理员配置(无人值守部署时使用)
-# 当用户表为空且 username/password 都有值时,启动会自动创建该管理员
init_admin:
- username: "admin"
- password: "" # 生产环境务必设置强密码
+ username: "admin" # 管理员用户名
+ password: "" # 密码(为空则首注成为 admin)
email: "admin@example.com"
```
### 1.2 配置项说明
-| 配置项 | 类型 | 必填 | 说明 |
-|--------|------|------|------|
-| `dsn` | string | 是 | 数据库连接字符串 |
-| `db` | string | 是 | 数据库类型:mysql/postgres/sqlite |
-| `redis` | object | 是 | Redis 配置,`addr: "memory"` 使用内存模式 |
-| `key` | string | 是 | 系统密钥,用于加密敏感数据(建议 32 位以上) |
-| `host` | string | 否 | 服务监听地址,默认 `0.0.0.0` |
-| `port` | int | 否 | 服务监听端口,默认 `8080` |
-| `storage_path` | string | 否 | 文件存储路径,默认 `./data` |
-| `init_admin` | object | 否 | 初始管理员配置(无人值守部署时使用) |
+| 配置项 | 类型 | 必填 | 默认值 | 说明 |
+|--------|------|------|--------|------|
+| `db.type` | string | 否 | `sqlite` | 数据库类型:sqlite / mysql / postgres |
+| `db.dsn` | string | 否 | `/tmp/vbase.db` | 数据库连接字符串 |
+| `db.prefix` | string | 否 | `vb_` | 表名前缀 |
+| `redis.addr` | string | 否 | `memory` | Redis 地址,`memory` 使用内存模式 |
+| `redis.password` | string | 否 | — | Redis 密码 |
+| `redis.db` | int | 否 | `0` | Redis 数据库编号 |
+| `key` | string | 是 | — | 系统密钥,用于加密敏感数据(建议 32 位以上) |
+| `jwt.secret` | string | 否 | — | JWT 密钥(为空则使用 `key`) |
+| `jwt.access_expiry` | duration | 否 | `15m` | Access Token 有效期 |
+| `jwt.refresh_expiry` | duration | 否 | `30d` | Refresh Token 有效期 |
+| `jwt.issuer` | string | 否 | `vbase` | JWT 签发者名称 |
+| `jwt.cookie_path` | string | 否 | `/` | Cookie Path 限定路径 |
+| `jwt.cookie_prefix` | string | 否 | `vb_` | Cookie Key 前缀 |
+
+### 1.3 Token 传送方式
-**`init_admin` 详细说明:**
+VBase 支持三种 Token 提取方式,优先级从高到低:
+
+1. **Cookie**(HttpOnly,浏览器自动携带):Key 为 `{cookie_prefix}access`
+2. **Authorization Header**:`Bearer `
+3. **Query 参数**:`?access_token=`
+
+### 1.4 初始管理员配置
系统提供两种互斥的初始化管理员方式:
| 方式 | 触发条件 | 适用场景 |
|------|----------|----------|
-| **自动创建**(本配置) | `username` 和 `password` 均已配置 | 无人值守部署、容器化环境 |
-| **首注成为 admin** | `password` 为空,首个用户注册时 | 交互式部署、开发测试 |
+| **自动创建** | `init_admin.username` 和 `init_admin.password` 均已配置 | 无人值守部署、容器化环境 |
+| **首注成为 admin** | `init_admin.password` 为空 | 交互式部署、开发测试 |
**方式一:配置 init_admin(推荐用于生产)**
-当满足以下条件时,**系统启动时**自动创建管理员:
-- 用户表为空(首次部署)
-- `init_admin.username` 和 `init_admin.password` 均已配置
-
```yaml
init_admin:
username: "admin"
@@ -80,21 +90,29 @@ init_admin:
email: "admin@example.com"
```
-效果:启动时自动创建 admin,后续注册的用户均为普通 user。
+启动时自动创建管理员,后续注册的用户均为普通 user。
**方式二:首个注册用户成为 admin(默认行为)**
-当 `init_admin.password` 为空时,保持原有逻辑:
-- 第一个注册用户自动被授予 `admin` 角色
-- 从第二个用户开始均为普通 user
+当 `init_admin.password` 为空时,第一个注册用户自动被授予 `admin` 角色。
+
+### 1.5 运行时钩子
+
+```go
+// OnUserCreate 用户创建成功后的回调钩子
+// 可用于发送欢迎邮件、初始化用户资源等
+cfg.OnUserCreate = func(userID string) error {
+ // 自定义逻辑
+ return nil
+}
+```
+
+---
+
+## 二、线上配置(Settings)
-**字段说明:**
+线上配置存储在数据库 `settings` 表中,通过管理后台或 API 实时修改,无需重启。
-| 字段 | 类型 | 必填 | 说明 |
-|------|------|------|------|
-| `username` | string | 是 | 管理员用户名 |
-| `password` | string | 条件 | 密码(配置则启用方式一,为空则启用方式二) |
-| `email` | string | 否 | 邮箱地址 |
+包括:应用名称、登录方式、密码字段配置、验证码开关、邮件/SMS 开关、OAuth 提供商等。
-**其他注意事项:**
-- 仅在没有用户时生效,已有用户则跳过
+详见 API 的 `/api/settings` 和 `/api/info` 接口。
diff --git a/docs/design.md b/docs/design.md
index 562468d..764f067 100644
--- a/docs/design.md
+++ b/docs/design.md
@@ -2,16 +2,17 @@
## 1. 概览
-VBase 是一个基于 Golang 的高性能后端基础框架,旨在提供一套标准化的用户管理、作用域权限控制(Scoped RBAC)和 OAuth2 认证服务。项目采用分层架构设计,基于 `vigo` 框架构建,强调代码的可维护性、扩展性和安全性。
+VBase 是一个基于 Golang 的高性能后端基础框架,旨在提供一套标准化的用户管理、作用域权限控制(Scoped RBAC)和 OAuth2 认证服务。项目采用分层架构设计,基于 `vigo` 框架构建,支持 Session-based 认证、Cookie 令牌传送以及短信/邮箱验证码注册,强调代码的可维护性、扩展性和安全性。
## 2. 技术栈
-- **语言**: Golang 1.22+
+- **语言**: Golang 1.24+
- **Web 框架**: [vigo](https://github.com/veypi/vigo) (基于洋葱模型的高性能框架)
-- **ORM**: GORM (支持 MySQL, PostgreSQL, SQLite 等)
-- **配置管理**: `cfg` 包 (支持环境变量、配置文件)
-- **认证**: JWT (JSON Web Token) + OAuth2
-- **数据库**: 关系型数据库 (MySQL/PostgreSQL)
+- **ORM**: GORM (支持 MySQL, PostgreSQL, SQLite)
+- **配置管理**: `cfg` 包 (YAML 配置文件 + 环境变量)
+- **认证**: Session + JWT (Access/Refresh Token)、Cookie 令牌传送
+- **多因素认证**: 短信/邮箱验证码注册
+- **数据库**: 默认为 SQLite,可按需切换 MySQL/PostgreSQL
## 3. 系统架构
@@ -32,12 +33,15 @@ Response <- [Global After Middleware] <------------------------------------+
│ ├── oauth/ # OAuth2 Provider 接口
│ ├── role/ # 角色管理接口
│ ├── user/ # 用户管理接口
+│ ├── settings/ # 系统设置接口
+│ ├── verification/ # 短信/邮箱验证码接口
│ └── init.go # 路由聚合与全局中间件配置
-├── auth/ # 核心权限控制模块 (Scoped RBAC 实现)
-├── cfg/ # 配置与基础设施 (DB, Redis, Log)
+├── auth/ # 核心权限控制模块 (Scoped RBAC + Session 管理)
+├── cfg/ # 配置与基础设施 (DB, Redis, JWT)
├── models/ # 数据模型定义 (GORM 结构体)
-├── libs/ # 通用工具库
+├── libs/ # 通用工具库 (jwt, cache, crypto, sms, email)
├── cli/ # 命令行入口
+├── ui/ # 嵌入式管理界面 (vhtml)
└── docs/ # 文档
```
@@ -63,7 +67,7 @@ Auth 模块是系统的核心安全组件,实现了基于作用域(Scope)
系统提供 `auth.Auth` 接口和 `auth.Factory` 工厂,支持多应用集成:
1. **获取实例**: `myAuth := auth.Factory.New("my_scope")`
-2. **基础权限**: `myAuth.PermRead("resource:id")` - 检查读取权限。
+2. **基础权限**: `myAuth.RequireRead("resource:id")` - 检查读取权限。
3. **层级检查**:
- **Admin (Level 7)**: 拥有 `*` 或前缀匹配权限(向下继承)。
- **Standard**: 精确匹配且满足 Level 要求。
@@ -71,14 +75,14 @@ Auth 模块是系统的核心安全组件,实现了基于作用域(Scope)
#### 4.1.3 中间件流程
-1. **LoginMiddleware**: 解析 JWT Token,提取 UserID。
-2. **PermMiddleware**: 在业务 Handler 执行前拦截请求,调用 `Check` 进行鉴权。
+1. **Login 中间件**: 从 Cookie / Authorization Header / Query 提取 Token,解析 JWT 获取 UserID 和 SessionID,验证 Session 有效性(Redis 缓存 + DB 回退)。
+2. **Perm 中间件**: 在业务 Handler 执行前拦截请求,调用 `Check` 进行鉴权,支持动态权限码解析(从 Path/Query/Header 获取参数)。
### 4.2 用户体系 (User Module)
-- **User**: 核心用户实体,包含基本信息 (昵称、头像、邮箱等)。
-- **Identity**: 认证信息表,支持多种登录方式 (密码、OAuth、验证码等) 关联到同一用户。
-- **Session**: 用户会话管理,用于记录登录状态和刷新 Token。
+- **User**: 核心用户实体,包含基本信息(用户名、密码、邮箱、手机号等),支持邮箱/手机验证状态追踪。
+- **Identity**: 认证信息表,支持多种登录方式(密码、OAuth 第三方登录)关联到同一用户。
+- **Session**: 登录会话管理,每次登录创建一个 Session,记录设备信息和 IP,支持版本号轮换(防止并发刷新互踢)和单独吊销。
### 4.3 OAuth2 服务 (OAuth Module)
@@ -91,12 +95,16 @@ Auth 模块是系统的核心安全组件,实现了基于作用域(Scope)
## 5. 接口规范
-### 5.1 请求处理
+### 5.1 API 文档
+
+路由启用了 `EnableApiDoc()`,可通过 `/_api.json` 查看所有注册接口的完整文档(包括参数、响应、中间件信息)。
+
+### 5.2 请求处理
- **参数解析**: 利用 `vigo` 的泛型 Handler 机制,自动解析 Query, Path, Header, JSON Body 参数到结构体。
- **验证**: 结构体 Tag (`src`, `json`, `default`) 定义参数源和默认值。
-### 5.2 响应格式
+### 5.3 响应格式
统一使用 JSON 格式响应,由全局后置中间件 `common.JsonResponse` 处理:
@@ -118,19 +126,30 @@ Auth 模块是系统的核心安全组件,实现了基于作用域(Scope)
## 6. 数据库设计
-推荐使用 `vigo.Model` 作为基类,统一包含以下字段:
+所有模型以 `vigo.Model` 作为基类,统一包含以下字段:
- `ID`: UUID (varchar(36))
- `CreatedAt`: 创建时间
- `UpdatedAt`: 更新时间
-- `DeletedAt`: 软删除标记
+- `DeletedAt`: 软删除标记(GORM)
+
+### 6.1 核心表
+
+| 表 | 说明 |
+|----|------|
+| `users` | 用户表(用户名、密码、邮箱、手机、验证状态) |
+| `identities` | 第三方身份绑定表 |
+| `sessions` | 登录会话表(设备信息、IP、版本号轮换) |
+| `roles` | 角色表(系统/自定义) |
+| `permissions` | 权限表(作用域隔离、支持按用户/角色授权) |
+| `user_roles` | 用户-角色关联表 |
-所有模型在 `models/init.go` 中注册,支持服务启动时自动迁移 (`AutoMigrate`)。
+所有模型在 `models/init.go` 中注册,启动时通过 `Init()` 自动迁移 (`AutoMigrate`)。
## 7. 部署与运维
-- **配置**: 支持 `.env` 文件和环境变量覆盖。
-- **构建**: `go build -o vbase cli/main.go`
-- **运行**: `./vbase -p 8080`
+- **配置**: 支持 YAML 配置文件和环境变量覆盖,默认 SQLite 无需额外配置即可运行。
+- **运行**: `go run ./cli/*.go -p 4000` 或 `make run`
+- **数据库迁移**: `go run ./cli/main.go db migrate`
## 8. 开发规范
diff --git a/docs/integration.md b/docs/integration.md
index a843350..02ca178 100644
--- a/docs/integration.md
+++ b/docs/integration.md
@@ -10,121 +10,141 @@ go get github.com/veypi/vbase
## 2. 初始化
-在你的应用入口文件(如 `main.go` 或 `init.go`)中进行初始化。
+在你的应用入口文件(如 `main.go`)中进行初始化。
```go
package main
import (
- "github.com/veypi/vbase"
- "github.com/veypi/vbase/cfg"
- "github.com/veypi/vbase/auth"
- "github.com/veypi/vigo"
+ "github.com/veypi/vbase"
+ "github.com/veypi/vbase/cfg"
+ "github.com/veypi/vigo"
)
-// 1. 创建你的应用 Router
-var Router = vigo.NewRouter()
+func main() {
+ // cfg.Global 已包含默认配置(SQLite 内存数据库)
+ // 可通过环境变量或 YAML 文件覆盖
+ app := vigo.New("myapp", vbase.Router, cfg.Global, vigo.WithInit(vbase.Init))
+ panic(app.Run())
+}
+```
-// 2. 创建你的应用 Auth 实例 (指定 Scope)
-var AppAuth = auth.Factory.New("my_app")
+VBase 自带嵌入式管理界面,启动后访问 `/vb/` 即可使用。
-func init() {
- // 3. 挂载 VBase 路由 (提供登录、用户管理等基础接口)
- Router.Extend("/vb", vbase.Router)
+### 2.1 挂载路由
- // 4. 配置数据库连接 (如果尚未配置)
- // export DSN="root:password@tcp(127.0.0.1:3306)/myapp?charset=utf8&parseTime=True"
-}
+VBase Router 已内置所有 API 路由(`/api/auth`、`/api/users`、`/api/roles`、`/api/oauth`、`/api/settings` 等)和 UI 路由(`/vb/`、`/v/`、`/vhtml/`)。
-func main() {
- // 5. 初始化数据库迁移
- if err := vbase.Init(); err != nil {
- panic(err)
- }
-
- // 6. 启动服务
- app := vigo.New("myapp", Router, cfg.Global, nil)
- panic(app.Run())
+如果你的应用需要自定义路由,将 VBase Router 挂载到你的 Router 上:
+
+```go
+var Router = vigo.NewRouter()
+
+func init() {
+ // 将 VBase 挂载到 /vb 前缀
+ Router.Extend("/vb", vbase.Router)
}
```
## 3. 使用权限系统
-### 3.1 定义路由权限
+VBase 通过 `cfg.Auth` 提供全局权限管理实例。所有中间件方法前缀为 `Require*`。
-使用 `AppAuth` 实例的中间件方法来保护你的路由。
+### 3.1 定义路由权限
```go
func init() {
- // 需要登录
- Router.Use(AppAuth.Login())
+ // 所有路由需要登录
+ Router.Use(cfg.Auth.Login())
- // 资源操作示例
- // 假设你的资源是 "article"
-
- // 创建文章 (需要 "article" 的创建权限 Level 1)
- Router.Post("/articles", AppAuth.PermCreate("article"), CreateArticle)
+ // 资源操作示例
+ // 创建文章 (需要 "article" 的创建权限 Level 1)
+ Router.Post("/articles", cfg.Auth.RequireCreate("article"), CreateArticle)
- // 读取文章 (需要 "article:{id}" 的读取权限 Level 2)
- Router.Get("/articles/{id}", AppAuth.PermRead("article:{id}"), GetArticle)
+ // 读取文章 (需要对特定实例的读取权限 Level 2)
+ Router.Get("/articles/{id}", cfg.Auth.RequireRead("article:{id}"), GetArticle)
- // 更新文章 (需要 "article:{id}" 的写入权限 Level 4)
- Router.Put("/articles/{id}", AppAuth.PermWrite("article:{id}"), UpdateArticle)
+ // 更新文章 (需要对特定实例的写入权限 Level 4)
+ Router.Put("/articles/{id}", cfg.Auth.RequireWrite("article:{id}"), UpdateArticle)
- // 删除文章 (需要 "article:{id}" 的管理员权限 Level 7)
- Router.Delete("/articles/{id}", AppAuth.PermAdmin("article:{id}"), DeleteArticle)
+ // 删除文章 (需要对特定实例的管理员权限 Level 7)
+ Router.Delete("/articles/{id}", cfg.Auth.RequireAdmin("article:{id}"), DeleteArticle)
}
```
-### 3.2 业务逻辑授权
+### 3.2 权限码动态解析
+
+权限码支持动态占位符,从不同来源获取值:
+
+| 语法 | 来源 | 示例 |
+|------|------|------|
+| `{key}` 或 `{key@ctx}` | Context | `{appID}` |
+| `{key@path}` | 路径参数 | `{appID@path}` |
+| `{key@query}` | Query 参数 | `{appID@query}` |
+| `{key@header}` | Header | `{Authorization@header}` |
-在创建资源时,通常需要授予创建者管理权限。
+### 3.3 业务逻辑授权
+
+在创建资源时,授予创建者管理权限:
```go
func CreateArticle(x *vigo.X, req *ArticleReq) (*Article, error) {
- // 1. 获取当前用户
- userID := AppAuth.UserID(x)
-
- // 2. 创建资源逻辑...
- article := &Article{Title: req.Title, AuthorID: userID}
- db.Create(article)
-
- // 3. 授予权限
- // 授予用户对该文章的管理员权限 (Level 7)
- // 这样用户后续可以读取、修改、删除该文章
- permissionID := fmt.Sprintf("article:%s", article.ID)
- if err := AppAuth.Grant(x.Context(), userID, permissionID, auth.LevelAdmin); err != nil {
- // 处理错误 (通常记录日志)
- x.Log.Error("Failed to grant permission", "err", err)
- }
-
- return article, nil
+ userID := cfg.Auth.UserID(x)
+
+ article := &Article{Title: req.Title, AuthorID: userID}
+ cfg.DB().Create(article)
+
+ // 授予用户对该文章的管理员权限 (Level 7)
+ permID := "article:" + article.ID
+ cfg.Auth.Grant(x.Context(), userID, permID, auth.LevelAdmin)
+
+ return article, nil
}
```
-### 3.3 复杂场景:项目成员管理
+### 3.4 多 Scope 权限隔离
-假设你有一个项目管理系统,用户可以被邀请加入项目。
+不同业务模块通过 Scope 实现权限隔离:
```go
-// 邀请成员接口
-func InviteMember(x *vigo.X, req *InviteReq) error {
- // 检查当前用户是否有权限邀请 (通常需要管理员权限)
- // 这一步由路由中间件 PermAdmin("project:{id}") 保证
-
- // 授予被邀请人读取权限 (Level 2)
- permID := fmt.Sprintf("project:%s", req.ProjectID)
- return AppAuth.Grant(x.Context(), req.TargetUserID, permID, auth.LevelRead)
-}
+// 为不同应用创建独立的 Provider
+var AppA_Auth = auth.Factory.New("app_a")
+var AppB_Auth = auth.Factory.New("app_b")
+
+// AppA 的用户权限不会影响 AppB
```
-## 4. 配置说明
+## 4. Session 管理
+
+VBase 使用 Session + JWT 双 Token 机制:
+
+- **Access Token**:短期有效(默认 15 分钟),用于 API 认证
+- **Refresh Token**:长期有效(默认 30 天),用于刷新 Access Token
+- **Session**:记录登录设备信息和 IP,支持版本号轮换防止并发刷新互踢
-VBase 使用 `cfg` 包管理配置,支持环境变量。
+Token 默认通过 HttpOnly Cookie 传送。Session 可通过 `auth.RevokeSession`、`auth.RevokeOtherSessions`、`auth.RevokeAllSessions` 管理。
-- `DB_DSN`: 数据库连接字符串
-- `DB_TYPE`: 数据库类型 (mysql, postgres, sqlite)
-- `JWT_SECRET`: JWT 签名密钥 (生产环境务必修改)
-- `JWT_ISSUER`: Token 签发者
+## 5. 配置说明
+
+VBase 默认使用 SQLite 内存数据库(`/tmp/vbase.db`),无需任何外部依赖即可运行。
+
+配置文件示例(`config.yaml`):
+
+```yaml
+db:
+ type: "sqlite"
+ dsn: "/tmp/vbase.db"
+
+redis:
+ addr: "memory"
+
+key: "your-secret-key-min-32-characters"
+
+jwt:
+ access_expiry: 900
+ refresh_expiry: 2592000
+ issuer: "vbase"
+ cookie_prefix: "vb_"
+```
更多配置详情请参考 [配置文档](./configuration.md)。
diff --git a/init.go b/init.go
index c1a9c97..678e826 100644
--- a/init.go
+++ b/init.go
@@ -18,7 +18,7 @@ import (
"github.com/veypi/vigo"
)
-const Version = "v1.1.1"
+const Version = "v1.2.0"
var Router = vigo.NewRouter().EnableApiDoc()
var (
diff --git a/tests/README.md b/tests/README.md
index 468a04d..5d2223f 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -22,9 +22,9 @@ go test -v ./tests/auth_test.go
### 1. 全局设置 (`main_test.go`)
- **TestMain**: 测试套件的入口点。
- - 初始化 Vigo 应用程序。
+ - 初始化 Vigo 应用程序(带 VBase Router + Auth)。
- 设置内存 SQLite 数据库 (`:memory:`)。
- - 模拟 Redis 客户端。
+ - 模拟 Redis 客户端(支持 Session 缓存验证)。
- 运行所有测试。
- 执行后无需清理资源。
diff --git a/tests/SECURITY_TEST_REPORT.md b/tests/SECURITY_TEST_REPORT.md
index 8431760..856d215 100644
--- a/tests/SECURITY_TEST_REPORT.md
+++ b/tests/SECURITY_TEST_REPORT.md
@@ -1,7 +1,17 @@
# VBase 安全测试报告
## 测试执行时间
-2026-02-18
+2026-02-18(原始) / 2026-06-06(v1.2.0 更新)
+
+## v1.2.0 修复状态
+
+| 问题 | 严重度 | 状态 |
+|------|--------|------|
+| OAuth 客户端访问控制缺失 | 高 | 已修复 |
+| 输入验证缺失 | 高 | 已修复 |
+| Admin 无法访问所有组织 | 高 | 已修复(Session 认证替代) |
+| 并发操作问题 | 中 | 部分修复(Session 版本号轮换) |
+| 速率限制缺失 | 中 | 待实现 |
## 测试概述
本次测试针对 VBase 身份认证和权限管理系统进行了全面的安全测试,包括权限系统、多租户隔离、OAuth2 安全、并发安全和边界情况测试。