GIn + Casbin: RBAC 权限控制系统集成

一、前言

现代 Web 应用的安全体系中,「身份认证」与「权限授权」是两大核心支柱。身份认证确认"你是谁",权限授权决定"你能做什么"。本教程将整合 Gin 框架、JWT 认证与 Casbin RBAC 授权,构建一套完整的双重安全保障方案------通过 JWT 实现无状态身份校验,通过 Casbin RBAC 模型实现精细化权限控制,二者协同工作,既保障接口访问安全,又满足复杂业务场景的权限管理需求。

关于JWT认证,请看往期文章: 《Gin + JWT 认证机制详解:构建安全的Go Web应用》

二、核心技术原理概述

在集成开发前,先明确 JWT、Casbin RBAC 及二者协同逻辑,为后续实战奠定基础。

2.1 JWT 认证原理

JWT(JSON Web Token)是一种轻量级的身份认证令牌,基于 JSON 格式传递用户身份信息,支持无状态校验,无需在服务端存储会话状态,适配分布式、微服务场景。其核心结构分为三部分:

  • Header(头部):指定令牌类型(JWT)和签名算法(如 HS256、RS256);

  • Payload(载荷):存储用户核心信息(如用户 ID、角色、过期时间),非加密字段,不可存敏感数据;

  • Signature(签名):通过头部指定的算法,结合密钥对 Header 和 Payload 加密生成,用于校验令牌完整性和合法性。

JWT 认证流程:用户登录成功后,服务端生成 JWT 令牌返回给客户端;客户端后续请求携带令牌(通常放在 Header 的 Authorization 字段);服务端拦截请求并校验令牌,通过则确认用户身份,失败则拒绝访问。

2.2 Casbin RBAC 授权原理

Casbin 是一款灵活的开源访问控制框架,核心基于 PERM(Policy、Effect、Request、Matchers)元模型,支持 ACL、RBAC、ABAC 等多种权限模型。其中 RBAC(基于角色的访问控制)是企业级应用最常用的模型,通过「用户-角色-权限」的三层映射实现权限批量管理,降低权限维护成本。

Casbin RBAC 核心逻辑:通过模型文件定义权限判定规则,通过策略数据存储具体的「角色-资源-操作」权限关系;当用户发起请求时,Casbin 提取「用户角色、访问资源、操作方式」三元组,匹配策略数据并返回授权结果(允许/拒绝)。

2.3 双重安全协同逻辑

JWT 与 Casbin 协同工作,形成"先认证、后授权"的安全链路:

  1. 客户端携带 JWT 令牌发起请求;

  2. Gin 中间件先执行 JWT 校验:验证令牌合法性、有效性,解析出用户 ID、角色等信息存入上下文;

  3. JWT 校验通过后,执行 Casbin 授权校验:从上下文提取用户角色,结合请求路径(资源)、请求方法(操作),通过 Casbin 判定是否拥有访问权限;

  4. 双重校验均通过,才允许访问接口业务逻辑;任一校验失败,直接返回对应错误信息(401 未认证、403 权限不足)。

三、核心设计

3.1 依赖更新

添加依赖:

go 复制代码
github.com/casbin/casbin/v2
github.com/casbin/gorm-adapter/v3

3.2 RBAC 模型设计

has
has
has
has
User
uint
id
PK
string
username
string
email
string
password
UserRole
uint
user_id
FK
uint
role_id
FK
Role
uint
id
PK
string
name
string
description
RolePermission
uint
role_id
FK
uint
permission_id
FK
Permission
uint
id
PK
string
name
string
path
string
method
string
description

新建角色和权限模型文件,包含:

  • Role 模型:id, name, description, 关联 Users 和 Permissions
  • Permission 模型:id, name, path, method, description
  • UserRole 关联表:user_id, role_id
  • RolePermission 关联表:role_id, permission_id

