纯内存+中间件可实现轻量rbac,关键是在jwt解析后立即校验预加载的权限列表,按资源动作组合(如users:post)匹配,支持通配符、分组路由绑定、内存缓存及格式一致性校验。

直接用 gin 做 RBAC 不需要引入 casbin 或其他重型库,只要角色粒度是“接口级”(比如 GET /api/users 需要 user:read),且角色层级不深(无继承、无动态策略),纯内存 + 中间件就能跑通。关键不是“能不能做”,而是“别把权限校验逻辑塞进 handler 里”。
怎么定义角色和权限映射关系
权限本质是「资源+动作」的组合,比如 user:delete、order:list。角色只是这些权限的容器,不建议在代码里硬编码所有角色,而应从存储加载:
-
role_id=1(管理员)→["user:read", "user:write", "order:*"] -
role_id=2(客服)→["order:list", "order:read"] - 通配符
*表示该资源下全部动作,但需在中间件里显式支持,不能依赖字符串strings.Contains粗暴匹配 - 数据库中建议用中间表
role_permissions(role_id, perm_code),避免 JSON 字段导致查询和索引失效
AuthMiddleware 怎么写才不漏判
中间件必须在解析 JWT 后立即校验权限,且不能只比对角色名(如 "admin"),而要查该角色实际拥有的 perm_code 列表。常见错误是:拿到 role_id 后没查权限,直接放行。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 从
c.Get("user")取出解析后的claims,确保含role_id字段(不是role_name) - 调用
rbacManager.GetUserPermissions(uint(claims["role_id"].(float64)))获取权限列表 - 构造当前请求的
permCode:比如fmt.Sprintf("%s:%s", strings.TrimPrefix(c.Request.URL.Path, "/api/"), strings.ToLower(c.Request.Method))→users:post - 逐个比对权限项,支持
users:*匹配users:post,但users:*不能匹配user:post(注意复数/单数) - 校验失败时返回
403,并c.Abort(),避免后续 handler 执行
路由注册时怎么绑定权限
不要在每个 GET/POST 调用里重复写 Use(AuthMiddleware("admin")),而应按权限维度分组:
- 用
r.Group("/api/admin").Use(AuthMiddleware("admin"))统一拦截管理端 - 普通用户接口用
r.Group("/api/user").Use(AuthMiddleware("user")) - 若同一组路由需要不同权限(如
/api/orders允许user:read,但/api/orders/export需user:export),就得拆成子组或单独加中间件 - 超管绕过校验?别写
if isSuper { c.Next() },而是把super:*:*这类通配权限预加载进其权限列表,保持校验逻辑统一
为什么内存缓存权限列表很重要
每次请求都查 DB 或 Redis 加载角色权限,QPS 上千时会成为瓶颈。真实项目中必须缓存:
- 权限数据变更不频繁,可用
sync.Map缓存map[roleID][]string - 设置 TTL(如 5 分钟),配合后台更新时主动
Delete对应 key - 避免用全局变量存整个
rbacManager实例,而应注入到 handler 或中间件闭包中,方便单元测试替换 - 如果用 MySQL,记得给
role_permissions.role_id加索引,否则WHERE role_id = ?会全表扫
最易被忽略的一点:权限字符串的格式一致性。前端传的 resource 和后端拼的 permCode 必须严格一致——比如路由是 /v1/users,就别拼成 user:list,而应统一为 users:list。大小写、复数、路径前缀都要约定死,否则权限永远不生效。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










