ldap-1-backend/middleware/CasbinMiddleware.go

143 lines
4.2 KiB
Go
Raw Normal View History

2025-08-26 00:43:50 +08:00
/*
Package middleware 包含系统的各种中间件
Casbin 权限控制中间件实现了基于 RBACRole-Based Access Control的权限访问控制模型
该中间件负责
- 验证用户登录状态和账户状态
- 获取用户的角色信息
- 基于角色检查用户对特定资源的访问权限
- 阻止未授权的请求访问受保护的资源
权限控制流程
1. JWT Token 中获取当前登录用户信息
2. 检查用户账户状态是否被禁用
3. 获取用户的所有有效角色
4. 根据请求的 URL HTTP 方法进行权限检查
5. 允许或拒绝请求继续执行
RBAC 模型说明
- Subject主体用户的角色关键字
- Object对象请求的 API 路径
- Action动作HTTP 请求方法GETPOSTPUTDELETE
*/
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
}