更新 [User](file:///e:/go_study/13.gin/models/models.go#16-28) 模型,添加 Roles 多对多关系字段。

go 复制代码
// Role 角色模型
type Role struct {
	BaseModel
	Name        string       `gorm:"unique;not null;size:50" json:"name"`
	Description string       `gorm:"size:200" json:"description"`
	Users       []User       `gorm:"many2many:user_roles;" json:"users,omitempty"`
	Permissions []Permission `gorm:"many2many:role_permissions;" json:"permissions,omitempty"`
}

// Permission 权限模型
type Permission struct {
	BaseModel
	Name        string `gorm:"unique;not null;size:100" json:"name"`
	Path        string `gorm:"not null;size:200" json:"path"`
	Method      string `gorm:"not null;size:10" json:"method"`
	Description string `gorm:"size:200" json:"description"`
	Roles       []Role `gorm:"many2many:role_permissions;" json:"roles,omitempty"`
}
go 复制代码
type User struct {
	...
	// 多对多关系:用户可以拥有多个角色
	Roles []Role `gorm:"many2many:user_roles;" json:"roles,omitempty"`
}

3.3 Casbin RBAC 模型定义文件:

创建 RBAC 模型文件,定义请求、策略、角色映射及匹配规则,放在项目根目录:

ini 复制代码
# 请求定义:sub(角色)、obj(资源路径)、act(请求方法)
[request_definition]
r = sub, obj, act

# 策略定义:角色对资源的操作权限
[policy_definition]
p = sub, obj, act

# 角色定义:用户与角色的映射关系
[role_definition]
g = _, _

# 策略效果:存在一条允许规则即通过授权
[policy_effect]
e = some(where (p.eft == allow))

# 匹配器:角色匹配 + 资源路径通配 + 方法匹配
[matchers]
m = g(r.sub, p.sub) && keyMatch2(r.obj, p.obj) && r.act == p.act

说明:这里通过 JWT 直接传递用户角色,简化了「用户-角色」映射步骤;若需支持多用户多角色动态映射,可通过 Casbin 的 g 规则维护映射关系。

3.4 Casbin 初始化:

  • 加载 RBAC 模型配置
  • 使用 GORM Adapter 连接数据库
  • 创建全局 Enforcer 实例
  • 提供策略加载和刷新功能
go 复制代码
// InitCasbin 初始化 Casbin
func InitCasbin() {
	// 使用 GORM Adapter
	adapter, err := gormadapter.NewAdapterByDB(global.DB)
	if err != nil {
		global.Logger.Fatal("Failed to create casbin adapter", zap.Error(err))
	}

	// 加载模型配置
	enforcer, err := casbin.NewEnforcer("rbac_model.conf", adapter)
	if err != nil {
		global.Logger.Fatal("Failed to create casbin enforcer", zap.Error(err))
	}

	// 加载策略
	if err := enforcer.LoadPolicy(); err != nil {
		global.Logger.Fatal("Failed to load casbin policy", zap.Error(err))
	}

	global.CasbinEnforcer = enforcer
	global.Logger.Info("Casbin initialized successfully")
}
go 复制代码
// RefreshCasbinPolicy 刷新 Casbin 策略
func RefreshCasbinPolicy() error {
	if global.CasbinEnforcer == nil {
		return fmt.Errorf("casbin enforcer not initialized")
	}
	return global.CasbinEnforcer.LoadPolicy()
}

// AddRoleForUser 为用户添加角色
func AddRoleForUser(userID uint, roleName string) (bool, error) {
	return global.CasbinEnforcer.AddGroupingPolicy(fmt.Sprintf("user:%d", userID), roleName)
}

// RemoveRoleForUser 移除用户角色
func RemoveRoleForUser(userID uint, roleName string) (bool, error) {
	return global.CasbinEnforcer.RemoveGroupingPolicy(fmt.Sprintf("user:%d", userID), roleName)
}
...

Casbin 权限检查中间件:

  • 从 JWT 上下文获取用户ID
  • 查询用户角色
  • 调用 Casbin Enforcer 检查权限
  • 无权限时返回 403
go 复制代码
func CasbinAuth() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 获取用户ID
		userID, exists := c.Get("userId")
		if !exists {
			c.JSON(http.StatusUnauthorized, gin.H{
				"code":    401,
				"message": "用户未登录",
			})
			c.Abort()
			return
		}

		// 获取请求路径和方法
		path := c.Request.URL.Path
		method := c.Request.Method

		// 构造用户标识
		sub := fmt.Sprintf("user:%d", userID.(uint))

		// 检查权限
		// 在项目启动时(initialize.InitCasbin 中调用 enforcer.LoadPolicy()),Casbin 会把数据库中的所有策略加载到内存中
		// enforcer.Enforce() 是直接在内存中进行匹配校验的,速度非常快,不会产生数据库 IO
		ok, err := global.CasbinEnforcer.Enforce(sub, path, method)
		if err != nil {
			global.Logger.Error("Casbin enforce error: " + err.Error())
			c.JSON(http.StatusInternalServerError, gin.H{
				"code":    500,
				"message": "权限验证失败",
			})
			c.Abort()
			return
		}

		if !ok {
			c.JSON(http.StatusForbidden, gin.H{
				"code":    403,
				"message": "权限不足",
			})
			c.Abort()
			return
		}

		c.Next()
	}
}

