跳转至主要内容

Golang RBAC Tutorial: Role-Based Access Control in Gin

本教程为一个用 Go 和 Gin 编写的 REST API 加上基于角色的访问控制(RBAC)。最后,三个角色(admin、editor、viewer)可以对不同路由使用不同的 HTTP 方法,规则放在一个策略文件中,修改时无需改动处理函数。

同样的模型和策略在所有 Casbin 实现中都能使用,所以可以在本教程的 Node.js、Python、Java 和 Rust 版本中复用。 gRPC 服务见 在 Go 中实现 gRPC 授权。

1. 安装​

go mod init example.com/rbac
go get github.com/casbin/casbin/v3 github.com/gin-gonic/gin

2. 编写模型​

模型说明请求是什么样的,以及如何与策略匹配。保存为 model.conf:

[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) && regexMatch(r.act, p.act)
  • r = sub, obj, act:每个请求由用户、URL 路径和 HTTP 方法组成。
  • g = _, _:用户可以被分配到角色。
  • g(r.sub, p.sub):请求的用户必须拥有策略规则中指定的角色。
  • keyMatch2 匹配 /api/articles/:id 这样的路径,regexMatch 让一条规则可以列出多个方法。其他内置匹配函数见 函数。

3. 编写策略​

保存为 policy.csv:

p, admin, /api/*, (GET)|(POST)|(PUT)|(DELETE)
p, editor, /api/articles, (GET)|(POST)
p, editor, /api/articles/:id, (GET)|(PUT)
p, viewer, /api/articles, GET
p, viewer, /api/articles/:id, GET

g, alice, admin
g, bob, editor
g, carol, viewer

以 p 开头的行授予某个角色对某个路径和一组方法的访问权。以 g 开头的行把用户分配到角色。

4. 添加中间件​

中间件用用户、路径和方法调用 Enforce,Casbin 判定为否时以 403 Forbidden 拒绝请求。

main.go
package main

import (
"log"
"net/http"

"github.com/casbin/casbin/v3"
"github.com/gin-gonic/gin"
)

// Authorize asks Casbin whether the current user may call this route.
func Authorize(e *casbin.Enforcer) gin.HandlerFunc {
return func(c *gin.Context) {
// Replace this with the user from your session or JWT.
user := c.GetHeader("X-User")

allowed, err := e.Enforce(user, c.Request.URL.Path, c.Request.Method)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if !allowed {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "forbidden"})
return
}
c.Next()
}
}

func main() {
e, err := casbin.NewEnforcer("model.conf", "policy.csv")
if err != nil {
log.Fatal(err)
}

r := gin.Default()
api := r.Group("/api", Authorize(e))
api.GET("/articles", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"articles": []string{}}) })
api.POST("/articles", func(c *gin.Context) { c.JSON(http.StatusCreated, gin.H{"created": true}) })
api.PUT("/articles/:id", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"updated": c.Param("id")}) })
api.DELETE("/articles/:id", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"deleted": c.Param("id")}) })

log.Fatal(r.Run(":8080"))
}
备注

示例用 X-User 请求头保持简短。 Casbin 只负责授权;实际应用中,用户名来自你的认证层,例如经过校验的 JWT 或会话。

5. 试一试​

go run .
curl -i -X POST   -H "X-User: bob"   http://localhost:8080/api/articles     # 201, editors can create
curl -i -X DELETE -H "X-User: bob" http://localhost:8080/api/articles/1 # 403, editors cannot delete
curl -i -X DELETE -H "X-User: alice" http://localhost:8080/api/articles/1 # 200, admins can
curl -i -X POST -H "X-User: carol" http://localhost:8080/api/articles # 403, viewers are read-only

6. 在运行时修改角色​

角色和权限都是数据,可以在服务器运行时修改:

e.AddRoleForUser("dave", "editor")           // dave becomes an editor
e.DeleteRoleForUser("bob", "editor") // bob is no longer an editor
e.AddPolicy("editor", "/api/comments", "POST") // editors may now post comments

roles, _ := e.GetRolesForUser("alice") // [admin]

完整列表见 RBAC API 和 Management API。

下一步​

  • 把策略存到数据库中。 用 MySQL、PostgreSQL、MongoDB、Redis 等的 适配器 代替 policy.csv。这样通过 API 做的策略修改会自动保存。
  • 运行多个实例。 使用 watcher,这样任何一个实例修改策略后,其他实例都会重新加载。
  • 多租户应用。 带域的 RBAC 让用户在不同租户中拥有不同角色。
  • 基于属性的规则。 把角色与 ABAC 结合,例如“编辑只能修改自己的文章”。 ABAC 教程 一步步演示了这一点。
  • 现成的中间件。 中间件列表 中有 Gin、Echo、Fiber、Chi 等框架的插件。
  • 在浏览器中试验。 把模型和策略粘贴到 在线编辑器,无需运行代码就能测试请求。

Comments are powered by giscus and GitHub Discussions. They load from giscus.app and GitHub only after you click the button below.