|
|
# vbase 用户权限系统重构设计
|
|
|
|
|
|
## 一、设计原则
|
|
|
|
|
|
1. **标准化**:遵循 OAuth2.0 / OIDC 标准协议
|
|
|
2. **无状态**:服务无状态,水平扩展友好
|
|
|
3. **安全性**:密码bcrypt、JWT签名、HTTPS强制
|
|
|
4. **性能**:Redis缓存热点数据,1分钟TTL自动刷新
|
|
|
5. **可维护性**:清晰分层,单一职责
|
|
|
|
|
|
---
|
|
|
|
|
|
## 二、系统架构
|
|
|
|
|
|
```
|
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
|
│ 接入层 │
|
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
|
|
|
│ │ REST API │ │ OAuth2.0 │ │ OIDC Discovery │ │
|
|
|
│ └──────┬──────┘ └──────┬──────┘ └──────────┬──────────┘ │
|
|
|
└─────────┼────────────────┼────────────────────┼────────────┘
|
|
|
│ │ │
|
|
|
┌─────────▼────────────────▼────────────────────▼────────────┐
|
|
|
│ 服务层 │
|
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
|
|
|
│ │ Auth服务 │ │ Org服务 │ │ OAuth服务 │ │
|
|
|
│ │ (认证) │ │ (组织) │ │ (第三方接入) │ │
|
|
|
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
|
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
|
|
|
│ │ User服务 │ │ Role服务 │ │ Permission服务 │ │
|
|
|
│ │ (用户) │ │ (角色) │ │ (权限) │ │
|
|
|
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
|
|
|
└─────────────────────────────────────────────────────────────┘
|
|
|
│
|
|
|
┌─────────▼──────────────────────────────────────────────────┐
|
|
|
│ 数据层 │
|
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
|
|
|
│ │ MySQL/PSQL │ │ Redis │ │ (可选)Etcd │ │
|
|
|
│ │ (主存储) │ │ (缓存/会话) │ │ (配置中心) │ │
|
|
|
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
|
|
|
└─────────────────────────────────────────────────────────────┘
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
## 三、核心数据模型
|
|
|
|
|
|
### 3.1 身份认证模型
|
|
|
|
|
|
```go
|
|
|
// User - 全局用户表
|
|
|
// 一个用户可属于多个组织,但登录是全局的
|
|
|
type User struct {
|
|
|
ID string `json:"id" gorm:"primaryKey;type:varchar(36)"`
|
|
|
Username string `json:"username" gorm:"uniqueIndex;size:50;not null"`
|
|
|
Password string `json:"-" gorm:"size:255"` // bcrypt hash,第三方登录可为空
|
|
|
Nickname string `json:"nickname" gorm:"size:50"`
|
|
|
Avatar string `json:"avatar" gorm:"size:500"`
|
|
|
Email string `json:"email" gorm:"uniqueIndex;size:100"`
|
|
|
Phone string `json:"phone" gorm:"uniqueIndex;size:20"`
|
|
|
Status int `json:"status" gorm:"default:1"` // 0:禁用 1:正常 2:未激活
|
|
|
EmailVerified bool `json:"email_verified" gorm:"default:false"`
|
|
|
PhoneVerified bool `json:"phone_verified" gorm:"default:false"`
|
|
|
LastLoginAt *time.Time `json:"last_login_at"`
|
|
|
CreatedAt time.Time `json:"created_at"`
|
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
|
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
|
|
}
|
|
|
|
|
|
// Identity - 第三方身份绑定
|
|
|
// 支持多种登录方式绑定到同一账号
|
|
|
type Identity struct {
|
|
|
ID string `json:"id" gorm:"primaryKey;type:varchar(36)"`
|
|
|
UserID string `json:"user_id" gorm:"index;not null"`
|
|
|
Provider string `json:"provider" gorm:"size:20;not null"` // google/github/wechat/ldap
|
|
|
ProviderUID string `json:"provider_uid" gorm:"index;size:100;not null"` // 第三方唯一ID
|
|
|
ProviderName string `json:"provider_name" gorm:"size:50"`
|
|
|
Avatar string `json:"avatar" gorm:"size:500"`
|
|
|
Email string `json:"email" gorm:"size:100"`
|
|
|
AccessToken string `json:"-" gorm:"size:500"` // 加密存储
|
|
|
RefreshToken string `json:"-" gorm:"size:500"`
|
|
|
ExpiresAt *time.Time `json:"-"`
|
|
|
CreatedAt time.Time `json:"created_at"`
|
|
|
}
|
|
|
|
|
|
// Session - 登录会话
|
|
|
// 用于多端登录管理和撤销
|
|
|
type Session struct {
|
|
|
ID string `json:"id" gorm:"primaryKey;type:varchar(36)"`
|
|
|
UserID string `json:"user_id" gorm:"index;not null"`
|
|
|
TokenID string `json:"token_id" gorm:"uniqueIndex;size:36"` // JWT jti
|
|
|
Type string `json:"type" gorm:"size:20;not null"` // access/refresh
|
|
|
DeviceInfo string `json:"device_info" gorm:"size:200"`
|
|
|
IP string `json:"ip" gorm:"size:50"`
|
|
|
ExpiresAt time.Time `json:"expires_at"`
|
|
|
Revoked bool `json:"revoked" gorm:"default:false"`
|
|
|
RevokedAt *time.Time `json:"revoked_at"`
|
|
|
CreatedAt time.Time `json:"created_at"`
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 3.2 组织架构模型
|
|
|
|
|
|
```go
|
|
|
// Org - 组织/租户
|
|
|
// 完全隔离的数据边界
|
|
|
type Org struct {
|
|
|
ID string `json:"id" gorm:"primaryKey;type:varchar(36)"`
|
|
|
Name string `json:"name" gorm:"size:50;not null"`
|
|
|
Code string `json:"code" gorm:"uniqueIndex;size:30;not null"` // 组织唯一编码
|
|
|
OwnerID string `json:"owner_id" gorm:"not null"` // 创建者/所有者
|
|
|
|
|
|
// 树形结构
|
|
|
ParentID *string `json:"parent_id" gorm:"index"`
|
|
|
Path string `json:"path" gorm:"size:500;index"` // 完整路径 /root/tech/backend
|
|
|
Level int `json:"level" gorm:"default:0"`
|
|
|
|
|
|
// 配置
|
|
|
Description string `json:"description" gorm:"size:200"`
|
|
|
Logo string `json:"logo" gorm:"size:500"`
|
|
|
Settings string `json:"-" gorm:"type:text"` // JSON配置
|
|
|
|
|
|
// 状态
|
|
|
Status int `json:"status" gorm:"default:1"` // 0:禁用 1:正常
|
|
|
MaxMembers int `json:"max_members" gorm:"default:100"`
|
|
|
|
|
|
CreatedAt time.Time `json:"created_at"`
|
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
|
}
|
|
|
|
|
|
// OrgMember - 组织成员关系
|
|
|
// 用户与组织的多对多关系
|
|
|
type OrgMember struct {
|
|
|
ID string `json:"id" gorm:"primaryKey;type:varchar(36)"`
|
|
|
OrgID string `json:"org_id" gorm:"uniqueIndex:idx_org_user;not null"`
|
|
|
UserID string `json:"user_id" gorm:"uniqueIndex:idx_org_user;not null"`
|
|
|
|
|
|
// 角色(多个角色,逗号分隔)
|
|
|
RoleIDs string `json:"role_ids" gorm:"size:200"`
|
|
|
|
|
|
// 成员信息
|
|
|
Position string `json:"position" gorm:"size:50"` // 职位
|
|
|
Department string `json:"department" gorm:"size:50"` // 部门名称(冗余)
|
|
|
JoinedAt time.Time `json:"joined_at"`
|
|
|
|
|
|
// 状态 0:待审核 1:正常 2:禁用
|
|
|
Status int `json:"status" gorm:"default:1"`
|
|
|
|
|
|
CreatedAt time.Time `json:"created_at"`
|
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 3.3 权限模型(RBAC + ABAC)
|
|
|
|
|
|
```go
|
|
|
// Role - 角色
|
|
|
// 组织级别的角色定义
|
|
|
type Role struct {
|
|
|
ID string `json:"id" gorm:"primaryKey;type:varchar(36)"`
|
|
|
OrgID string `json:"org_id" gorm:"index;not null"`
|
|
|
Code string `json:"code" gorm:"size:50;not null"` // 角色编码
|
|
|
Name string `json:"name" gorm:"size:50;not null"`
|
|
|
Description string `json:"description" gorm:"size:200"`
|
|
|
|
|
|
// 关联策略
|
|
|
PolicyIDs string `json:"policy_ids" gorm:"type:text"` // 逗号分隔
|
|
|
|
|
|
// 属性
|
|
|
IsDefault bool `json:"is_default" gorm:"default:false"` // 新成员默认角色
|
|
|
IsSystem bool `json:"is_system" gorm:"default:false"` // 系统内置(不可删)
|
|
|
SortOrder int `json:"sort_order" gorm:"default:0"`
|
|
|
|
|
|
CreatedAt time.Time `json:"created_at"`
|
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
|
}
|
|
|
|
|
|
// Policy - 策略
|
|
|
// 细粒度权限定义,支持ABAC
|
|
|
type Policy struct {
|
|
|
ID string `json:"id" gorm:"primaryKey;type:varchar(36)"`
|
|
|
OrgID string `json:"org_id" gorm:"index"` // 空表示全局策略
|
|
|
Code string `json:"code" gorm:"uniqueIndex;size:50;not null"`
|
|
|
Name string `json:"name" gorm:"size:50;not null"`
|
|
|
Description string `json:"description" gorm:"size:200"`
|
|
|
|
|
|
// 资源定义
|
|
|
Resource string `json:"resource" gorm:"size:50;not null"` // 资源类型: user/org/member/role等
|
|
|
Action string `json:"action" gorm:"size:20;not null"` // read/create/update/delete/* /batch_create等
|
|
|
|
|
|
// ABAC条件(CEL表达式)
|
|
|
// 示例: "resource.owner == user.id" - 只能操作自己的资源
|
|
|
// 示例: "user.roles.exists(r, r == 'admin')" - 需要admin角色
|
|
|
// 示例: "resource.org_id == org.id" - 只能操作当前组织的资源
|
|
|
Condition string `json:"condition" gorm:"type:text"`
|
|
|
|
|
|
// 效果: allow/deny(deny优先)
|
|
|
Effect string `json:"effect" gorm:"size:10;default:allow"`
|
|
|
|
|
|
// 优先级(数字越大优先级越高,deny策略建议高优先级)
|
|
|
Priority int `json:"priority" gorm:"default:0"`
|
|
|
|
|
|
IsSystem bool `json:"is_system" gorm:"default:false"`
|
|
|
CreatedAt time.Time `json:"created_at"`
|
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 3.4 OAuth2.0 模型
|
|
|
|
|
|
```go
|
|
|
// OAuthClient - 注册的应用客户端
|
|
|
type OAuthClient struct {
|
|
|
ID string `json:"id" gorm:"primaryKey;type:varchar(36)"`
|
|
|
Name string `json:"name" gorm:"size:50;not null"`
|
|
|
Description string `json:"description" gorm:"size:200"`
|
|
|
|
|
|
// 客户端凭证
|
|
|
ClientID string `json:"client_id" gorm:"uniqueIndex;size:32;not null"`
|
|
|
ClientSecret string `json:"-" gorm:"size:64;not null"`
|
|
|
|
|
|
// OAuth配置
|
|
|
RedirectURIs string `json:"redirect_uris" gorm:"type:text"` // 逗号分隔
|
|
|
GrantTypes string `json:"grant_types" gorm:"size:100"` // authorization_code/refresh_token/client_credentials
|
|
|
ResponseTypes string `json:"response_types" gorm:"size:50"` // code/token
|
|
|
|
|
|
// 访问控制
|
|
|
AllowedScopes string `json:"allowed_scopes" gorm:"size:200"` // openid profile email org roles
|
|
|
TokenExpiry int `json:"token_expiry" gorm:"default:3600"` // access_token有效期(秒)
|
|
|
RefreshExpiry int `json:"refresh_expiry" gorm:"default:2592000"` // refresh_token有效期(秒)
|
|
|
|
|
|
// 归属
|
|
|
OwnerID string `json:"owner_id" gorm:"not null"`
|
|
|
OrgID string `json:"org_id" gorm:"index"` // 可选,绑定特定组织
|
|
|
|
|
|
// 状态 0:禁用 1:正常
|
|
|
Status int `json:"status" gorm:"default:1"`
|
|
|
|
|
|
CreatedAt time.Time `json:"created_at"`
|
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
|
}
|
|
|
|
|
|
// OAuthAuthorization - 授权码存储
|
|
|
type OAuthAuthorization struct {
|
|
|
ID string `json:"id" gorm:"primaryKey;type:varchar(36)"`
|
|
|
UserID string `json:"user_id" gorm:"index;not null"`
|
|
|
ClientID string `json:"client_id" gorm:"index;not null"`
|
|
|
OrgID string `json:"org_id" gorm:"index"`
|
|
|
|
|
|
// 授权信息
|
|
|
Code string `json:"code" gorm:"uniqueIndex;size:64"`
|
|
|
Scope string `json:"scope" gorm:"size:200"`
|
|
|
State string `json:"state" gorm:"size:100"`
|
|
|
|
|
|
// PKCE
|
|
|
CodeChallenge string `json:"-" gorm:"size:128"`
|
|
|
CodeChallengeMethod string `json:"-" gorm:"size:10"`
|
|
|
|
|
|
// 状态
|
|
|
Used bool `json:"used" gorm:"default:false"`
|
|
|
UsedAt *time.Time `json:"used_at"`
|
|
|
ExpiresAt time.Time `json:"expires_at"`
|
|
|
CreatedAt time.Time `json:"created_at"`
|
|
|
}
|
|
|
|
|
|
// OAuthToken - OAuth访问令牌
|
|
|
type OAuthToken struct {
|
|
|
ID string `json:"id" gorm:"primaryKey;type:varchar(36)"`
|
|
|
UserID string `json:"user_id" gorm:"index;not null"`
|
|
|
ClientID string `json:"client_id" gorm:"index;not null"`
|
|
|
OrgID string `json:"org_id" gorm:"index"`
|
|
|
|
|
|
// Token信息
|
|
|
AccessToken string `json:"-" gorm:"uniqueIndex;size:64"`
|
|
|
RefreshToken string `json:"-" gorm:"uniqueIndex;size:64"`
|
|
|
TokenType string `json:"token_type" gorm:"size:10;default:Bearer"`
|
|
|
Scope string `json:"scope" gorm:"size:200"`
|
|
|
|
|
|
// 有效期
|
|
|
ExpiresAt time.Time `json:"expires_at"`
|
|
|
Revoked bool `json:"revoked" gorm:"default:false"`
|
|
|
RevokedAt *time.Time `json:"revoked_at"`
|
|
|
|
|
|
CreatedAt time.Time `json:"created_at"`
|
|
|
}
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
## 四、API 设计
|
|
|
|
|
|
### 4.1 路由结构
|
|
|
|
|
|
```
|
|
|
/api/v1
|
|
|
├── /auth # 认证相关(公开或需基础认证)
|
|
|
│ ├── POST /login
|
|
|
│ ├── POST /logout
|
|
|
│ ├── POST /refresh
|
|
|
│ ├── POST /register
|
|
|
│ ├── POST /forgot-password
|
|
|
│ ├── POST /reset-password
|
|
|
│ ├── GET /captcha
|
|
|
│ ├── GET /oauth/:provider # 第三方登录跳转
|
|
|
│ ├── GET /oauth/:provider/callback
|
|
|
│ └── POST /oauth/bind
|
|
|
│
|
|
|
├── /me # 当前用户(需认证)
|
|
|
│ ├── GET /
|
|
|
│ ├── PATCH /
|
|
|
│ ├── GET /sessions # 登录会话列表
|
|
|
│ ├── DELETE /sessions/:id # 撤销会话
|
|
|
│ ├── GET /identities # 绑定的第三方账号
|
|
|
│ ├── DELETE /identities/:provider # 解绑
|
|
|
│ ├── GET /orgs # 我的组织列表
|
|
|
│ └── POST /change-password
|
|
|
│
|
|
|
├── /users # 用户管理(需权限)
|
|
|
│ ├── GET /
|
|
|
│ ├── POST /
|
|
|
│ ├── GET /:id
|
|
|
│ ├── PATCH /:id
|
|
|
│ ├── DELETE /:id
|
|
|
│ └── PATCH /:id/status
|
|
|
│
|
|
|
├── /orgs # 组织管理
|
|
|
│ ├── GET / # 列表
|
|
|
│ ├── POST / # 创建(需登录)
|
|
|
│ ├── GET /:id
|
|
|
│ ├── PATCH /:id # 需组织管理员权限
|
|
|
│ ├── DELETE /:id
|
|
|
│ ├── GET /:id/tree # 组织树
|
|
|
│ ├── GET /:id/members # 成员列表
|
|
|
│ ├── POST /:id/members # 邀请成员
|
|
|
│ ├── GET /:id/members/:user_id
|
|
|
│ ├── PATCH /:id/members/:user_id # 修改角色/状态
|
|
|
│ ├── DELETE /:id/members/:user_id
|
|
|
│ ├── GET /:id/roles # 组织角色
|
|
|
│ ├── POST /:id/roles
|
|
|
│ └── GET /:id/policies # 组织策略
|
|
|
│
|
|
|
├── /roles # 角色管理(需组织上下文)
|
|
|
│ ├── GET /
|
|
|
│ ├── POST /
|
|
|
│ ├── GET /:id
|
|
|
│ ├── PATCH /:id
|
|
|
│ └── DELETE /:id
|
|
|
│
|
|
|
├── /policies # 策略管理
|
|
|
│ ├── GET /
|
|
|
│ ├── POST /
|
|
|
│ ├── GET /:id
|
|
|
│ ├── PATCH /:id
|
|
|
│ └── DELETE /:id
|
|
|
│
|
|
|
├── /oauth # OAuth2.0 服务端
|
|
|
│ ├── GET /authorize # 授权端点
|
|
|
│ ├── POST /token # 令牌端点
|
|
|
│ ├── POST /revoke # 撤销令牌
|
|
|
│ ├── GET /userinfo # 用户信息
|
|
|
│ ├── GET /.well-known/openid-configuration
|
|
|
│ ├── GET /.well-known/jwks.json
|
|
|
│ │
|
|
|
│ └── /clients # 客户端管理(需认证)
|
|
|
│ ├── GET /
|
|
|
│ ├── POST /
|
|
|
│ ├── GET /:id
|
|
|
│ ├── PATCH /:id
|
|
|
│ ├── DELETE /:id
|
|
|
│ └── POST /:id/reset-secret
|
|
|
│
|
|
|
└── /check # 权限检查(内部服务调用)
|
|
|
└── POST /permission # 检查指定权限
|
|
|
```
|
|
|
|
|
|
### 4.2 认证相关 API
|
|
|
|
|
|
#### POST /api/v1/auth/login
|
|
|
账号密码登录
|
|
|
|
|
|
```json
|
|
|
// Request
|
|
|
{
|
|
|
"username": "string", // 用户名/邮箱/手机号
|
|
|
"password": "string",
|
|
|
"captcha_id": "string", // 可选,错误多次后需要
|
|
|
"captcha_code": "string",
|
|
|
"remember": false // 是否长效token
|
|
|
}
|
|
|
|
|
|
// Response 200
|
|
|
{
|
|
|
"access_token": "eyJhbGciOiJSUzI1NiIs...",
|
|
|
"refresh_token": "dGhpcyBpcyBhIHJlZnJlc2g...",
|
|
|
"token_type": "Bearer",
|
|
|
"expires_in": 3600,
|
|
|
"user": {
|
|
|
"id": "uuid",
|
|
|
"username": "john",
|
|
|
"nickname": "John Doe",
|
|
|
"email": "john@example.com",
|
|
|
"avatar": "https://..."
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
#### POST /api/v1/auth/oauth/:provider
|
|
|
第三方登录,返回跳转URL或直接302跳转
|
|
|
|
|
|
```json
|
|
|
// Query Params
|
|
|
?redirect_uri=https://app.com/callback&state=xyz
|
|
|
|
|
|
// Response (如果是SPA,返回URL)
|
|
|
{
|
|
|
"auth_url": "https://accounts.google.com/o/oauth2/v2/auth..."
|
|
|
}
|
|
|
```
|
|
|
|
|
|
#### POST /api/v1/auth/oauth/bind
|
|
|
绑定第三方账号到已有账号
|
|
|
|
|
|
```json
|
|
|
// Request
|
|
|
{
|
|
|
"provider": "google",
|
|
|
"code": "authorization_code_from_provider",
|
|
|
"bind_type": "register" | "login", // 新注册还是绑定已有
|
|
|
"username": "string", // bind_type=register时需要
|
|
|
"password": "string" // bind_type=login时需要
|
|
|
}
|
|
|
|
|
|
// Response
|
|
|
{
|
|
|
"access_token": "...",
|
|
|
"user": {...}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 4.3 组织相关 API
|
|
|
|
|
|
#### GET /api/v1/orgs
|
|
|
获取组织列表(用户加入的)
|
|
|
|
|
|
```json
|
|
|
// Response
|
|
|
{
|
|
|
"items": [
|
|
|
{
|
|
|
"id": "uuid",
|
|
|
"name": "技术部",
|
|
|
"code": "tech",
|
|
|
"logo": "https://...",
|
|
|
"owner_id": "uuid",
|
|
|
"member_count": 25,
|
|
|
"my_roles": ["admin", "developer"],
|
|
|
"joined_at": "2024-01-15T10:30:00Z"
|
|
|
}
|
|
|
]
|
|
|
}
|
|
|
```
|
|
|
|
|
|
#### POST /api/v1/orgs
|
|
|
创建组织
|
|
|
|
|
|
```json
|
|
|
// Request
|
|
|
{
|
|
|
"name": "新产品线",
|
|
|
"code": "new-product", // 唯一编码
|
|
|
"description": "...",
|
|
|
"logo": "https://..."
|
|
|
}
|
|
|
|
|
|
// Response
|
|
|
{
|
|
|
"id": "uuid",
|
|
|
"name": "新产品线",
|
|
|
"code": "new-product",
|
|
|
"owner_id": "current_user_id",
|
|
|
// 自动创建owner角色并分配
|
|
|
}
|
|
|
```
|
|
|
|
|
|
**注意**:创建者自动成为组织所有者(owner),拥有所有权限。
|
|
|
|
|
|
### 4.4 权限相关 API
|
|
|
|
|
|
#### GET /api/v1/check/permissions
|
|
|
获取当前用户在指定组织的所有权限(用于前端按钮控制)
|
|
|
|
|
|
```json
|
|
|
// Query Params
|
|
|
?org_id=xxx
|
|
|
|
|
|
// Response
|
|
|
{
|
|
|
"org_id": "uuid",
|
|
|
"roles": ["admin", "developer"],
|
|
|
"permissions": [
|
|
|
"user:read",
|
|
|
"user:update:own",
|
|
|
"org:read",
|
|
|
"member:manage",
|
|
|
"role:read"
|
|
|
]
|
|
|
}
|
|
|
```
|
|
|
|
|
|
#### POST /api/v1/check/permission
|
|
|
检查具体权限(其他服务调用)
|
|
|
|
|
|
```json
|
|
|
// Request
|
|
|
{
|
|
|
"org_id": "uuid",
|
|
|
"resource": "user",
|
|
|
"action": "update",
|
|
|
"resource_id": "target_user_id" // 可选
|
|
|
}
|
|
|
|
|
|
// Response
|
|
|
{
|
|
|
"allowed": true,
|
|
|
"reason": "用户是资源所有者"
|
|
|
}
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
## 五、权限系统设计
|
|
|
|
|
|
### 5.1 策略表达式(CEL)
|
|
|
|
|
|
使用 [CEL (Common Expression Language)](https://github.com/google/cel-go) 作为条件表达式语言。
|
|
|
|
|
|
```go
|
|
|
// 内置变量
|
|
|
user // 当前用户对象
|
|
|
org // 当前组织对象
|
|
|
resource // 被访问资源对象
|
|
|
action // 动作: read/create/update/delete
|
|
|
|
|
|
// 示例策略条件
|
|
|
"true" // 无条件允许
|
|
|
"resource.created_by == user.id" // 只能操作自己创建的资源
|
|
|
"resource.owner_id == user.id" // 资源所有者
|
|
|
"org.members.exists(m, m.user_id == user.id && m.roles.exists(r, r.code == 'admin'))"
|
|
|
"user.id in org.owners" // 组织所有者
|
|
|
"resource.org_id == org.id" // 只能访问当前组织的资源
|
|
|
"resource.status == 'pending' && action == 'update'" // 特定状态才允许操作
|
|
|
```
|
|
|
|
|
|
### 5.2 系统内置策略
|
|
|
|
|
|
| 编码 | 资源 | 动作 | 条件 | 效果 | 说明 |
|
|
|
|------|------|------|------|------|------|
|
|
|
| `sys:user:read:own` | user | read | `resource.id == user.id` | allow | 读自己 |
|
|
|
| `sys:user:update:own` | user | update | `resource.id == user.id` | allow | 改自己 |
|
|
|
| `sys:org:admin` | org | * | `org.owner_id == user.id` | allow | 组织所有者 |
|
|
|
| `sys:member:read` | member | read | `resource.org_id == org.id` | allow | 读成员 |
|
|
|
| `sys:member:manage` | member | * | 需自定义角色 | allow | 管理成员 |
|
|
|
| `sys:role:read` | role | read | `resource.org_id == org.id` | allow | 读角色 |
|
|
|
| `sys:role:manage` | role | * | 需自定义角色 | allow | 管理角色 |
|
|
|
|
|
|
### 5.3 权限检查流程
|
|
|
|
|
|
```go
|
|
|
// 中间件流程
|
|
|
func PermissionMiddleware(resource, action string) vigo.Middleware {
|
|
|
return func(x *vigo.X) error {
|
|
|
// 1. 获取当前用户
|
|
|
user := auth.CurrentUser(x)
|
|
|
if user == nil {
|
|
|
return vigo.ErrUnauthorized
|
|
|
}
|
|
|
|
|
|
// 2. 获取当前组织(从Header或Query)
|
|
|
orgID := x.Request.Header.Get("X-Org-ID")
|
|
|
if orgID == "" {
|
|
|
orgID = x.Query("org_id")
|
|
|
}
|
|
|
|
|
|
// 3. 构建缓存key
|
|
|
cacheKey := fmt.Sprintf("perm:%s:%s:%s:%s", user.ID, orgID, resource, action)
|
|
|
|
|
|
// 4. 查缓存
|
|
|
if cached, err := redis.Get(cacheKey); err == nil {
|
|
|
if cached == "deny" {
|
|
|
return vigo.ErrForbidden
|
|
|
}
|
|
|
x.Set("permission_checked", true)
|
|
|
return x.Next()
|
|
|
}
|
|
|
|
|
|
// 5. 查询用户在该组织的角色
|
|
|
member := getOrgMember(orgID, user.ID)
|
|
|
if member == nil {
|
|
|
redis.Set(cacheKey, "deny", 1*time.Minute)
|
|
|
return vigo.ErrForbidden
|
|
|
}
|
|
|
|
|
|
// 6. 收集所有策略
|
|
|
policies := getPoliciesByRoles(member.RoleIDs)
|
|
|
|
|
|
// 7. 评估策略
|
|
|
allowed := evaluatePolicies(policies, user, org, resource, action)
|
|
|
|
|
|
// 8. 写入缓存
|
|
|
if allowed {
|
|
|
redis.Set(cacheKey, "allow", 1*time.Minute)
|
|
|
} else {
|
|
|
redis.Set(cacheKey, "deny", 1*time.Minute)
|
|
|
}
|
|
|
|
|
|
if !allowed {
|
|
|
return vigo.ErrForbidden
|
|
|
}
|
|
|
|
|
|
return x.Next()
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
## 六、OAuth2.0 / OIDC 实现
|
|
|
|
|
|
### 6.1 支持的授权流程
|
|
|
|
|
|
1. **Authorization Code Flow**(推荐,支持PKCE)
|
|
|
- 用于服务端应用
|
|
|
- 支持 `code_challenge` (PKCE)
|
|
|
|
|
|
2. **Implicit Flow**(不推荐,但为了兼容支持)
|
|
|
- 用于纯前端应用(建议迁移到PKCE)
|
|
|
|
|
|
3. **Client Credentials Flow**
|
|
|
- 用于服务间调用
|
|
|
|
|
|
4. **Refresh Token**
|
|
|
- 用于刷新 access_token
|
|
|
|
|
|
### 6.2 OIDC 支持
|
|
|
|
|
|
- `/.well-known/openid-configuration` - 发现端点
|
|
|
- `/.well-known/jwks.json` - 公钥获取
|
|
|
- Scope: `openid profile email org roles`
|
|
|
- ID Token: JWT格式,包含用户基本信息
|
|
|
|
|
|
### 6.3 授权流程示例
|
|
|
|
|
|
```
|
|
|
┌─────────────┐ ┌─────────────┐
|
|
|
│ 第三方应用 │ │ vbase │
|
|
|
└──────┬──────┘ └──────┬──────┘
|
|
|
│ │
|
|
|
│ 1. GET /oauth/authorize? │
|
|
|
│ response_type=code& │
|
|
|
│ client_id=xxx& │
|
|
|
│ redirect_uri=xxx& │
|
|
|
│ scope=openid profile org& │
|
|
|
│ state=xxx& │
|
|
|
│ code_challenge=xxx& │
|
|
|
│ code_challenge_method=S256 │
|
|
|
├─────────────────────────────────────────────────►│
|
|
|
│ │
|
|
|
│ 2. 未登录,重定向到登录页 │
|
|
|
│◄─────────────────────────────────────────────────┤
|
|
|
│ │
|
|
|
│ 3. 登录后,用户授权确认页面 │
|
|
|
│ (展示应用请求的权限范围) │
|
|
|
├─────────────────────────────────────────────────►│
|
|
|
│ │
|
|
|
│ 4. 302 重定向到 redirect_uri │
|
|
|
│ ?code=xxx&state=xxx │
|
|
|
│◄─────────────────────────────────────────────────┤
|
|
|
│ │
|
|
|
│ 5. POST /oauth/token │
|
|
|
│ grant_type=authorization_code& │
|
|
|
│ code=xxx& │
|
|
|
│ redirect_uri=xxx& │
|
|
|
│ client_id=xxx& │
|
|
|
│ client_secret=xxx& │
|
|
|
│ code_verifier=xxx (PKCE) │
|
|
|
├─────────────────────────────────────────────────►│
|
|
|
│ │
|
|
|
│ 6. 返回 Token │
|
|
|
│ { │
|
|
|
│ access_token: "...", │
|
|
|
│ id_token: "...", │
|
|
|
│ refresh_token: "...", │
|
|
|
│ token_type: "Bearer", │
|
|
|
│ expires_in: 3600 │
|
|
|
│ } │
|
|
|
│◄─────────────────────────────────────────────────┤
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
## 七、缓存策略
|
|
|
|
|
|
### 7.1 Redis 缓存结构
|
|
|
|
|
|
```
|
|
|
# 权限缓存(核心)
|
|
|
key: perm:{user_id}:{org_id}:{resource}:{action}
|
|
|
value: allow | deny
|
|
|
ttl: 60秒
|
|
|
|
|
|
# 用户信息缓存
|
|
|
key: user:{user_id}
|
|
|
value: JSON
|
|
|
ttl: 300秒
|
|
|
|
|
|
# 组织信息缓存
|
|
|
key: org:{org_id}
|
|
|
value: JSON
|
|
|
ttl: 300秒
|
|
|
|
|
|
# 组织成员缓存
|
|
|
key: org:{org_id}:member:{user_id}
|
|
|
value: JSON (包含角色ID列表)
|
|
|
ttl: 60秒
|
|
|
|
|
|
# 角色策略缓存
|
|
|
key: role:{role_id}:policies
|
|
|
value: JSON数组
|
|
|
ttl: 300秒
|
|
|
|
|
|
# Session/Token 黑名单(用于撤销)
|
|
|
key: token:revoked:{jti}
|
|
|
value: 1
|
|
|
ttl: 与token剩余有效期一致
|
|
|
|
|
|
# 限流计数
|
|
|
key: ratelimit:{ip}:{path}
|
|
|
value: 计数
|
|
|
ttl: 60秒
|
|
|
|
|
|
# 验证码
|
|
|
key: captcha:{captcha_id}
|
|
|
value: 验证码内容
|
|
|
ttl: 300秒
|
|
|
|
|
|
# OAuth 授权码
|
|
|
key: oauth:code:{code}
|
|
|
value: 授权信息JSON
|
|
|
ttl: 600秒
|
|
|
```
|
|
|
|
|
|
### 7.2 缓存更新策略
|
|
|
|
|
|
1. **被动失效**:数据变更时删除缓存
|
|
|
2. **主动刷新**:TTL到期自动重新加载
|
|
|
3. **权限缓存**:短TTL(1分钟),保证实时性
|
|
|
4. **配置缓存**:长TTL(5分钟),减少DB查询
|
|
|
|
|
|
---
|
|
|
|
|
|
## 八、安全设计
|
|
|
|
|
|
### 8.1 密码安全
|
|
|
|
|
|
- **算法**:bcrypt(cost=12)
|
|
|
- **历史密码**:不允许重复使用最近5次密码
|
|
|
- **复杂度**:最小8位,包含大小写+数字
|
|
|
- **传输**:HTTPS only,前端bcrypt预哈希(可选)
|
|
|
|
|
|
### 8.2 Token安全
|
|
|
|
|
|
- **Access Token**:JWT RS256签名,有效期1小时
|
|
|
- **Refresh Token**:随机字符串,有效期30天,可撤销
|
|
|
- **Token绑定**:可选绑定设备指纹,防止盗用
|
|
|
- **撤销机制**:JWT jti 存入黑名单实现撤销
|
|
|
|
|
|
### 8.3 OAuth安全
|
|
|
|
|
|
- **PKCE**:所有public client强制启用
|
|
|
- **State参数**:强制验证,防止CSRF
|
|
|
- **Redirect URI**:必须预注册,严格匹配
|
|
|
- **Client Secret**:仅用于confidential client
|
|
|
|
|
|
### 8.4 防护措施
|
|
|
|
|
|
- **限流**:
|
|
|
- 登录:5次/分钟,错误5次后需要验证码
|
|
|
- 注册:3次/小时
|
|
|
- API:100次/分钟/用户
|
|
|
|
|
|
- **验证码**:
|
|
|
- 图形验证码:登录错误多次后
|
|
|
- 邮箱/短信验证码:密码重置、敏感操作
|
|
|
|
|
|
- **审计日志**:
|
|
|
- 登录/登出
|
|
|
- 密码修改
|
|
|
- 权限变更
|
|
|
- 组织重要操作
|
|
|
|
|
|
---
|
|
|
|
|
|
## 九、初始化流程
|
|
|
|
|
|
系统首次启动时自动执行:
|
|
|
|
|
|
1. **创建数据库表**
|
|
|
|
|
|
2. **创建超级管理员**
|
|
|
- 检测用户表是否为空
|
|
|
- 为空时创建默认超管账号
|
|
|
- 用户名/密码从环境变量读取(或随机生成并打印日志)
|
|
|
|
|
|
3. **创建系统策略**
|
|
|
- 创建所有系统内置策略
|
|
|
|
|
|
4. **创建默认组织**(可选)
|
|
|
- 创建名为"Default"的根组织
|
|
|
- 将超管加入该组织
|
|
|
|
|
|
---
|
|
|
|
|
|
## 十、技术选型
|
|
|
|
|
|
| 组件 | 选型 | 说明 |
|
|
|
|------|------|------|
|
|
|
| Web框架 | Vigo | 保持使用 |
|
|
|
| ORM | GORM | 保持使用 |
|
|
|
| 数据库 | MySQL/PostgreSQL | 保持使用 |
|
|
|
| 缓存 | Redis | 必选 |
|
|
|
| 密码哈希 | bcrypt | golang.org/x/crypto/bcrypt |
|
|
|
| JWT | jwt-go / golang-jwt | 标准库 |
|
|
|
| CEL表达式 | cel-go | github.com/google/cel-go |
|
|
|
| 验证码 | base64Captcha | 或自研 |
|
|
|
| OAuth2 | go-oauth2/oauth2 | 或自研实现 |
|
|
|
|
|
|
---
|
|
|
|
|
|
## 十一、目录结构
|
|
|
|
|
|
```
|
|
|
/
|
|
|
├── cmd/
|
|
|
│ └── server/
|
|
|
│ └── main.go # 服务入口
|
|
|
├── internal/ # 私有代码
|
|
|
│ ├── api/ # API层
|
|
|
│ │ ├── auth/ # 认证API
|
|
|
│ │ ├── user/ # 用户API
|
|
|
│ │ ├── org/ # 组织API
|
|
|
│ │ ├── role/ # 角色API
|
|
|
│ │ ├── policy/ # 策略API
|
|
|
│ │ ├── oauth/ # OAuth2.0服务端
|
|
|
│ │ └── middleware/ # 中间件
|
|
|
│ │ ├── auth.go # 认证中间件
|
|
|
│ │ ├── permission.go # 权限中间件
|
|
|
│ │ ├── ratelimit.go # 限流中间件
|
|
|
│ │ └── cors.go # 跨域中间件
|
|
|
│ ├── service/ # 业务逻辑层
|
|
|
│ │ ├── auth.go
|
|
|
│ │ ├── user.go
|
|
|
│ │ ├── org.go
|
|
|
│ │ ├── role.go
|
|
|
│ │ ├── policy.go
|
|
|
│ │ ├── permission.go # 权限检查核心
|
|
|
│ │ └── oauth.go
|
|
|
│ ├── model/ # 数据模型
|
|
|
│ │ ├── user.go
|
|
|
│ │ ├── org.go
|
|
|
│ │ ├── role.go
|
|
|
│ │ ├── policy.go
|
|
|
│ │ ├── oauth.go
|
|
|
│ │ └── migrate.go # 数据库迁移
|
|
|
│ ├── repository/ # 数据访问层
|
|
|
│ │ ├── user.go
|
|
|
│ │ ├── org.go
|
|
|
│ │ └── ...
|
|
|
│ ├── cache/ # 缓存封装
|
|
|
│ │ └── redis.go
|
|
|
│ ├── pkg/ # 内部工具包
|
|
|
│ │ ├── crypto/ # 加密工具
|
|
|
│ │ ├── jwt/ # JWT工具
|
|
|
│ │ ├── cel/ # CEL表达式
|
|
|
│ │ └── oauth/ # OAuth2工具
|
|
|
│ └── config/ # 配置
|
|
|
│ └── config.go
|
|
|
├── pkg/ # 可公开使用的包
|
|
|
│ └── sdk/ # 其他服务使用的SDK
|
|
|
├── docs/ # 文档
|
|
|
├── scripts/ # 脚本
|
|
|
├── configs/ # 配置文件
|
|
|
├── go.mod
|
|
|
└── README.md
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
## 十二、开发计划
|
|
|
|
|
|
### Phase 1: 基础架构
|
|
|
1. 数据库模型定义和迁移
|
|
|
2. Redis缓存封装
|
|
|
3. JWT认证中间件
|
|
|
4. 基础配置管理
|
|
|
|
|
|
### Phase 2: 认证模块
|
|
|
1. 用户注册/登录/登出
|
|
|
2. 密码管理
|
|
|
3. Session管理
|
|
|
4. 验证码
|
|
|
|
|
|
### Phase 3: 组织与成员
|
|
|
1. 组织CRUD
|
|
|
2. 成员管理
|
|
|
3. 组织树查询
|
|
|
|
|
|
### Phase 4: 权限系统
|
|
|
1. 策略定义
|
|
|
2. 角色管理
|
|
|
3. 权限检查中间件
|
|
|
4. CEL表达式评估
|
|
|
|
|
|
### Phase 5: OAuth2.0
|
|
|
1. 客户端管理
|
|
|
2. 授权流程
|
|
|
3. Token管理
|
|
|
4. OIDC支持
|
|
|
|
|
|
### Phase 6: 第三方登录
|
|
|
1. Google/GitHub/微信登录
|
|
|
2. 账号绑定
|
|
|
|
|
|
### Phase 7: 完善
|
|
|
1. 审计日志
|
|
|
2. 管理后台API
|
|
|
3. 测试和文档
|
|
|
|
|
|
---
|
|
|
|
|
|
**确认以上设计后,开始Phase 1实现。**
|