API 组件

角色管理 API:

  • POST /role - 创建角色
  • GET /role/list - 获取角色列表
  • GET /role/:id - 获取角色详情
  • PUT /role/:id - 更新角色
  • DELETE /role/:id - 删除角色
  • POST /role/:id/permissions - 设置角色权限
  • POST /user/:id/roles - 设置用户角色
go 复制代码
// SetRolePermissions 设置角色权限
// @Summary 设置角色权限
// @Description 设置角色的权限列表
// @Tags 角色管理
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param id path int true "角色ID"
// @Param req body SetRolePermissionsReq true "权限ID列表"
// @Success 200 {object} res.Response{} "设置成功"
// @Failure 400 {object} res.Response{} "设置失败"
// @Router /role/{id}/permissions [post]
func SetRolePermissions(c *gin.Context) {
	id := c.Param("id")
	var role models.Role

	if err := global.DB.First(&role, id).Error; err != nil {
		res.FailWithMessage(c, "角色不存在")
		return
	}

	var req SetRolePermissionsReq
	if err := c.ShouldBindJSON(&req); err != nil {
		ResponseValidateErr(c, err)
		return
	}

	// 获取权限列表
	var permissions []models.Permission
	if err := global.DB.Where("id IN ?", req.PermissionIDs).Find(&permissions).Error; err != nil {
		res.FailWithMessage(c, "获取权限失败")
		return
	}

	// 更新角色权限关联
	if err := global.DB.Model(&role).Association("Permissions").Replace(permissions); err != nil {
		res.FailWithMessage(c, "设置权限失败: "+err.Error())
		return
	}

	// 更新 Casbin 策略
	// 先删除角色的所有策略
	global.CasbinEnforcer.DeletePermissionsForUser(role.Name)

	// 添加新的策略
	// 当你调用 Casbin 的 API(如 AddPolicy, RemovePolicy 等,时,Casbin 会同时做两件事:
	// 更新内存中的策略缓存
	// 将变更写入数据库(通过 GORM Adapter)
	for _, perm := range permissions {
		global.CasbinEnforcer.AddPolicy(role.Name, perm.Path, perm.Method)
	}

	res.Success(c, nil)
}

权限管理 API:

  • POST /permission - 创建权限
  • GET /permission/list - 获取权限列表
  • GET /permission/:id - 获取权限详情
  • PUT /permission/:id - 更新权限
  • DELETE /permission/:id - 删除权限
