Ladon 折腾手记:结合 Gin 开发一个简易 ACL 接口

Xpitz · · 1456 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

文章首发于个人公众号:「阿拉平平」

前段时间试了下用 Gin 编写接口,但是在权限控制这块,网上很多资料都是用 casbin 来实现。网上关于 casbin 的文档良莠不齐,有的甚至看得我一脸懵逼。之后在 V 站上看到有大佬推荐了 Ladon,于是我抽空折腾了下。

文中我将使用 Gin 和 Ladon 实现一个简易的 ACL 接口,通过向接口发送数据判断某用户是否具有操作资源的权限。

简介

Ladon[1] 是一个用于控制访问策略的库,类似于 RBAC 或 ACL,受 AWS IAM 策略启发,由 Golang 编写。

接口

由于 Ladon 本身没有提供 HTTP 服务,所以接口的需要自己实现。这里我用的是 Gin 框架。

首先进入项目目录 Ladon-demo,安装 Gin:

$ cd Ladon-demo
$ go get -u github.com/gin-gonic/gin

创建 main.go 并编写接口,代码如下:

package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run() // listen and serve on 0.0.0.0:8080
}

运行服务并测试接口返回值:

$ go run main.go
$ curl -s http://127.0.0.1:8080/ping
{"message":"pong"}

如果返回结果与我一致,说明接口服务正常。接下来就可以使用 Ladon 进行权限控制了。

概念

通常来说,一条完整的策略可以明确告诉我们:谁在特定环境下可以做什么。

因此,向接口发送的数据需要包含:

  • subject:对应「谁」,即主体。
  • action:对应「可以做」,即操作的种类,比如增删改查。
  • resource:对应「做什么」,即具体操作的资源。
  • context:对应「特定环境」,即一些限制条件,可选。

转换成 JSON 格式,大致如下:

{
  "subject": "users:peter",
  "action" : "delete",
  "resource": "resources:articles:ladon-introduction",
  "context": {
    "remoteIP": "192.168.0.5"
  }
}

后端存储的策略格式则复杂些,不过应该不难理解:

{
  "description": "One policy to rule them all.",
  "subjects": ["users:<peter|ken>", "users:maria", "groups:admins"],
  "actions" : ["delete", "<create|update>"],
  "effect": "allow",
  "resources": [
    "resources:articles:<.*>",
    "resources:printer"
  ],
  "conditions": {
    "remoteIP": {
        "type": "CIDRCondition",
        "options": {
            "cidr": "192.168.0.1/16"
        }
    }
  }
}

实例

老板张三开了家理发店,并且雇佣了理发界三巨头 Tony、Kevin 和 Allen。现在需要为三位老师开通理发业务的权限。

安装导入

安装前确保 Go 版本在 1.11 以上。

$ go get github.com/ory/ladon

导入 Ladon:

import  "github.com/ory/ladon"

创建策略

先创建一个策略:

var pol = &ladon.DefaultPolicy{
    ID: "0",
    Description: "Hair Design",
    Subjects: []string{"<Tony|Kevin|Allen>"},
    Resources: []string{
        "resources:hair",
    },
    Actions: []string{"delete", "<create|update>"},
    Effect: ladon.AllowAccess,
}

说明:

  • ID:策略的标识。
  • Description:策略的描述。
  • Subjects:策略的主体。<> 内为正则表达式。
  • Resources:策略的资源。
  • Actions:策略的操作类型。
  • Effect:AllowAccess 表示允许。DenyAccess 表示拒绝。

添加接口

现在添加一个接口 /check,通过 POST 请求发送数据来验证刚刚创建的策略。

修改 main.go,加入以下代码:

r.POST("/check", func(c *gin.Context) {
    // 声明数据结构体
    accessRequest :=  &ladon.Request{}
    var message string
    // 绑定接口发送的数据
    if err := c.BindJSON(accessRequest); err != nil {
        fmt.Println(err)
    } else {
        // 实例化 warden
        warden := &ladon.Ladon{
            // 数据持久化
            Manager: memory.NewMemoryManager(),
            // 打印审计日志
            AuditLogger: &ladon.AuditLoggerInfo{},
        }
        // 添加策略
        warden.Manager.Create(pol)
        // 判断是否拥有权限
        if err := warden.IsAllowed(accessRequest); err != nil {
            message = "无操作权限"
        } else {
            message = "有操作权限"
        }

        c.JSON(200, gin.H{
            "message": message,
        })
    }
})

需要注意的是,在实例化 warden 时指定了用内存方式做数据持久化,因此需要导入 memory:

import "github.com/ory/ladon/manager/memory"

测试接口

访问 http://127.0.0.1:8080/check 测试接口。

先查看下 Tony 老师有没有权限:

$ curl -s \
>       -X POST \
>       -H "Content-Type: application/json" \
>       -d@- \
>       "http://127.0.0.1:8080/check" <<EOF
>         {
>           "subject": "Tony",
>           "action" : "delete",
>           "resource": "resources:hair"
>         }
> EOF
{"message":"有操作权限"}

再测下张三有没有权限:

$ curl -s \
>       -X POST \
>       -H "Content-Type: application/json" \
>       -d@- \
>       "http://127.0.0.1:8080/check" <<EOF
>         {
>   "subject": "张三",
>   "action" : "delete",
>   "resource": "resources:hair"
> }
> EOF
{"message":"无操作权限"}

可以看到,测试结果符合我们的预期。张老板很开心,但是又提了个需求。

限定条件

张老板提出:现在叫 Tony 的人实在太多了,不能是个 Tony 就给他理发业务的权限。

张老板的需求不无道理,所以我们完善下之前的代码,给发送的数据加个限定条件,比如数据中需要指明 boss。

在 pol 里加入 Conditions:

var pol = &ladon.DefaultPolicy{
    ...
    Conditions: ladon.Conditions{
        "boss": &ladon.StringEqualCondition{
            Equals: "张三",
        },
    },
}

这里的 StringEqualCondition 用于判断字符串是否相等,即发送的数据里 boss 是否等于 张三。当然,Ladon 还自带了其它的限定条件,同时也支持自定义,这里就不展开了。

接下来测试下接口:

$ curl -s \
>       -X POST \
>       -H "Content-Type: application/json" \
>       -d@- \
>       "http://127.0.0.1:8080/check" <<EOF
>         {
>   "subject": "Tony",
>   "action" : "delete",
>   "resource": "resources:hair"
> }
> EOF
{"message":"无操作权限"}

这里没有指定 boss,所以 Tony 老师就没有权限了。现在指定下 boss 再测试下:

$ curl -s \
>       -X POST \
>       -H "Content-Type: application/json" \
>       -d@- \
>       "http://127.0.0.1:8080/check" <<EOF
>         {
>   "subject": "Tony",
>   "action" : "delete",
>   "resource": "resources:hair",
>   "context": {
>     "boss": "张三"
>   }
> }
> EOF
{"message":"有操作权限"}

可以看到,在指定 boss 后,Tony 老师又获得了权限。

结语

有一说一,Ladon 相关的文档的确不多,功能也不如 casbin 强大。但是如果你熟悉 AWS IAM 策略的话,相信很快就能上手,在权限控制上也可以多一种选择。

References

[1] Ladon: https://github.com/ory/ladon


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:Xpitz

查看原文:Ladon 折腾手记:结合 Gin 开发一个简易 ACL 接口

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1456 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传