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

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

前段时间试了下用 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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值