golang 1010工作室
视频地址:https://www.bilibili.com/video/BV12i4y1x7AG/?spm_id_from=333.788&vd_source=eba330e2ab2e59ae2e4ecace161e0983
URI和URL的概念和区别 - 掘金 (juejin.cn)
URL是统一资源定位符(像人的地址),URI是统一资源标识符(像人的身份证)
GOPROXY=https://goproxy.cn,direct
https://www.kancloud.cn/shuangdeyu/gin_book/949411
golang找不到对应的包。go get github.com/gin-gonic/gin
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
}
找到一个最适合自己的中文文档
localhost:8080/ping
Run(":1010")
参数挂在url中 uri中传参
r := gin.Default() //携带基础中间件 启动路由
r.GET("/path/:id", func(c *gin.Context) {
//c 上下文
id := c.Param("id")
//user和pwd 地址栏后面的,用query传参.默认传参,如果不存在给一个默认值
user := c.DefaultQuery("user", "qimiao")
pwd := c.Query("pwd")
c.JSON(200, gin.H{
"id": id,
"user": user,
"pwd": pwd,
})
})
r.Run()
参数在form body中 或者uri
r.POST("/path", func(c *gin.Context) {
//从form表单中取数据
user := c.DefaultPostForm("user", "qimiao")
pwd := c.PostForm("pwd")
c.JSON(200, gin.H{
"user": user,
"pwd": pwd,
})
})
一般情况为uri 同样也可以用body
r.DELETE("/path/:id", func(c *gin.Context) {
id := c.Param("id")
c.JSON(200, gin.H{
"id": id,
})
})
参数在form body 或者uri
r.PUT("/path", func(c *gin.Context) {
//从form表单中取数据
user := c.DefaultPostForm("user", "qimiao")
pwd := c.PostForm("pwd")
c.JSON(200, gin.H{
"user": user,
"pwd": pwd,
})
})
一般情况下为 地址栏的query
//user和pwd 地址栏后面的,用query传参.默认传参,如果不存在给一个默认值
user := c.DefaultQuery("user", "qimiao")
pwd := c.Query("pwd")
一般情况下 为FORM参数
//从form表单中取数据
user := c.DefaultPostForm("user", "qimiao")
pwd := c.PostForm("pwd")
地址栏定义占位符 通过占位符取参数
bind模式一定要设置tag
如果一个字段用binding:"required"
修饰,并且在绑定时该字段的值为空,那么将返回一个错误。json uri form
type PostParams struct {
Name string `json:"name" uri:"name" form:"name"`
Sex bool `json:"sex" uri:"sex" form:"sex"`
}
func main() {
r := gin.Default()
//r.POST("/restBind", func(c *gin.Context) {
//r.POST("/restBind/:name/:sex", func(c *gin.Context) {
r.POST("/restBind", func(c *gin.Context) {
var p PostParams
//err := c.ShouldBindJSON(&p)
//err := c.ShouldBindUri(&p) //restBind/你好/true
err := c.ShouldBindQuery(&p) //restBind?name=qimiao&sex=true
if err != nil {
c.JSON(400, gin.H{
"mes": "报错了",
"data": gin.H{},
})
} else {
c.JSON(200, gin.H{
"mes": "成功",
"data": p,
})
}
})
r.Run(":8080")
}
binding:"required" //传入参数的不能为空
type PostParams struct {
// ShouldBindJSON、ShouldBindUri、ShouldBindQuery
Name string `json:"name" uri:"name" form:"name" `
Sex bool `json:"sex" uri:"sex" form:"sex" `
Age int `json:"age" uri:"sex" form:"sex" binding:"required,mustBig"`
}
/*
{
"name":"奇妙",
"sex":true,
"age":17
}
报错信息:Key: 'PostParams.Age' Error:Field validation for 'Age' failed on the 'mustBig' tag
*/
func mustBig(fl validator.FieldLevel) bool {
if fl.Field().Interface().(int) <= 18 {
return false
}
return true
}
func main(){
r := gin.Default()
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterValidation("mustBig", mustBig)
}
//r.POST("/restBind", func(c *gin.Context) {
//r.POST("/restBind/:name/:sex", func(c *gin.Context) {
r.POST("/restBind", func(c *gin.Context) {
var p PostParams
err := c.ShouldBindJSON(&p)
//err := c.ShouldBindUri(&p) //restBind/你好/true
//err := c.ShouldBindQuery(&p) //restBind?name=qimiao&sex=true
fmt.Println(err)
if err != nil {
c.JSON(400, gin.H{
"mes": "报错了",
"data": gin.H{},
})
} else {
c.JSON(200, gin.H{
"mes": "成功",
"data": p,
})
}
})
r.Run(":8080")
}
读取到的文件就可以进行文件的操作
c.FormFile("前端放到file里面的name")
原生:这里使用os.create方法来写
gin封装的 c.SaveUploadedFile(file, dst)
r := gin.Default()
r.POST("/testUpload", func(c *gin.Context) {
file, _ := c.FormFile("file")
name := c.PostForm("name")
//c.SaveUploadedFile(file, "./"+file.Filename) //等于以下手写封装功能
in, _ := file.Open()
defer in.Close()
out, _ := os.Create("./" + file.Filename)
io.Copy(out, in)
defer out.Close()
c.JSON(200, gin.H{
"mes": file,
"name": name,
})
})
r.Run(":8080")
多文件上传
上传文件 · Gin中文文档 · 看云 (kancloud.cn)
c.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=%s", "文件名")) //fmt.Sprintf("attachment; filename=%s", filename)对下载的文件重命名
r := gin.Default()
r.POST("/testUpload", func(c *gin.Context) {
file, _ := c.FormFile("file")
//name := c.PostForm("name")
//c.SaveUploadedFile(file, "./"+file.Filename) //以下手写封装功能
in, _ := file.Open()
defer in.Close()
out, _ := os.Create("./" + file.Filename)
defer out.Close()
io.Copy(out, in)
//文件写回前端
c.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=%s", file.Filename))
c.File("./" + file.Filename) //保存文件到前端
})
对 router 创建 Group就是分组
同一分组会拥有同一前缀和同一中间件
写法:
router:= gin.Default()
v1 := router.Group("/v1") {
v1.POST("/login", loginEndpoint)
v1.POST("/submit", submitEndpoint)
v1.POST("/read", readEndpoint) }
案例
func main() {
r := gin.Default()
v1 := r.Group("v1")
v1.GET("/test", func(c *gin.Context) {
fmt.Println("我在分组v1里面")
c.JSON(200, gin.H{
"success": true,
})
})
v1.GET("/test2", func(c *gin.Context) {
fmt.Println("我在分组v1里面")
c.JSON(200, gin.H{
"success": true,
})
})
r.Run(":8080")
// 访问:http://localhost:8080/v1/test
}
在请求到达路由的方法的前和后进行的一系列操作
在路由器(路由组)上进行use操作 后面传入中间件函数即可
有点类似洋葱,从外往里走,走完在从里走出去。属于洋葱中间件.
func funcname() gin.HandlerFunc {
return func(c *gin.Context) {
fmt.Println("before request")
c.Next()
fmt.Println("after request")
}
}
案例
//创建中间件
func middle() gin.HandlerFunc {
return func(c *gin.Context) {
fmt.Println("我在方法前,我是1")
c.Next() //是否往下面走
fmt.Println("我在方法后,我是1")
}
}
//创建中间件
func middle2() gin.HandlerFunc {
return func(c *gin.Context) {
fmt.Println("我在方法前,我是2")
c.Next() //是否往下面走,走到2后next发现没有中间件了,走方法内部,然后走2,走完走1
fmt.Println("我在方法后,我是2")
}
}
func main() {
r := gin.Default()
v1 := r.Group("v1").Use(middle(), middle2())//等效于middle(). middle2()
v1.GET("/test", func(c *gin.Context) {
fmt.Println("我在分组v1里面")
c.JSON(200, gin.H{
"success": true,
})
})
v1.GET("/test2", func(c *gin.Context) {
fmt.Println("我在分组v1里面")
c.JSON(200, gin.H{
"success": true,
})
})
r.Run(":8080")
// 访问:http://localhost:8080/v1/test
}
我在方法前,我是1
我在方法前,我是2
我在分组v1里面
我在方法后,我是2
我在方法后,我是1
耦合度较高,自定义起来比较麻烦
导入 gorm
导入mysql驱动器
使用open链接 得到 数据库操作对象(以mysql为例)
db, err := gorm.Open("mysql", "user:password@/dbname?charset=utf8&parseTime=True&loc=Local")
defer db.Close()
gorm支持自动迁移模式 使用 AutoMigrate 方法来帮我们自动化创建数据库表
// 自动迁移模式
db.AutoMigrate(&Product{})
增:Create (跟结构体指针地址)
删:Delete (跟结构体指针地址)或者条件 会根据主键自动去查询单条或者根据条件删除多条
改:Update 更新单一数据 还有 Updates 更新数据中指定内容 Save更新所有内容
查:First (跟结构体示例指针地址,查符合条件的第一个)
var user Userinfo
db.First(&user, "name=?", "辉**")
fmt.Println(user)
查: Find (跟结构体切片指针地址,查符合条件的所有)
var user []Userinfo
db.Find(&user) //不跟条件,查所有
fmt.Println(user)
条件:Where Or 填写简单的sql查询语句执行得到model
模型:Model
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type Userinfo struct {
Id uint
Name string
Gender string
Hobby string
gorm.Model
}
func main() {
// 连接数据库
dsn := "root:123456@tcp(127.0.0.1:3306)/db2?charset=utf8mb4&parseTime=True&loc=Local" //是否格式化时间:是,时区本地
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
//自动迁移
db.AutoMigrate(&Userinfo{})
//增
/*
u1 := Userinfo{
Name: "亚**",
Gender: "男",
Hobby: "编程",
}
db.Create(&u1)*/
/*
//查
var user []Userinfo
db.Where("id<=?", 2).Find(&user)
fmt.Println(user)
//更新一条
db.Where("id=?", 2).First(&Userinfo{}).Update("gender", "男")
*/
/*
//更新多条
db.Where("id in (?)", []int{1, 2}).Find(&[]Userinfo{}).Updates(map[string]interface{}{
"Name": "六六",
"Gender": "男",
})*/
/*
db.Where("id in (?)", []int{1, 3}).Find(&[]Userinfo{}).Updates(Userinfo{
Name: "七七",
Gender: "女",
})*/
//db.Where("id=?", 1).Delete(&Userinfo{}) //软删除
db.Where("id in (?)", []int{1, 2}).Unscoped().Delete(&Userinfo{}) //硬删除
}
模型定义 | GORM - The fantastic ORM library for Golang, aims to be developer friendly.
type User struct {
gorm.Model
Name string `gorm:"primaryKey;column:user_name;type:varchar(100)"`
}
//例子一
func (User) TableName() string {
return "qm_users"
}
//例子二
func (u User) TableName() string {
if u.Role == "admin" {
return "admin_users"
} else {
return "qm_users"
}
}
多对多使用many2many关键字
手写一对一、一对多、多对多关系
//班级里有多个学生(一对多) -- 学生属于classId班级(多对一) -- 一个学生有一个idCard(一对一) -- 一个学生多个老师,多个老师对一个学生(多对多)
type Class struct {
gorm.Model
ClassName string
Students []Student //班级里有多个学生
}
type Student struct {
gorm.Model
StudentName string
// 学生属于classId班级
ClassID uint
//一个学生一个学生卡
IdCard IdCard
//多对多 学生有多个老师,并且知道老师的id
Teachers []Teacher `gorm:"many2many:Student_Teachers"`
//TeacherID uint
}
type IdCard struct {
gorm.Model
StudentID uint
Num int
}
type Teacher struct {
gorm.Model
TeacherName string
//老师有多个学生,老师知道学生的id
//StudentID uint
Students []Student `gorm:"many2many:Student_Teachers"`
}
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/db2?charset=utf8mb4&parseTime=True&loc=Local"
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
db.AutoMigrate(&Teacher{}, &Class{}, &Student{}, &IdCard{})
i := IdCard{Num: 123456}
t := Teacher{
TeacherName: "老师夫",
//Students: []Student{s},
}
s := Student{
StudentName: "qm",
IdCard: i,
Teachers: []Teacher{t},
}
c := Class{
ClassName: "奇妙的班级",
Students: []Student{s},
}
_ = db.Create(&c).Error //把班级创建,班级把学生创建,学生把学生卡创建,学生把老师创建
//_ = db.Create(&t).Error
}
分页查询使用 Count记录总数 使用Limit 和 Offset 指定记录位置
预加载 Preload(可以把嵌套结构体数据也查出来)
db.Preload("Teachers").Preload("IdCard").Where("id=?", id).First(&student)
嵌套预加载
db.Preload("Students").Preload("Students.IdCard").Preload("Students.Teachers").Where("id=?", id).First(&class)
import (
"github.com/gin-gonic/gin"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
//班级里有多个学生(一对多) -- 学生属于classId班级(多对一) -- 一个学生有一个idCard(一对一) -- 一个学生多个老师,多个老师对一个学生(多对多)
type Class struct {
gorm.Model
ClassName string
Students []Student //班级里有多个学生
}
type Student struct {
gorm.Model
StudentName string
// 学生属于classId班级
ClassID uint
//一个学生一个学生卡
IdCard IdCard
//多对多 学生有多个老师,并且知道老师的id
Teachers []Teacher `gorm:"many2many:Student_Teachers"`
//TeacherID uint
}
type IdCard struct {
gorm.Model
StudentID uint
Num int
}
type Teacher struct {
gorm.Model
TeacherName string
//老师有多个学生,老师知道学生的id
//StudentID uint
Students []Student `gorm:"many2many:Student_Teachers"`
}
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/db2?charset=utf8mb4&parseTime=True&loc=Local"
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
db.AutoMigrate(&Teacher{}, &Class{}, &Student{}, &IdCard{})
r := gin.Default()
r.POST("/student", func(c *gin.Context) {
var student Student
_ = c.BindJSON(&student)
db.Create(&student)
})
r.GET("/student/:ID", func(c *gin.Context) {
id := c.Param("ID")
var student Student
db.Preload("Teachers").Preload("IdCard").Where("id=?", id).First(&student)
c.JSON(200, gin.H{
"mes": "成功",
"data": student,
})
})
r.GET("/class/:ID", func(c *gin.Context) {
id := c.Param("ID")
var class Class
db.Preload("Students").Preload("Students.IdCard").Preload("Students.Teachers").Where("id=?", id).First(&class)
c.JSON(200, gin.H{
"mes": "成功",
"data": class,
})
})
r.Run(":8888")
/*
{
"StudentName":"qm",
"ClassID":1,
"IdCard":{
"Num":555
},
"Teachers":[{
"TeacherName":"老师1"
},{
"TeacherName":"老师2"
}]
}
*/
}
官网
通常使用 NewWithClaims
因为我们可以通过匿名结构体来实现 Claims接口 从而可以携带自己的参数
这边我把一个Claims结构体从源码中粘贴出来
type StandardClaims struct {
Audience string `json:"aud,omitempty"`
ExpiresAt int64 `json:"exp,omitempty"`
Id string `json:"jti,omitempty"`
IssuedAt int64 `json:"iat,omitempty"`
Issuer string `json:"iss,omitempty"`
NotBefore int64 `json:"nbf,omitempty"`
Subject string `json:"sub,omitempty"`
}
其中最重要的三个参数
type MyClaims struct {
MyType string `json:"myType"`
jwt.StandardClaims `json:"standardClaims"`
}
匿名函数实现接口
案例加密
type MyClaims struct {
UserName string `json:"username"`
jwt.StandardClaims
}
func main() {
mySigningKey := []byte("qimiao")
c := MyClaims{
UserName: "qimiao",
StandardClaims: jwt.StandardClaims{
NotBefore: time.Now().Unix() - 60, //base64解成秒 60s
ExpiresAt: time.Now().Unix() + 60*60*2,
Issuer: "qimiao",
},
}
t := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
fmt.Println(t)
s, e := t.SignedString(mySigningKey) //把t用密钥k加密
if e != nil {
fmt.Printf("%s", e)
}
fmt.Println(s)
}
// 加密串 头 体
//&{ 0xc000114108 map[alg:HS256 typ:JWT] {qimiao { 1670931390 0 qimiao 1670938530 }} false}
//eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InFpbWlhbyIsImV4cCI6MTY3MDkzMTk5NCwiaXNzIjoicWltaWFvIiwibmJmIjoxNjcwOTM5MTM0fQ.fo8_JrIbrb3tT4Awg2nok2povhpKh_3YQ-NOYxyBwvA
案例解密解密
import (
"fmt"
"github.com/dgrijalva/jwt-go"
"time"
)
type MyClaims struct {
UserName string `json:"username"`
jwt.StandardClaims
}
func main() {
mySigningKey := []byte("qimiao")
c := MyClaims{
UserName: "qimiao",
StandardClaims: jwt.StandardClaims{
NotBefore: time.Now().Unix() - 60, //base64解成秒 60s
ExpiresAt: time.Now().Unix() + 60*60*2,
Issuer: "qimiao",
},
}
t := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
s, e := t.SignedString(mySigningKey)
if e != nil {
fmt.Printf("%s", e)
}
fmt.Println(s)
time.Sleep(2 * time.Second)
token, err := jwt.ParseWithClaims(s, &MyClaims{}, func(token *jwt.Token) (interface{}, error) {
return mySigningKey, nil
})
if err != nil {
fmt.Printf("%s", err)
return
}
//fmt.Println(token.Claims) //&{qimiao { 1670990981 0 qimiao 1670983721 }}
fmt.Println(token.Claims.(*MyClaims).UserName)
}
jwt.MapClaims{
"myType": "myType",
"nbf": time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix(),
"exp": time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix(),
}
map形式
案例
func main() {
mySigningKey := []byte("qimiao")
t := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"exp": time.Now().Unix() + 60,
"iss": "qimiao",
"nbf": time.Now().Unix() - 5,
"username": "my",
})
s, e := t.SignedString(mySigningKey)
if e != nil {
fmt.Printf("%s", e)
}
fmt.Println(s)
time.Sleep(2 * time.Second)
token, err := jwt.ParseWithClaims(s, &jwt.MapClaims{}, func(token *jwt.Token) (interface{}, error) {
return mySigningKey, nil
})
if err != nil {
fmt.Printf("%s", err)
return
}
//fmt.Println(token.Claims) //&{qimiao { 1670990981 0 qimiao 1670983721 }}
fmt.Println(token.Claims.(*jwt.MapClaims))
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) // 创建
ss,err := token.SignedString(mySigningKey) // 签发
HS256(对称加密) RS256(非对称加密 需要一个结构体指针 三个属性
{ECDSAKeyD,
ECDSAKeyX,
ECDSAKeyY},ES256)
token,err := jwt.ParseWithClaims(token,claims,func)
token是一个jwtToken类型的数据 我们需要的就是其中 的Claims
对Claims进行断言 然后进行取用即可
token, err := jwt.ParseWithClaims(s, &MyClaims{}, func(token *jwt.Token) (interface{}, error) {
return mySigningKey, nil
})
if err != nil {
fmt.Printf("%s", err)
return
}
//fmt.Println(token.Claims) //&{qimiao { 1670990981 0 qimiao 1670983721 }}
fmt.Println(token.Claims.(*MyClaims).UserName)
Model 的语法 | Casbin
p策略(Policy )、e影响(Effect )、r请求( Request )、m匹配规则(Matchers )
Editor | Casbin
subject(sub 访问实体), object(obj 访问的资源)和 action(act 访问方法)eft(策略结果 一般为空 默认指定 allow) 还可以定义为 deny
Policy 策略 p={sub, obj, act, eft}
策略一般存储到数据库 因为会有很多
[policy_definition]
p = sub,obj,act
Matchers 匹配规则 Request和Policy的匹配规则。
m = r.sub == p.sub && r.act == p.act && r.obj == p.obj
r 请求 p 策略
这时候会把 r 和 p按照上述描述进行匹配 从而返回匹配结果(eft)如果不定义 会返回 allow 如果定义过了 会返回我们定义过的那个结果
Effect 影响
Request 请求 r={sub, obj,act}
Request带着匹配规则(matchers)和Policy匹配,返回一个结果,再通过effect校验是否能校验通过
加完g——我的入参可以是alice也可以是data2_admin
以角色为基础
[request_definition]
r = sub, obj, act
人话:请求入参(实体,资源,方法)
[policy_definition]
p = sub, obj, act
人话:策略(实体,资源,方法)
[role_definition]
g = _, _
人话:这个情况下 g写啥都行 毕竟match里面根本没有涉及到g 不过我们规范一点 按照角色权限 这里意思是g收两个参数 g=用户,角色
[policy_effect]
e = some(where (p.eft == allow))
人话 :看看经过下面那些个匹配规则后的返回值是否有一条等于里面那个 allow
[matchers]
m = r.sub == p.sub && ParamsMatch(r.obj,p.obj) && r.act == p.act
人话:进来的实体 资源 方法 能不能在 权限表(p)里面找到一个一模一样的
多租户模型
[request_definition]r = sub, dom, obj, act
人话:入参(实体,域【商户】,资源,方法)
[policy_definition]p = sub, dom, obj, act
人话:权限模型(实体,域【商户】,资源,方法)
[role_definition]g = _, _, _
半句人话:域匹配规则 后面g会说 这里意思是 g收三个参数(属于哪个用户,用户属于哪个角色,角色属于哪个商户)
[policy_effect]e = some(where (p.eft == allow))
人话 :看看经过下面那些个匹配规则后的返回值是否等于里面那个 allow
[matchers]m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act
先列出来一个权限定义里面的东西
g,qm,teacher,classOne
然后用g做一些解析动作
一般我们会收到前端的入参 大概长这样 “qm”,“classOne”,“/base/api”,“get”
上面我们定义的g就是我们需要的模型 经过g以后 g已经把我们的入参 qm 解析成了 r.sub 为 teacher r.rom 为 classOne 先从这个商户找一下角色 找到就过了 然后用 dom 去匹配然后用一个类似于这样的请求 去 策略里面找
teacher,classOne ,/api/base,get
先匹配,然后根据p获得eft,然后去policy获得策略效果
开始使用 | Casbin
引入包 github.com/casbin/casbin/v2
创建模型和policy 并且引入 e, err := casbin.NewEnforcer(“path/to/model.conf”, “path/to/policy.csv”)
调用api并且使用
sub := "alice" // the user that wants to access a resource.
obj := "data1" // the resource that is going to be accessed.
act := "read" // the operation that the user performs on the resource.
ok, err := e.Enforce(sub, obj, act)
if err != nil {
// handle err
}
if ok == true {
// permit alice to read data1
} else {
// deny the request, show an error
}
案例
model.conf
[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
policy.csv
p,zhangsan,data1,read
mian.go
import (
"fmt"
"github.com/casbin/casbin/v2"
)
func main() {
e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
sub := "alice" // 想要访问资源的用户。
obj := "data1" // 将被访问的资源。
act := "read" // 用户对资源执行的操作。
added, err := e.AddPolicy("alice", "data1", "read")
fmt.Println(added)
fmt.Println(err)
//进行检测
ok, err := e.Enforce(sub, obj, act)
if err != nil {
// 处理err
fmt.Printf("%s", err)
}
if ok == true {
// 允许alice读取data1
fmt.Println("通过")
} else {
// 拒绝请求,抛出异常
fmt.Printf("未通过")
}
// 您可以使用BatchEnforce()来批量执行一些请求
// 这个方法返回布尔切片,此切片的索引对应于二维数组的行索引。
// 例如results[0] 是{"alice", "data1", "read"}的结果
//results, err := e.BatchEnforce([[] []interface{}{"alice", "data1", "read"}, {"bob", datata2", "write"}, {"jack", "data3", "read"}})
//e.BatchEnforce([][]interface{}{{"alice", "data1", "read"}, {"bob", "datata2", "write"}, {"jack", "data3", "read"}})
}
GitHub - casbin/gorm-adapter: Gorm adapter for Casbin
使用gorm适配器进行存储
引用包 github.com/casbin/gorm-adapter
a, _ := gormadapter.NewAdapter("mysql", "mysql_username:mysql_password@tcp(127.0.0.1:3306)/",true) // Your driver and data source.
e, _ := casbin.NewEnforcer("examples/rbac_model.conf", a)
连接数据库并自动建表
初体验
// Load the policy from DB.
e.LoadPolicy()
// Check the permission.
e.Enforce("alice", "data1", "read")
// Modify the policy.
// e.AddPolicy(...)
// e.RemovePolicy(...)
// Save the policy back to DB.
e.SavePolicy()
案例
import (
"fmt"
"github.com/casbin/casbin/v2"
gormadapter "github.com/casbin/gorm-adapter/v3"
_ "github.com/go-sql-driver/mysql"
)
func main() {
//e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
a, _ := gormadapter.NewAdapter("mysql", "root:123456@tcp(127.0.0.1:3306)/casbin", true) // Your driver and data source.
e, _ := casbin.NewEnforcer("./model.conf", a)
sub := "alice" // 想要访问资源的用户。
obj := "data1" // 将被访问的资源。
act := "read" // 用户对资源执行的操作。
added, err := e.AddPolicy("alice", "data1", "read")
fmt.Println(added)
fmt.Println(err)
//进行检测
ok, err := e.Enforce(sub, obj, act)
if err != nil {
// 处理err
fmt.Printf("%s", err)
}
if ok == true {
// 允许alice读取data1
fmt.Println("通过")
} else {
// 拒绝请求,抛出异常
fmt.Printf("未通过")
}
}
查
filteredPolicy := e.GetFilteredPolicy(0, "alice")//第v0列是alice的数据
增
p AddPolicy()
g e.AddGroupingPolicy("group1", "data2_admin")
删
removed := e.RemovePolicy("alice", "data1", "read")
改
updated, err := e.UpdatePolicy([]string{"eve", "data3", "read"}, []string{"eve", "data3", "write"})
函数 | Casbin
func KeyMatch(key1 string, key2 string) bool {
return key1 == key2
/* i := strings.Index(key2, "*")
if i == -1 {
return key1 == key2
}
if len(key1) > i {
return key1[:i] == key2[:i]
}
return key1 == key2[:i]*/
}
func KeyMatchFunc(args ...interface{}) (interface{}, error) {
name1 := args[0].(string)
name2 := args[1].(string)
return (bool)(KeyMatch(name1, name2)), nil
}
model.conf
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && my_func(r.obj, p.obj) && r.act == p.act
# m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
# m = r.sub == p.sub && r.obj == p.obj && r.act == p.act