概念

权限管理几乎是每个系统或者服务都会直接或者间接涉及的部分. 权限管理保障了资源(大部分时候就是数据)的安全, 权限管理一般都是和业务强关联, 每当有新的业务或者业务变化时, 不能将精力完全放在业务实现上, 权限的调整往往耗费大量的精力. 其实, 权限的本质没有那么复杂, 只是对访问的控制而已, 有一套完善的访问控制接口, 再加上简单的权限模型. 权限模型之所以能够简单, 就是因为权限管理本身并不复杂, 只是在和具体业务结合时, 出现了各种各样的访问控制场景, 才显得复杂.

PERM 模型

PERM(Policy, Effect, Request, Matchers)模型很简单, 但是反映了权限的本质 – 访问控制

  1. Policy: 定义权限的规则
  2. Effect: 定义组合了多个 Policy 之后的结果, allow/deny
  3. Request: 访问请求, 也就是谁想操作什么
  4. Matcher: 判断 Request 是否满足 Policy

casbin的作用

  1. 以经典{subject, object, action}形式或您定义的自定义形式实施策略,同时支持允许和拒绝授权。
  2. 处理访问控制模型及其策略的存储。
  3. 管理角色用户映射和角色角色映射(RBAC中的角色层次结构)。
  4. 支持内置的超级用户,例如root或administrator。超级用户可以在没有显式权限的情况下执行任何操作。
  5. 多个内置运算符支持规则匹配。例如,keyMatch可以将资源键映射/foo/bar到模式/foo*。

casbin不执行的操作

  1. 身份验证(又名验证username以及password用户登录时)
  2. 管理用户或角色列表。我相信项目本身管理这些实体会更方便。用户通常具有其密码,而Casbin并非设计为密码容器。但是,Casbin存储RBAC方案的用户角色映射。

在使用Casbin 控制后台接口时使用以下模型

[request_definition]
r = sub, obj, act
# 请求的规则
# r 是规则的名称,sub 为请求的实体,obj 为资源的名称, act 为请求的实际操作动作
[policy_definition]
p = sub, obj, act
# 策略的规则
# 同请求
[role_definition]
g = _, _
# 角色的定义
# g 角色的名称,第一个位置为用户,第二个位置为角色,第三个位置为域(在多租户场景下使用)
[policy_effect]
e = some(where (p.eft == allow))
# 任意一条 policy rule 满足, 则最终结果为 allow
[matchers]
m = g(r.sub, p.sub) == true \
&& keyMatch2(r.obj, p.obj) == true \
&& regexMatch(r.act, p.act) == true \
|| r.sub == "root"
# [matchers] 也可以这样写
# m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act || r.sub == "root"
# 前三个用来匹配上面定义的请求的规则, 最后一个或条件为:如果实体是root 直接通过, 不验证权限

RBAC模型的示例策略如下:

p, cityAdmin, /city, GET
p, cityAdmin, /city, POST
p, countyAdmin, /county, GET
g, mayanan, superAdmin

在理解了Casbin 的工作原理后,实际写代码测试一下

需要使用的外部包

go get -u github.com/casbin/casbin  Casbin 官方库
go get -u github.com/casbin/gorm-adapter Casbin 插件,用来将规则和策略保存到数据库中
go get -u github.com/gin-gonic/gin Go Web 框架
go get -u github.com/go-sql-driver/mysql Go MySQL 驱动

方案一

参考链接

点击查看代码
package main

import (
"fmt"
"github.com/casbin/casbin"
"github.com/casbin/gorm-adapter"
"github.com/gin-gonic/gin"
_ "github.com/go-sql-driver/mysql"
"net/http"
) func main() {
// 要使用自己定义的数据库rbac_db,最后的true很重要.默认为false,使用缺省的数据库名casbin,不存在则创建
a := gormadapter.NewAdapter("mysql", "root:pwdZ@tcp(rm-xxxx.mysql.rds.aliyuncs.com:33016)/my_casbin?charset=utf8mb4&parseTime=True&loc=Local", true)
e := casbin.NewEnforcer("./test/model.conf", a)
// 从DB加载策略
e.LoadPolicy() //获取router路由对象
r := gin.New() r.POST("/api/v1/add", func(c *gin.Context) {
fmt.Println("增加Policy")
// AddPolicy 向当前策略添加授权规则。如果规则已经存在,函数返回false,不会添加规则。否则,该函数通过添加新规则返回 true
if ok := e.AddPolicy("admin", "/api/v1/hello", "GET"); !ok {
fmt.Println("Policy已经存在")
} else {
fmt.Println("增加成功")
}
})
//删除policy
r.DELETE("/api/v1/delete", func(c *gin.Context) {
fmt.Println("删除Policy")
// RemovePolicy 从当前策略中删除授权规则。
if ok := e.RemovePolicy("admin", "/api/v1/hello", "GET"); !ok {
fmt.Println("Policy不存在")
} else {
fmt.Println("删除成功")
}
})
//获取policy
r.GET("/api/v1/get", func(c *gin.Context) {
fmt.Println("查看policy")
// GetPolicy 获取策略中的所有授权规则。
list := e.GetPolicy()
for _, vlist := range list {
for _, v := range vlist {
fmt.Printf("value: %s, ", v)
}
}
})
//使用自定义拦截器中间件
r.Use(Authorize(e))
//创建请求
r.GET("/api/v1/hello", func(c *gin.Context) {
fmt.Println("Hello 接收到GET请求..")
}) r.Run(":9000") //参数为空 默认监听8080端口
} //拦截器
func Authorize(e *casbin.Enforcer) gin.HandlerFunc {
return func(c *gin.Context) {
//获取请求的URI
obj := c.Request.URL.RequestURI()
//获取请求方法
act := c.Request.Method
//获取用户的角色
sub := "admin" //判断策略中是否存在
if ok := e.Enforce(sub, obj, act); ok {
fmt.Println("恭喜您,权限验证通过")
c.Next()
} else {
fmt.Println("很遗憾,权限验证没有通过")
c.Abort()
c.String(http.StatusUnauthorized, "无权访问")
}
}
}

