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/你好/trueerr := c.ShouldBindQuery(&p) //restBind?name=qimiao&sex=trueif 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、ShouldBindQueryName 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 PostParamserr := c.ShouldBindJSON(&p)//err := c.ShouldBindUri(&p) //restBind/你好/true//err := c.ShouldBindQuery(&p) //restBind?name=qimiao&sex=truefmt.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,走完走1fmt.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 Userinfodb.First(&user, "name=?", "辉**")fmt.Println(user)
查: Find (跟结构体切片指针地址,查符合条件的所有)
var user []Userinfodb.Find(&user) //不跟条件,查所有fmt.Println(user)
条件:Where Or 填写简单的sql查询语句执行得到model
模型:Model
import ("gorm.io/driver/mysql""gorm.io/gorm"
)type Userinfo struct {Id uintName stringGender stringHobby stringgorm.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 []Userinfodb.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.ModelName 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.ModelClassName stringStudents []Student //班级里有多个学生
}type Student struct {gorm.ModelStudentName string// 学生属于classId班级ClassID uint//一个学生一个学生卡IdCard IdCard//多对多 学生有多个老师,并且知道老师的idTeachers []Teacher `gorm:"many2many:Student_Teachers"`//TeacherID uint}type IdCard struct {gorm.ModelStudentID uintNum int
}type Teacher struct {gorm.ModelTeacherName string//老师有多个学生,老师知道学生的id//StudentID uintStudents []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.ModelClassName stringStudents []Student //班级里有多个学生
}type Student struct {gorm.ModelStudentName string// 学生属于classId班级ClassID uint//一个学生一个学生卡IdCard IdCard//多对多 学生有多个老师,并且知道老师的idTeachers []Teacher `gorm:"many2many:Student_Teachers"`//TeacherID uint}type IdCard struct {gorm.ModelStudentID uintNum int
}type Teacher struct {gorm.ModelTeacherName string//老师有多个学生,老师知道学生的id//StudentID uintStudents []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 Studentdb.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 Classdb.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解成秒 60sExpiresAt: 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解成秒 60sExpiresAt: 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 {// 处理errfmt.Printf("%s", err)}if ok == true {// 允许alice读取data1fmt.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 {// 处理errfmt.Printf("%s", err)}if ok == true {// 允许alice读取data1fmt.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