Введение
Управление привилегиями является обязательным модулем почти в каждой системе. Если при разработке проекта необходимо один раз реализовать управление разрешениями, это, несомненно, приведет к пустой трате времени и увеличению затрат на разработку. следовательно,casbin
Появилась библиотека.casbin
Это мощная и эффективная библиотека управления доступом. Поддерживает множество широко используемых моделей контроля доступа, таких какACL/RBAC/ABAC
Ждать.可以实现灵活的访问权限控制。 в то же время,casbin
Поддерживает несколько языков программирования,Go/Java/Node/PHP/Python/.NET/Rust
.我们只需要Узнай один раз, используй много.
быстрый в использовании
Мы по-прежнему используем Go Module для написания кода, сначала инициализируем:
$ mkdir casbin && cd casbin
$ go mod init github.com/darjun/go-daily-lib/casbin
Затем установитеcasbin
,В настоящее времяv2
Версия:
$ go get github.com/casbin/casbin/v2
Разрешение действительно контролируетВОЗдакакой ресурсчто делать.casbin
Абстрактная модель для контроля доступа на основе Action Perm (политика, эффект, эффект, запрос, сопоставление) профиль метамодели (файл модели). Таким образом, коммутатор или обновление механизмов авторизации просто нужно изменить файл конфигурации.
policy
Это определение политики или правил. Он определяет конкретные правила.
request
это абстракция для запросов доступа, которая связана сe.Enforce()
Параметры функции имеют взаимно однозначное соответствие
matcher
Сопоставитель сопоставит запрос с каждым из определенныхpolicy
Сопоставьте один за другим, чтобы получить несколько совпадающих результатов.
effect
На основе агрегирования всех результатов применения сопоставителя к запросу принимается решение о том, является ли запроспозволятьвсе ещеМусор.
Следующая диаграмма хорошо изображает этот процесс:
Начнем с написания файла модели:
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
[policy_effect]
e = some(where (p.eft == allow))
В приведенном выше файле модели указано, что разрешения предоставляютсяsub,obj,act
Он состоит из трех элементов, и запрос может быть передан только в том случае, если в списке политик есть точно такая же политика, как и она. Результат сопоставления можно получить с помощьюp.eft
Получать,some(where (p.eft == allow))
Указывает, что только одна политика разрешает это.
Затем наш файл политики (т.е. кто что может делать с каким ресурсом):
p, dajun, data1, read
p, lizi, data2, write
вышеpolicy.csv
Две строки файла представляютdajun
к даннымdata1
имеютread
Орган власти,lizi
к даннымdata2
имеютwrite
разрешения.
Далее используется код:
package main
import (
"fmt"
"log"
"github.com/casbin/casbin/v2"
)
func check(e *casbin.Enforcer, sub, obj, act string) {
ok, _ := e.Enforce(sub, obj, act)
if ok {
fmt.Printf("%s CAN %s %s\n", sub, act, obj)
} else {
fmt.Printf("%s CANNOT %s %s\n", sub, act, obj)
}
}
func main() {
e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
if err != nil {
log.Fatalf("NewEnforecer failed:%v\n", err)
}
check(e, "dajun", "data1", "read")
check(e, "lizi", "data2", "write")
check(e, "dajun", "data1", "write")
check(e, "dajun", "data2", "read")
}
Код на самом деле не сложный. Сначала создайтеcasbin.Enforcer
объект, загрузить файл моделиmodel.conf
и файлы политикиpolicy.csv
, позвони егоEnforce
Метод проверки разрешений. Запустите программу:
$ go run main.go
dajun CAN read data1
lizi CAN write data2
dajun CANNOT write data1
dajun CANNOT read data2
Запрос должен точно соответствовать политике для прохождения.("dajun", "data1", "read")
совпадениеp, dajun, data1, read
,("lizi", "data2", "write")
совпадениеp, lizi, data2, write
, поэтому первые две проверки проходят. 3-й, потому что"dajun"
без правdata1
изwrite
разрешения, 4-й, потому чтоdajun
правильноdata2
нетread
разрешения, поэтому проверка не проходит. Результат соответствует ожиданиям.
sub/obj/act
соответственноEnforce
Три параметра метода. На самом деле здесьsub/obj/act
иread/write/data1/data2
Я выбрал его наугад, вы можете использовать любое другое имя, если оно соответствует.
То, что достигается в приведенном выше примере,ACL
(список контроля доступа, список контроля доступа).ACL
Разрешения каждого субъекта на каждый ресурс определены, а те, которые не определены, не имеют разрешений. Мы также можем добавить суперадминистраторов, суперадминистраторы могут делать все что угодно. Предположим, что суперадминистраторroot
, нам просто нужно изменить сопоставитель:
[matchers]
e = r.sub == p.sub && r.obj == p.obj && r.act == p.act || r.sub == "root"
до тех пор, пока принцип доступаroot
Всегда отпускай.
проверять:
func main() {
e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
if err != nil {
log.Fatalf("NewEnforecer failed:%v\n", err)
}
check(e, "root", "data1", "read")
check(e, "root", "data2", "write")
check(e, "root", "data1", "execute")
check(e, "root", "data3", "rwx")
}
так какsub = "root"
, сопоставитель должен пройти, и результат:
$ go run main.go
root CAN read data1
root CAN write data2
root CAN execute data1
root CAN rwx data3
модель RBAC
ACL
Нет проблем с моделью, когда пользователей и ресурсов мало, а количество пользователей и ресурсов велико.ACL
становится чрезвычайно громоздким. Представьте, как больно сбрасывать нужные ему разрешения каждый раз, когда добавляется новый пользователь.RBAC
(ролевой контроль доступа) путем введения ролей (role
) этот средний слой, чтобы решить эту проблему. Каждый пользователь принадлежит определенной роли, такой как разработчик, администратор, эксплуатация и техническое обслуживание и т. д. Каждая роль имеет свои определенные разрешения, а разрешения добавляются и удаляются с помощью ролей. При добавлении пользователя таким способом нам нужно только назначить ему роль, и он может иметь все разрешения роли. При изменении разрешений роли права пользователей, принадлежащих к этой роли, будут соответствующим образом изменены.
существуетcasbin
используется вRBAC
Модель нужно добавить в файл моделиrole_definition
Модуль:
[role_definition]
g = _, _
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
g = _,_
Определяет отношение пользователь-роль, отображение роли-роли, первый является членом второго и имеет разрешения последнего. Тогда в матчере нам не нужно судитьr.sub
иp.sub
Полный равный, просто нужно использоватьg(r.sub, p.sub)
определить предмет запросаr.sub
Принадлежит ли этоp.sub
Эта роль подойдет. Наконец, мы модифицируем файл политики, чтобы добавить определение роли пользователя:
p, admin, data, read
p, admin, data, write
p, developer, data, read
g, dajun, admin
g, lizi, developer
вышеpolicy.csv
В документе указывается,dajun
принадлежатьadmin
администратор,lizi
принадлежатьdeveloper
разработчики, используйтеg
определить это отношение. Кроме тогоadmin
к даннымdata
использоватьread
иwrite
Разрешения,developer
к даннымdata
Толькоread
разрешения.
package main
import (
"fmt"
"log"
"github.com/casbin/casbin/v2"
)
func check(e *casbin.Enforcer, sub, obj, act string) {
ok, _ := e.Enforce(sub, obj, act)
if ok {
fmt.Printf("%s CAN %s %s\n", sub, act, obj)
} else {
fmt.Printf("%s CANNOT %s %s\n", sub, act, obj)
}
}
func main() {
e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
if err != nil {
log.Fatalf("NewEnforecer failed:%v\n", err)
}
check(e, "dajun", "data", "read")
check(e, "dajun", "data", "write")
check(e, "lizi", "data", "read")
check(e, "lizi", "data", "write")
}
очевидноlizi
нет ролиwrite
Разрешения:
dajun CAN read data
dajun CAN write data
lizi CAN read data
lizi CANNOT write data
несколькоRBAC
casbin
Поддержка одновременного существования несколькихRBAC
Системы, т.е. пользователи и ресурсы, имеют роли:
[role_definition]
g=_,_
g2=_,_
[matchers]
m = g(r.sub, p.sub) && g2(r.obj, p.obj) && r.act == p.act
Приведенный выше файл модели определяет дваRBAC
системаg
иg2
, мы используем в матчереg(r.sub, p.sub)
Определить принадлежность предмета запроса к определенной группе,g2(r.obj, p.obj)
Судя по тому, что запрошенный ресурс принадлежит к определенной группе и операция согласована, его можно освободить.
Политический файл:
p, admin, prod, read
p, admin, prod, write
p, admin, dev, read
p, admin, dev, write
p, developer, dev, read
p, developer, dev, write
p, developer, prod, read
g, dajun, admin
g, lizi, developer
g2, prod.data, prod
g2, dev.data, dev
Посмотрите сначала на ролевые отношения, то есть на последние 4 строки,dajun
принадлежатьadmin
Роль,lizi
принадлежатьdeveloper
Роль,prod.data
продуктивные ресурсыprod
Роль,dev.data
ресурсы развитияdev
Роль.admin
роль имеет паруprod
иdev
права на чтение и запись для ресурсов класса,developer
только имеют правоdev
права на чтение и запись иprod
разрешение на чтение.
check(e, "dajun", "prod.data", "read")
check(e, "dajun", "prod.data", "write")
check(e, "lizi", "dev.data", "read")
check(e, "lizi", "dev.data", "write")
check(e, "lizi", "prod.data", "write")
в первой функцииe.Enforce()
Метод получается первым, когда он фактически выполняетсяdajun
собственная рольadmin
, затем получитьprod.data
собственная рольprod
, согласно первой строке в файлеp, admin, prod, read
Разрешить запрос. в последней функцииlizi
принадлежать к ролиdeveloper
,иprod.data
принадлежать к ролиprod
, все политики запрещают это, поэтому запрос отклоняется:
dajun CAN read prod.data
dajun CAN write prod.data
lizi CAN read dev.data
lizi CAN write dev.data
lizi CANNOT write prod.data
многослойные роли
casbin
Также возможно определить роль владельца для роли, чтобы реализовать многоуровневое отношение ролей, и это отношение разрешений может быть передано. Напримерdajun
Это продвинутый разработчикsenior
,seinor
принадлежит разработчику, тоdajun
Он также принадлежит разработчику и имеет все права разработчика. Мы можем определить разрешения, общие для разработчиков, а затем дополнительно дляsenior
Определите некоторые специальные разрешения.
Файл модели изменять не нужно, а файл политики модифицируется следующим образом:
p, senior, data, write
p, developer, data, read
g, dajun, senior
g, senior, developer
g, lizi, developer
вышеpolicy.csv
файл определяет продвинутого разработчикаsenior
к даннымdata
имеютwrite
Разрешения, обычные разработчикиdeveloper
только для данныхread
разрешения. в то же времяsenior
Слишкомdeveloper
,такsenior
также унаследовалread
разрешения.dajun
принадлежатьsenior
,такdajun
правильноdata
имеютread
иwrite
разрешения, покаlizi
Принадлежатьdeveloper
, для данныхdata
Толькоread
разрешения.
check(e, "dajun", "data", "read")
check(e, "dajun", "data", "write")
check(e, "lizi", "data", "read")
check(e, "lizi", "data", "write")
RBAC
domain
существуетcasbin
, роли могут быть глобальными или конкретнымиdomain
(поле) илиtenant
(арендатор), что может быть просто понято какГруппа. Напримерdajun
в группеtenant1
Середина — это администратор с относительно высоким авторитетом вtenant2
Может просто брат.
использоватьRBAC domain
Требуются следующие изменения в файле модели:
[request_definition]
r = sub, dom, obj, act
[policy_definition]
p = sub, dom, obj, act
[role_definition]
g = _,_,_
[matchers]
m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.obj
g=_,_,_
Указывает, что первый имеет промежуточную роль в последнем, используемом в сопоставленииg
принестиdom
.
p, admin, tenant1, data1, read
p, admin, tenant2, data2, read
g, dajun, admin, tenant1
g, dajun, developer, tenant2
существуетtenant1
, Толькоadmin
данные можно прочитатьdata1
. существуетtenant2
, Толькоadmin
данные можно прочитатьdata2
.dajun
существуетtenant1
средний даadmin
,Но когдаtenant2
Китай нет.
func check(e *casbin.Enforcer, sub, domain, obj, act string) {
ok, _ := e.Enforce(sub, domain, obj, act)
if ok {
fmt.Printf("%s CAN %s %s in %s\n", sub, act, obj, domain)
} else {
fmt.Printf("%s CANNOT %s %s in %s\n", sub, act, obj, domain)
}
}
func main() {
e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
if err != nil {
log.Fatalf("NewEnforecer failed:%v\n", err)
}
check(e, "dajun", "tenant1", "data1", "read")
check(e, "dajun", "tenant2", "data2", "read")
}
Результат оказался неожиданным:
dajun CAN read data1 in tenant1
dajun CANNOT read data2 in tenant2
ABAC
RBAC
Модели полезны для реализации относительно регулярного, относительно статического управления правами. Но для особых, динамичных потребностей,RBAC
Это немного немного силы. Например, у нас есть данные в разные периоды времениdata
Внедрить различные элементы управления разрешения. нормальное рабочее время9:00-18:00
Каждый может читать и писатьdata
, в других случаях только владелец данных может читать и писать. Мы можем легко использовать этоABAC
(список доступа к базе атрибутов) модель завершает:
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[matchers]
m = r.sub.Hour >= 9 && r.sub.Hour < 18 || r.sub.Name == r.obj.Owner
[policy_effect]
e = some(where (p.eft == allow))
Это правило не требует файла политики:
type Object struct {
Name string
Owner string
}
type Subject struct {
Name string
Hour int
}
func check(e *casbin.Enforcer, sub Subject, obj Object, act string) {
ok, _ := e.Enforce(sub, obj, act)
if ok {
fmt.Printf("%s CAN %s %s at %d:00\n", sub.Name, act, obj.Name, sub.Hour)
} else {
fmt.Printf("%s CANNOT %s %s at %d:00\n", sub.Name, act, obj.Name, sub.Hour)
}
}
func main() {
e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
if err != nil {
log.Fatalf("NewEnforecer failed:%v\n", err)
}
o := Object{"data", "dajun"}
s1 := Subject{"dajun", 10}
check(e, s1, o, "read")
s2 := Subject{"lizi", 10}
check(e, s2, o, "read")
s3 := Subject{"dajun", 20}
check(e, s3, o, "read")
s4 := Subject{"lizi", 20}
check(e, s4, o, "read")
}
очевидноlizi
существует20:00
не можетread
данныеdata
:
dajun CAN read data at 10:00
lizi CAN read data at 10:00
dajun CAN read data at 20:00
lizi CANNOT read data at 20:00
мы знаем, что вmodel.conf
файл черезr.sub
иr.obj
,r.act
к доступуEnforce
параметры метода. Фактическиsub/obj
Может быть структурным объектом, благодаряgovaluate
Сила библиотеки, мы можемmodel.conf
Получить значения полей этих структур в файле. как указано вышеr.sub.Name
,r.Obj.Owner
Ждать.govaluate
Содержимое библиотеки можете посмотреть в моей предыдущей статье."оценка Go Daily Library".
использоватьABAC
Модели допускают очень гибкое управление разрешениями, но в целомRBAC
Достаточно.
Хранение моделей
В приведенном выше коде мы сохраняем модель в файле.casbin
Также можно динамически инициализировать модель в коде, например.get-started
Пример можно переписать так:
func main() {
m := model.NewModel()
m.AddDef("r", "r", "sub, obj, act")
m.AddDef("p", "p", "sub, obj, act")
m.AddDef("e", "e", "some(where (p.eft == allow))")
m.AddDef("m", "m", "r.sub == g.sub && r.obj == p.obj && r.act == p.act")
a := fileadapter.NewAdapter("./policy.csv")
e, err := casbin.NewEnforcer(m, a)
if err != nil {
log.Fatalf("NewEnforecer failed:%v\n", err)
}
check(e, "dajun", "data1", "read")
check(e, "lizi", "data2", "write")
check(e, "dajun", "data1", "write")
check(e, "dajun", "data2", "read")
}
Точно так же мы можем загружать модели из строк:
func main() {
text := `
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
`
m, _ := model.NewModelFromString(text)
a := fileadapter.NewAdapter("./policy.csv")
e, _ := casbin.NewEnforcer(m, a)
check(e, "dajun", "data1", "read")
check(e, "lizi", "data2", "write")
check(e, "dajun", "data1", "write")
check(e, "dajun", "data2", "read")
}
Но эти два метода не рекомендуются.
Хранение политик
В предыдущих примерах мы сохранили политику вpolicy.csv
в файле. Как правило, в практических приложениях хранилище файлов редко используется.casbin
Поддерживает различные методы хранения в стороннем адаптереMySQL/MongoDB/Redis/Etcd
И так далее, вы также можете реализовать собственное хранилище. Полный список смотрите здесьпотрите на guest.org/docs/en/ada…. Ниже мы вводим использованиеGorm Adapter
. Сначала подключитесь к базе данных и выполните следующееSQL
:
CREATE DATABASE IF NOT EXISTS casbin;
USE casbin;
CREATE TABLE IF NOT EXISTS casbin_rule (
p_type VARCHAR(100) NOT NULL,
v0 VARCHAR(100),
v1 VARCHAR(100),
v2 VARCHAR(100),
v3 VARCHAR(100),
v4 VARCHAR(100),
v5 VARCHAR(100)
);
INSERT INTO casbin_rule VALUES
('p', 'dajun', 'data1', 'read', '', '', ''),
('p', 'lizi', 'data2', 'write', '', '', '');
затем используйтеGorm Adapter
нагрузкаpolicy
,Gorm Adapter
Использовать по умолчаниюcasbin
в библиотекеcasbin_rule
поверхность:
package main
import (
"fmt"
"github.com/casbin/casbin/v2"
gormadapter "github.com/casbin/gorm-adapter/v2"
_ "github.com/go-sql-driver/mysql"
)
func check(e *casbin.Enforcer, sub, obj, act string) {
ok, _ := e.Enforce(sub, obj, act)
if ok {
fmt.Printf("%s CAN %s %s\n", sub, act, obj)
} else {
fmt.Printf("%s CANNOT %s %s\n", sub, act, obj)
}
}
func main() {
a, _ := gormadapter.NewAdapter("mysql", "root:12345@tcp(127.0.0.1:3306)/")
e, _ := casbin.NewEnforcer("./model.conf", a)
check(e, "dajun", "data1", "read")
check(e, "lizi", "data2", "write")
check(e, "dajun", "data1", "write")
check(e, "dajun", "data2", "read")
}
бегать:
dajun CAN read data1
lizi CAN write data2
dajun CANNOT write data1
dajun CANNOT read data2
использовать функцию
Мы можем использовать функции в сопоставителях.casbin
некоторые встроенные функцииkeyMatch/keyMatch2/keyMatch3/keyMatch4
все совпадающие пути URL,regexMatch
Используя обычное сопоставление,ipMatch
Совпадение IP-адресов. видетьпотрите на guest.org/docs/en/women…. Мы используем встроенные функции, которые можно легко направить на разделение полномочий:
[matchers]
m = r.sub == p.sub && keyMatch(r.obj, p.obj) && r.act == p.act
p, dajun, user/dajun/*, read
p, lizi, user/lizi/*, read
Разные пользователи могут получить доступ только к URL-адресам под соответствующими маршрутами:
func main() {
e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
if err != nil {
log.Fatalf("NewEnforecer failed:%v\n", err)
}
check(e, "dajun", "user/dajun/1", "read")
check(e, "lizi", "user/lizi/2", "read")
check(e, "dajun", "user/lizi/1", "read")
}
вывод:
dajun CAN read user/dajun/1
lizi CAN read user/lizi/2
dajun CANNOT read user/lizi/1
Конечно, мы также можем определить наши собственные функции. Сначала определите функцию, которая возвращает bool:
func KeyMatch(key1, key2 string) bool {
i := strings.Index(key2, "*")
if i == -1 {
return key1 == key2
}
if len(key1) > i {
return key1[:i] == key2[:i]
}
return key1 == key2[:i]
}
Здесь реализовано простое регулярное совпадение, только обработка*
.
Тогда используйте эту функциюinterface{}
Тип оборачивает один слой:
func KeyMatchFunc(args ...interface{}) (interface{}, error) {
name1 := args[0].(string)
name2 := args[1].(string)
return (bool)(KeyMatch(name1, name2)), nil
}
Затем добавил в центр сертификации, используя:
e.AddFunction("my_func", KeyMatchFunc)
Таким образом, мы можем использовать эту функцию в сопоставителе для достижения регулярного сопоставления:
[matchers]
m = r.sub == p.sub && my_func(r.obj, p.obj) && r.act == p.act
Затем мы устанавливаем файл политики какdajun
Предоставить разрешения:
p, dajun, data/*, read
dajun
шаблон сопоставления парdata/*
файлы имеютread
разрешения.
Подтвердите это:
check(e, "dajun", "data/1", "read")
check(e, "dajun", "data/2", "read")
check(e, "dajun", "data/1", "write")
check(e, "dajun", "mydata", "read")
dajun
правильноdata/1
нетwrite
разрешения,mydata
несовместимыйdata/*
режим, ниread
Разрешения:
dajun CAN read data/1
dajun CAN read data/2
dajun CANNOT write data/1
dajun CANNOT read mydata
Суммировать
casbin
Мощный, простой и эффективный, а также универсальный на нескольких языках. стоит учиться.
Если вы найдете забавную и простую в использовании языковую библиотеку Go, вы можете отправить сообщение о проблеме в ежедневной библиотеке Go GitHub😄
Ссылаться на
- кабин GitHub:GitHub.com/Wipe Guest/Протрите тканью…
- официальный сайт кабина:casbin.org/
- Язык описания политики управления доступом на основе метамодели:Ооо, ооо.Жо скажем .org.To/HTML/2020/2...
- Перейти на ежедневный репозиторий GitHub:GitHub.com/Darenjun/go-of…
я
мой блог:darjun.github.io
Добро пожаловать, чтобы обратить внимание на мою общедоступную учетную запись WeChat [GoUpUp], учитесь вместе и добивайтесь прогресса вместе ~