一、前言
现代 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 协同工作,形成"先认证、后授权"的安全链路:
-
客户端携带 JWT 令牌发起请求;
-
Gin 中间件先执行 JWT 校验:验证令牌合法性、有效性,解析出用户 ID、角色等信息存入上下文;
-
JWT 校验通过后,执行 Casbin 授权校验:从上下文提取用户角色,结合请求路径(资源)、请求方法(操作),通过 Casbin 判定是否拥有访问权限;
-
双重校验均通过,才允许访问接口业务逻辑;任一校验失败,直接返回对应错误信息(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 和 PermissionsPermission模型:id, name, path, method, descriptionUserRole关联表:user_id, role_idRolePermission关联表: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)
}
}
四、测试验证
-
启动服务器
powershellcd e:\go_study\13.gin go run main.go -
测试角色 API
- 使用 Postman 或 curl 测试角色的 CRUD 接口
- 验证需要登录后才能访问
-
测试权限控制
- 创建不同角色的用户
- 验证不同角色用户访问受限 API 的结果

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