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