方案二:

参考链接

点击查看代码
package main

import (
"fmt"
"github.com/casbin/casbin"
"github.com/casbin/gorm-adapter"
"github.com/gin-gonic/gin"
_ "github.com/go-sql-driver/mysql"
"github.com/jinzhu/gorm"
"net/http" //"gorm.io/gorm"
) // 统一响应结构体
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
} var O *gorm.DB
var PO *gormadapter.Adapter
var Enforcer *casbin.Enforcer func ping(c *gin.Context) {
var response Response
response.Code = 0
response.Message = "success"
response.Data = ""
c.JSON(200, response)
return
} // 数据库连接及角色规则的初始化
func connect() {
dsn := "root:xxx@tcp(rm-xxx.mysql.rds.aliyuncs.com:33016)/my_casbin?charset=utf8mb4&parseTime=True&loc=Local"
var err error
O, err = gorm.Open("mysql", dsn)
if err != nil {
fmt.Println("connect DB error")
panic(err)
}
// 将数据库连接同步给插件, 插件用来操作数据库
PO = gormadapter.NewAdapterByDB(O)
// 这里也可以使用原生字符串方式
Enforcer = casbin.NewEnforcer("./test/model.conf", PO)
// 开启权限认证日志
Enforcer.EnableLog(true)
// 加载数据库中的策略
err = Enforcer.LoadPolicy()
if err != nil {
fmt.Println("loadPolicy error")
panic(err)
}
// 创建一个角色,并赋于权限
// admin 这个角色可以用 GET 方式访问 /api/v2/ping
res := Enforcer.AddPolicy("admin", "/api/v2/ping", "GET")
if !res {
fmt.Println("policy is exist")
} else {
fmt.Println("policy is not exist, adding")
}
// 将 test 用户加入一个角色中
Enforcer.AddRoleForUser("test", "root")
Enforcer.AddRoleForUser("tom", "admin")
// 请看规则中如果用户名为 root 则不受限制
} func main() {
defer O.Close()
connect()
g := gin.Default()
// 这里的接口没有使用权限认证中间件
version1 := g.Group("/api/v1")
{
version1.GET("/ping", ping) // 这个是通用的接口
}
// 接口使用权限认证中间件
version2 := g.Group("/api/v2", CasbinMiddleWare)
{
version2.GET("/ping", ping)
}
_ = g.Run(":8099")
} // casbin middleware 权限认证中间件
func CasbinMiddleWare(c *gin.Context) {
var userName string
userName = c.GetHeader("userName")
if userName == "" {
fmt.Println("headers invalid")
c.JSON(200, gin.H{
"code": http.StatusUnauthorized,
"message": "Unauthorized",
"data": "",
})
c.Abort()
return
}
// 请求的path
p := c.Request.URL.Path
// 请求的方法
m := c.Request.Method
// 这里认证
res, err := Enforcer.EnforceSafe(userName, p, m)
// 这个 HasPermissionForUser 跟上面的有什么区别
// EnforceSafe 会验证角色的相关的权限
// 而 HasPermissionForUser 只验证用户是否有权限
//res = Enforcer.HasPermissionForUser(userName,p,m)
if err != nil {
fmt.Println("no permission ")
fmt.Println(err)
c.JSON(200, gin.H{
"code": 401,
"message": "Unauthorized",
"data": "",
})
c.Abort()
return
}
if !res {
fmt.Println("permission check failed")
c.JSON(200, gin.H{
"code": 401,
"message": "Unauthorized",
"data": "",
})
c.Abort()
return
}
c.Next()
}

  • 结果

    p 代表的是策略 admin 角色可以使用GET 访问 /api/v2/ping

    g 代表的是角色 test 用户在root 角色中

    tom 在admin 角色中

    所以在测试时请求头

    userName = root 有正常响应 (这里不会到数据库验证,策略最后一条)

    userName = tom 正常响应 (tom 有admin 角色 , 所以验证通过)

    userName = role_admin 正常响应 (参考这里:https://casbin.org/docs/zh-CN/rbac , 正常情况下用户名和角色名称不应该一样)

    userName = *** 都无法通过认证

RBAC API 官网

RBAC API 官网

gin框架中集成casbin-权限管理的更多相关文章

  1. YII框架中的srbac权限管理模块的安全与使用(版本是1.1.20)

    0x01 前言 srbac的原理: YII框架的srbac模块是一个专门管理权限的一个模块,那它是怎么管理权限的呢.我们知道YII框架的网页显示是由控制器实现的,控制器继承父类CController和 ...

  2. 在前后端分离的SpringBoot项目中集成Shiro权限框架

    参考[1].在前后端分离的SpringBoot项目中集成Shiro权限框架 参考[2]. Springboot + Vue + shiro 实现前后端分离.权限控制   以及跨域的问题也有涉及

  3. 细说shiro之五:在spring框架中集成shiro

    官网:https://shiro.apache.org/ 1. 下载在Maven项目中的依赖配置如下: <!-- shiro配置 --> <dependency> <gr ...

  4. (补漏)Springboot2.0 集成shiro权限管理

    原文Springboot2.0 集成shiro权限管理 一.关于停止使用外键. 原本集成shiro建立用户.角色.权限表的时候使用了外键,系统自动创建其中两个关联表,用@JoinTable.看起来省事 ...

  5. (十三)整合 SpringSecurity 框架,实现用户权限管理

    整合 SpringSecurity 框架,实现用户权限管理 1.Security简介 1.1 基础概念 1.2 核心API解读 2.SpringBoot整合SpringSecurity 2.1 流程描 ...

  6. (十二)整合 Shiro 框架,实现用户权限管理

    整合 Shiro 框架,实现用户权限管理 1.Shiro简介 1.1 基础概念 1.2 核心角色 1.3 核心理念 2.SpringBoot整合Shiro 2.1 核心依赖 2.2 Shiro核心配置 ...

  7. gin框架中的路由

    基本路由 gin框架中采用的路由库是基于httrouter做的 地址为:https://github.com/julienschmidt/httprouter httprouter路由库 点击查看代码 ...

  8. drf框架中认证与权限工作原理及设置

    0909自我总结 drf框架中认证与权限工作原理及设置 一.概述 1.认证 工作原理 返回None => 游客 返回user,auth => 登录用户 抛出异常 => 非法用户 前台 ...

  9. springboot框架中集成thymeleaf引擎,使用form表单提交数据,debug结果后台获取不到数据

    springboot框架中集成thymeleaf引擎,使用form表单提交数据,debug结果后台获取不到数据 表单html: <form class="form-horizontal ...

随机推荐

  1. 解决Centos7误删Python问题

    1.前言 昨天安装Python3.6的时候.不小心把原来的Python全删了.不知道咋办了.后面参考一篇博客.重新安装了一下.相关的包全回来了.所以还是得注意root模式下.慎用rm -rf命令.(笑 ...

  2. JAVA通过实体类生成数据库查询语句(驼峰命名规则)

    import java.io.IOException; import java.lang.reflect.Field; import java.util.HashMap; import java.ut ...

  3. 【LeetCode】5685. 交替合并字符串 Merge Strings Alternately (Python)

    作者: 负雪明烛 id: fuxuemingzhu 公众号:每日算法题 本文关键词:LeetCode,力扣,算法,算法题,交替合并字符串,Merge Strings Alternately,刷题群 目 ...

  4. 【LeetCode】122.Best Time to Buy and Sell Stock II 解题报告(Java & Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...

  5. 倍福CX5120嵌入式控制器使用教程

    1.新建工程 新建TwinCAT XAE Project 2.连接设备 点击SYSTEM,再点击"Change Target..." 在弹出的"choose Targt ...

  6. Kronecker Products and Stack Operator

    目录 定义 Stack Operator Kronecker Product 性质 Stack Operator Kronecker Product 半线性 Whitcomb L. Notes on ...

  7. Bayesian Optimization with a Finite Budget: An Approximate Dynamic Programming Approach

    目录 概 主要内容 Lam R, Willcox K, Wolpert D H, et al. Bayesian Optimization with a Finite Budget: An Appro ...

  8. 使用.NET 6开发TodoList应用(12)——实现ActionFilter

    系列导航及源代码 使用.NET 6开发TodoList应用文章索引 需求 Filter在.NET Web API项目开发中也是很重要的一个概念,它运行在执行MVC响应的Pipeline中执行,允许我们 ...

  9. MySQL数据库基础(1)数据库基础

    目录 一.数据库简介 二.mysql数据库 三.客户端连接mysql服务 四.Navicat for mysql 一.数据库简介 1.概念 (1)数据:如文字.图形.图像.声音以及学生的档案记录等,这 ...

  10. Java支持IPv6研究

    1.Java对IPv6的支持 相对其他开发语言而言,Java对IPv6的支持是比较透明的, 如果全部采用域名(主机名)的方式进行通信,那么基本不需要修改也无需编译原来的代码就可以直接在IPv6上运行. ...