三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Go Gin 框架怎么实现自定义 RBAC 权限控制的 API 鉴权?

Go Gin 框架怎么实现自定义 RBAC 权限控制的 API 鉴权?

在 Go Gin 框架中实现自定义 RBAC 权限控制,核心是通过中间件(Middleware)拦截请求,结合 JWT Claims 中的用户角色信息与路由所需的权限进行比对。

先说结论:基于 Gin 中间件机制在请求进入 Handler 前完成角色校验,是耦合度最低且易于维护的方案。

  • 适合:需要区分管理员、普通用户等多角色访问的 API 服务
  • 先看:用户身份标识(如 JWT Claims)中是否已包含角色信息
  • 建议:将权限规则配置在外部文件或数据库中,避免硬编码在代码里

核心代码实现

实现分为两步:首先解析 Token 将角色存入 Context,其次编写中间件校验角色。

1. JWT 解析与角色提取

在鉴权中间件中,验证 Token 有效性后将 roles 写入上下文,避免后续重复解析。以下示例展示如何提取 Claims 并存储。

func AuthMiddleware(secretKey string) gin.HandlerFunc {return func(c *gin.Context) {tokenStr := c.GetHeader("Authorization")if tokenStr == "" {c.AbortWithStatusJSON(401, gin.H{"error": "缺失 Token"})return}// 去除 Bearer 前缀tokenStr = strings.TrimPrefix(tokenStr, "Bearer ")// 解析 Token (示例伪代码,需引入 github.com/golang-jwt/jwt/v5)// token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {//     return []byte(secretKey), nil// })// if err != nil || !token.Valid {//     c.AbortWithStatusJSON(401, gin.H{"error": "Token 无效"})//     return// }// 假设 claims 结构体中包含 Roles 字段// claims := token.Claims.(*CustomClaims)// 关键步骤:将角色存入 Contextc.Set("roles", claims.Roles) c.Next()}
}

2. RBAC 鉴权中间件

从 Context 获取角色列表,遍历比对是否包含所需角色。此处补充完整的比对逻辑。

func RoleAuth(requiredRoles ...string) gin.HandlerFunc {return func(c *gin.Context) {roles, exists := c.Get("roles")if !exists {c.AbortWithStatusJSON(401, gin.H{"error": "未登录或角色信息缺失"})return}userRoles, ok := roles.([]string)if !ok {c.AbortWithStatusJSON(500, gin.H{"error": "角色格式错误"})return}// 核心比对逻辑:检查用户角色是否包含任一所需角色hasPermission := falsefor _, r := range userRoles {for _, required := range requiredRoles {if r == required {hasPermission = truebreak}}if hasPermission {break}}if !hasPermission {c.AbortWithStatusJSON(403, gin.H{"error": "权限不足"})return}c.Next()}
}

路由绑定示例

在初始化 Gin 引擎时,将中间件应用到特定路由组。注意中间件的顺序,先 Auth 后 Role。

r := gin.Default()
// 全局应用 Auth 中间件解析 Token
r.Use(AuthMiddleware("my-secret-key"))// 定义需要 admin 角色的路由组
adminGroup := r.Group("/api/admin")
adminGroup.Use(RoleAuth("admin"))
{adminGroup.GET("/users", GetUsers)adminGroup.POST("/users", CreateUser)
}// 定义需要 admin 或 editor 角色的路由
editorGroup := r.Group("/api/content")
editorGroup.Use(RoleAuth("admin", "editor"))
{editorGroup.POST("/article", CreateArticle)
}

验证方法

使用 curl 命令模拟不同场景请求,验证状态码是否符合预期。

场景 1:无 Token 访问

curl -i http://localhost:8080/api/admin/users
# 预期输出:HTTP/1.1 401 Unauthorized

场景 2:普通用户 Token 访问管理员接口

curl -i -H "Authorization: Bearer <user_token>" http://localhost:8080/api/admin/users
# 预期输出:HTTP/1.1 403 Forbidden

场景 3:管理员 Token 访问管理员接口

curl -i -H "Authorization: Bearer <admin_token>" http://localhost:8080/api/admin/users
# 预期输出:HTTP/1.1 200 OK

常见坑与排查

  • Token 有效性优先:必须先验证 Token 签名和过期时间,再校验角色,避免无效 Token 通过角色检查。
  • 角色大小写:建议统一转为小写后再比对,防止 "Admin" 与 "admin" 不一致。
  • Context 键名冲突:确保 c.Set("roles") 的键名在全局唯一,避免被其他中间件覆盖。
  • 性能优化:如果角色数据存储在数据库而非 Token 中,需在中间件内增加 Redis 缓存,避免每次请求查库。

原文链接:https://www.zjcp.cc/ask/11361.html

← 返回列表