gin框架中集成casbin-权限管理
概念
权限管理几乎是每个系统或者服务都会直接或者间接涉及的部分. 权限管理保障了资源(大部分时候就是数据)的安全, 权限管理一般都是和业务强关联, 每当有新的业务或者业务变化时, 不能将精力完全放在业务实现上, 权限的调整往往耗费大量的精力. 其实, 权限的本质没有那么复杂, 只是对访问的控制而已, 有一套完善的访问控制接口, 再加上简单的权限模型. 权限模型之所以能够简单, 就是因为权限管理本身并不复杂, 只是在和具体业务结合时, 出现了各种各样的访问控制场景, 才显得复杂.
PERM 模型
PERM(Policy, Effect, Request, Matchers)模型很简单, 但是反映了权限的本质 – 访问控制
- Policy: 定义权限的规则
- Effect: 定义组合了多个 Policy 之后的结果, allow/deny
- Request: 访问请求, 也就是谁想操作什么
- Matcher: 判断 Request 是否满足 Policy
casbin的作用
- 以经典{subject, object, action}形式或您定义的自定义形式实施策略,同时支持允许和拒绝授权。
- 处理访问控制模型及其策略的存储。
- 管理角色用户映射和角色角色映射(RBAC中的角色层次结构)。
- 支持内置的超级用户,例如root或administrator。超级用户可以在没有显式权限的情况下执行任何操作。
- 多个内置运算符支持规则匹配。例如,keyMatch可以将资源键映射/foo/bar到模式/foo*。
casbin不执行的操作
- 身份验证(又名验证username以及password用户登录时)
- 管理用户或角色列表。我相信项目本身管理这些实体会更方便。用户通常具有其密码,而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 官网
gin框架中集成casbin-权限管理的更多相关文章
- YII框架中的srbac权限管理模块的安全与使用(版本是1.1.20)
0x01 前言 srbac的原理: YII框架的srbac模块是一个专门管理权限的一个模块,那它是怎么管理权限的呢.我们知道YII框架的网页显示是由控制器实现的,控制器继承父类CController和 ...
- 在前后端分离的SpringBoot项目中集成Shiro权限框架
参考[1].在前后端分离的SpringBoot项目中集成Shiro权限框架 参考[2]. Springboot + Vue + shiro 实现前后端分离.权限控制 以及跨域的问题也有涉及
- 细说shiro之五:在spring框架中集成shiro
官网:https://shiro.apache.org/ 1. 下载在Maven项目中的依赖配置如下: <!-- shiro配置 --> <dependency> <gr ...
- (补漏)Springboot2.0 集成shiro权限管理
原文Springboot2.0 集成shiro权限管理 一.关于停止使用外键. 原本集成shiro建立用户.角色.权限表的时候使用了外键,系统自动创建其中两个关联表,用@JoinTable.看起来省事 ...
- (十三)整合 SpringSecurity 框架,实现用户权限管理
整合 SpringSecurity 框架,实现用户权限管理 1.Security简介 1.1 基础概念 1.2 核心API解读 2.SpringBoot整合SpringSecurity 2.1 流程描 ...
- (十二)整合 Shiro 框架,实现用户权限管理
整合 Shiro 框架,实现用户权限管理 1.Shiro简介 1.1 基础概念 1.2 核心角色 1.3 核心理念 2.SpringBoot整合Shiro 2.1 核心依赖 2.2 Shiro核心配置 ...
- gin框架中的路由
基本路由 gin框架中采用的路由库是基于httrouter做的 地址为:https://github.com/julienschmidt/httprouter httprouter路由库 点击查看代码 ...
- drf框架中认证与权限工作原理及设置
0909自我总结 drf框架中认证与权限工作原理及设置 一.概述 1.认证 工作原理 返回None => 游客 返回user,auth => 登录用户 抛出异常 => 非法用户 前台 ...
- springboot框架中集成thymeleaf引擎,使用form表单提交数据,debug结果后台获取不到数据
springboot框架中集成thymeleaf引擎,使用form表单提交数据,debug结果后台获取不到数据 表单html: <form class="form-horizontal ...
随机推荐
- 解决Centos7误删Python问题
1.前言 昨天安装Python3.6的时候.不小心把原来的Python全删了.不知道咋办了.后面参考一篇博客.重新安装了一下.相关的包全回来了.所以还是得注意root模式下.慎用rm -rf命令.(笑 ...
- JAVA通过实体类生成数据库查询语句(驼峰命名规则)
import java.io.IOException; import java.lang.reflect.Field; import java.util.HashMap; import java.ut ...
- 【LeetCode】5685. 交替合并字符串 Merge Strings Alternately (Python)
作者: 负雪明烛 id: fuxuemingzhu 公众号:每日算法题 本文关键词:LeetCode,力扣,算法,算法题,交替合并字符串,Merge Strings Alternately,刷题群 目 ...
- 【LeetCode】122.Best Time to Buy and Sell Stock II 解题报告(Java & Python & C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...
- 倍福CX5120嵌入式控制器使用教程
1.新建工程 新建TwinCAT XAE Project 2.连接设备 点击SYSTEM,再点击"Change Target..." 在弹出的"choose Targt ...
- Kronecker Products and Stack Operator
目录 定义 Stack Operator Kronecker Product 性质 Stack Operator Kronecker Product 半线性 Whitcomb L. Notes on ...
- 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 ...
- 使用.NET 6开发TodoList应用(12)——实现ActionFilter
系列导航及源代码 使用.NET 6开发TodoList应用文章索引 需求 Filter在.NET Web API项目开发中也是很重要的一个概念,它运行在执行MVC响应的Pipeline中执行,允许我们 ...
- MySQL数据库基础(1)数据库基础
目录 一.数据库简介 二.mysql数据库 三.客户端连接mysql服务 四.Navicat for mysql 一.数据库简介 1.概念 (1)数据:如文字.图形.图像.声音以及学生的档案记录等,这 ...
- Java支持IPv6研究
1.Java对IPv6的支持 相对其他开发语言而言,Java对IPv6的支持是比较透明的, 如果全部采用域名(主机名)的方式进行通信,那么基本不需要修改也无需编译原来的代码就可以直接在IPv6上运行. ...