143 lines
4.2 KiB
Go
143 lines
4.2 KiB
Go
/*
|
||
Package middleware 包含系统的各种中间件
|
||
|
||
Casbin 权限控制中间件实现了基于 RBAC(Role-Based Access Control)的权限访问控制模型。
|
||
该中间件负责:
|
||
- 验证用户登录状态和账户状态
|
||
- 获取用户的角色信息
|
||
- 基于角色检查用户对特定资源的访问权限
|
||
- 阻止未授权的请求访问受保护的资源
|
||
|
||
权限控制流程:
|
||
1. 从 JWT Token 中获取当前登录用户信息
|
||
2. 检查用户账户状态(是否被禁用)
|
||
3. 获取用户的所有有效角色
|
||
4. 根据请求的 URL 和 HTTP 方法进行权限检查
|
||
5. 允许或拒绝请求继续执行
|
||
|
||
RBAC 模型说明:
|
||
- Subject(主体):用户的角色关键字
|
||
- Object(对象):请求的 API 路径
|
||
- Action(动作):HTTP 请求方法(GET、POST、PUT、DELETE 等)
|
||
*/
|
||
package middleware
|
||
|
||
import (
|
||
"strings"
|
||
"sync"
|
||
|
||
"github.com/eryajf/go-ldap-admin/config"
|
||
"github.com/eryajf/go-ldap-admin/public/common"
|
||
"github.com/eryajf/go-ldap-admin/public/tools"
|
||
"github.com/eryajf/go-ldap-admin/service/isql"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
// checkLock 权限检查互斥锁
|
||
// 确保同一时间只有一个请求在执行权限校验,避免并发访问导致的权限检查异常
|
||
var checkLock sync.Mutex
|
||
|
||
// CasbinMiddleware Casbin 权限控制中间件
|
||
// 基于 RBAC 模型的权限访问控制中间件,用于保护需要权限验证的 API 接口
|
||
//
|
||
// 权限验证流程:
|
||
// 1. 获取当前登录用户信息
|
||
// 2. 验证用户状态(是否被禁用)
|
||
// 3. 收集用户的有效角色
|
||
// 4. 构建权限检查参数(角色、路径、方法)
|
||
// 5. 调用 Casbin 引擎进行权限验证
|
||
// 6. 根据验证结果决定是否允许请求继续
|
||
func CasbinMiddleware() gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
// ==================== 用户身份验证 ====================
|
||
|
||
// 从 JWT Token 中获取当前登录用户信息
|
||
user, err := isql.User.GetCurrentLoginUser(c)
|
||
if err != nil {
|
||
tools.Response(c, 401, 401, nil, "用户未登录")
|
||
c.Abort()
|
||
return
|
||
}
|
||
|
||
// 检查用户账户状态,禁用用户不允许访问
|
||
if user.Status != 1 {
|
||
tools.Response(c, 401, 401, nil, "当前用户已被禁用")
|
||
c.Abort()
|
||
return
|
||
}
|
||
|
||
// ==================== 角色信息收集 ====================
|
||
|
||
// 获取用户的所有角色
|
||
roles := user.Roles
|
||
|
||
// 收集所有有效角色的关键字(只包含启用状态的角色)
|
||
var subs []string
|
||
for _, role := range roles {
|
||
if role.Status == 1 {
|
||
subs = append(subs, role.Keyword)
|
||
}
|
||
}
|
||
|
||
// ==================== 权限检查参数构建 ====================
|
||
|
||
// 获取请求的 API 路径(移除 URL 前缀)
|
||
obj := strings.TrimPrefix(c.FullPath(), "/"+config.Conf.System.UrlPathPrefix)
|
||
|
||
// 获取 HTTP 请求方法
|
||
act := c.Request.Method
|
||
|
||
// ==================== 权限验证 ====================
|
||
|
||
// 调用权限检查函数
|
||
isPass := check(subs, obj, act)
|
||
if !isPass {
|
||
tools.Response(c, 401, 401, nil, "没有权限")
|
||
c.Abort()
|
||
return
|
||
}
|
||
|
||
// 权限验证通过,继续执行后续中间件和处理函数
|
||
c.Next()
|
||
}
|
||
}
|
||
|
||
// check 执行具体的权限检查逻辑
|
||
// 该函数使用 Casbin 引擎验证用户角色是否有权限访问指定资源
|
||
//
|
||
// 参数说明:
|
||
// - subs: 用户的角色关键字列表
|
||
// - obj: 请求的 API 路径
|
||
// - act: HTTP 请求方法
|
||
//
|
||
// 返回值:
|
||
// - bool: true 表示有权限,false 表示无权限
|
||
//
|
||
// 权限检查策略:
|
||
// 1. 遍历用户的所有有效角色
|
||
// 2. 对每个角色调用 Casbin 引擎进行权限验证
|
||
// 3. 只要有一个角色通过验证,就认为用户有权限
|
||
// 4. 所有角色都验证失败,则认为用户无权限
|
||
func check(subs []string, obj string, act string) bool {
|
||
// 使用互斥锁确保权限检查的线程安全
|
||
// 避免并发请求导致 Casbin 引擎状态异常
|
||
checkLock.Lock()
|
||
defer checkLock.Unlock()
|
||
|
||
isPass := false
|
||
|
||
// 遍历用户的所有角色,进行权限检查
|
||
for _, sub := range subs {
|
||
// 调用 Casbin 引擎进行权限验证
|
||
// Enforce(subject, object, action) 检查主体是否有权限对对象执行指定动作
|
||
pass, _ := common.CasbinEnforcer.Enforce(sub, obj, act)
|
||
if pass {
|
||
isPass = true
|
||
break // 只要有一个角色通过验证,就认为有权限
|
||
}
|
||
}
|
||
|
||
return isPass
|
||
}
|