go 复制代码
// CreatePermission 创建权限
// @Summary 创建权限
// @Description 创建新权限
// @Tags 权限管理
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param req body CreatePermissionReq true "创建权限请求参数"
// @Success 200 {object} res.Response{} "创建成功"
// @Failure 400 {object} res.Response{} "创建失败"
// @Router /permission [post]
func CreatePermission(c *gin.Context) {
	var req CreatePermissionReq
	if err := c.ShouldBindJSON(&req); err != nil {
		ResponseValidateErr(c, err)
		return
	}

	permission := models.Permission{
		Name:        req.Name,
		Path:        req.Path,
		Method:      req.Method,
		Description: req.Description,
	}

	if err := global.DB.Create(&permission).Error; err != nil {
		res.FailWithMessage(c, "创建权限失败: "+err.Error())
		return
	}

	res.Success(c, permission)
}

路由配置

权限相关路由注册,需要 JWT 认证和管理员权限。

go 复制代码
// InitPermissionRouter 初始化权限路由
func InitPermissionRouter(Router *gin.RouterGroup) {
	permissionRouter := Router.Group("permission").Use(middleware.JWTAuth()).Use(middleware.CasbinRoleAuth(models.RoleAdmin))
	{
		permissionRouter.POST("", api.CreatePermission)
		permissionRouter.GET("list", api.GetPermissionList)
		permissionRouter.GET(":id", api.GetPermission)
		permissionRouter.PUT(":id", api.UpdatePermission)
		permissionRouter.DELETE(":id", api.DeletePermission)
	}
}

post修改需要登录和超管角色

go 复制代码
func InitPostRouter(Router *gin.RouterGroup) {
	postRouter := Router.Group("post")
	{
		postRouter.GET("list", api.GetPosts)
		postRouter.GET("search", api.SearchPosts)
		postRouter.GET(":id", api.GetPostById)
		postRouter.POST("create", middleware.JWTAuth(), middleware.CasbinRoleAuth(models.RoleSuperAdmin), api.CreatePost)
		postRouter.PUT(":id", middleware.JWTAuth(), middleware.CasbinRoleAuth(models.RoleSuperAdmin), api.UpdatePost)
		postRouter.DELETE("id", middleware.JWTAuth(), middleware.CasbinRoleAuth(models.RoleSuperAdmin), api.DeletePost)
	}
}

四、测试验证

  1. 启动服务器

    powershell 复制代码
    cd e:\go_study\13.gin
    go run main.go
  2. 测试角色 API

    • 使用 Postman 或 curl 测试角色的 CRUD 接口
    • 验证需要登录后才能访问
  3. 测试权限控制

    • 创建不同角色的用户
    • 验证不同角色用户访问受限 API 的结果

五、总结

Gin 框架下 JWT 认证与 Casbin RBAC 授权的双重安全集成,通过"身份认证-权限授权"的链路化校验,构建企业级的接口安全体系。JWT 实现了无状态、分布式的身份校验,Casbin 提供了灵活、可扩展的权限控制能力,二者结合既保障了接口访问的安全性,又适配了复杂业务场景的权限管理需求。

参考代码

gitee

相关推荐
蜂蜜黄油呀土豆1 天前
深入了解 JWT:无状态认证与集群部署的解决方案
web安全·jwt·token
王家视频教程图书馆5 天前
go语言 gin grom jwt 登陆token验证 增删改查 分页 完整 图书管理系统
gin
liuyunshengsir6 天前
golang Gin 框架下的大数据量 CSV 流式下载
开发语言·golang·gin
catoop8 天前
基于 JWT/JWE 的跨系统安全数据透传方案
系统安全·jwt·jwe
勇气要爆发9 天前
AI后端工程化:FastAPI + Pydantic + JWT 鉴权实战,从零构建 AI 接口服务
人工智能·fastapi·jwt·pydantic
我不是8神12 天前
gin与gorm框架知识点总结
ios·iphone·gin
西京刀客12 天前
golang路由与框架选型(对比原生net/http、httprouter、Gin)
http·golang·gin
曲幽12 天前
FastAPI登录验证:用OAuth2与JWT构筑你的API安全防线
python·fastapi·web·jwt·token·oauth2