权限控制 - Casbin

最近在研究 iam 相关的内容,iam 即身份访问管理(identify access management) 身份: 用户在整个系统体系中扮演什么样的角色 访问管理:对身份的权限控制。 那既然谈到权限控制,不得不谈谈 casbin, casbin 目前是 github 上在权限控制处理方面 star 数是最多的,虽然之前没接触过 casbin,但也调研了一段时间,从易用性,扩展性,实现上来谈谈 casbin。 核心概念 权限模型目前主流的主要包括acl, rbac, abac。 简单先从这几个模型说起。而了解这几个模型之前,我们先约定一下一些术语。 subject 主体,在一般的权限体系中,主体一般是指操作者,比如某个用户,某个角色(其实角色最下面也是个用户),当然更广义的讲,可能主体也可以代表其他需要约束的对象,比如在物联网中,他也可以代表一个iot设备。 resouce 资源,一切可操作的实体都可以当做是一种资源,比如aliyun上的我们购买的ecs实例,后台系统的一个user action 操作,主体对资源的操作,比如我们可以对ecs实例进行开机,关机,重启等操作,可以新增,删除,编辑一个user。 condition 操作的条件,一般基于主体与资源的约事条件,比如 user 的ip必须在172.168.0.1/24的段内 effect 影响效力,一般只有allow(允许)/deny(不允许) 有了这些术语,我们去解释对应的权限模型,可能更知道不同模型的区别 ACL acl(访问控制列表),他代表的是,哪些用户(subject)或系统进程可以访问(action)资源(resource) 如果某个文件 apple.jpg(resource) 的 ACL 包含(Alice(subject): read(action),write(action);Bob(subject): read(action)),这将赋予 Alice 读写文件的权限,而只赋予 Bob 读取文件的权限。 RBAC ABAC casbin 中的概念 理解casbin,最主要理解其核心思路,casbin中的核心思路是使用policy(策略)中的规则去应用到model(模型)中,model 是一个规则引擎,帮我们处理一个主体(subject)是否有操作(action)某个资源(subject)的权限。 policy策略:一组规则的集合, 规则 model模型:对每行规则进行解析 因此,对于我们使用者来说,大多时候仅仅只需要关注policy和model就行。 model 初览 在model层面,casbin又基于 PERM (Policy, Effect, Request, Matcher). 一个完整的model(perm)文件格式如下:...

九月 14, 2023 · nobject

swagger的使用

随着RESTful API的流行,Swagger已经成为了一种通用的API描述和文档格式。它可以帮助开发者描述、测试和文档化RESTful API。在本文中,我们将介绍在go中如何使用相应的库生成swagger文档。在golang中, 有两款可以生成swagger的库,一般我们选择https://github.com/swaggo/swag,还有另一款https://github.com/go-swagger/go-swagger,相对来说,使用起来swag更易用一点 安装 1 go install github.com/swaggo/swag/cmd/swag@latest 默认情况下,会安装在go path相关的目录下,该目录一般已经加入环境变量,若没有加入环境变量,我们可根据每种系统的不同,手动加入至环境变量中 检测是否安装成功: 1 swag -v 返回具体的版本号即成功: 1 swag.exe version v1.8.12 使用 路由绑定 我们使用的是gin框架,swag对gin也有集成封装, 我们可以在gin的路由绑定至对应的swagger文档 1 2 3 // swagger文档查看路径 url := ginSwagger.URL("/swagger/doc.json") // The url pointing to API definition r.GET("swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, url)) 依赖库: 1 2 swaggerFiles "github.com/swaggo/files" "github.com/swaggo/gin-swagger" 路由注释 1 2 3 4 5 6 7 8 9 10 // ListUser // @Summary 查询用户信息 // @Tags 用户 // @Description 查询用户信息 // @Produce json // @Param UserId header string true "当前用户id" // @Param query query dto....

八月 27, 2023 · nobject

go分享

go的goroutine go中最重要的概念之一就是协程,协程的创建方式也很简单,go func()就简单的创建了一个协程,虽然go语言中并没有规定协程的 创建数据限制,每个协程相对于线程的成本也很低,协程最小占用只有2Kb, 1000个协程才2M,所以成本极低。 但创建容易的同时也应该注意以下几点: 对于panic捕获的安全性 我们一般使用go func()直接创建协程了,但一般在代码中,很少直接这么使用,一般会在外面再封装一层。具体的封装方式类似这样: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package thread import ( "fmt" "runtime/debug" ) func GoSafe(fn func()) { go RunSafe(fn) } func RunSafe(fn func()) { // 可以將panic上报给sentry等报警平台,或者钉钉,飞书机器人的钩子等 defer func() { if p := recover(); p != nil { fmt.Printf("RunSafe capture crash,msg: %s \n %s ", fmt.Sprint(p), string(debug.Stack())) } }() fn() } 原因很简单,go语言外层的defer是捕获不了内部goroutine的异常的,所以如果内部的goroutine发生panic,那会导致整个程序直接crash。 因此,使用goroutine的话最好再加一层安全封装,以防止潜在的panic导致的crash。...

五月 24, 2023 · nobject

golang 面试题集

Go 中的 rune 和 byte 有什么区别? Go 中的 string 和 []byte 有什么区别? 什么是深拷贝和浅拷贝? Go 中的 slice 和 array 有什么区别?slice的扩容机制与扩容时机,使用slice该注意什么 说说 Go 中闭包的底层原理? 说一下 GMP 模型的原理 Go 的默认栈大小是多少?最大值多少? Go 中的分段栈和连续栈的区别? 简述一下 Go 栈空间的扩容/缩容过程? GMP 为什么要有 P ? Go 中的 GC 演变是怎样的? 哪些情况会导致协程泄露? 内存分配原理 gin框架的路由实现原理 go中http库的实现原理 代码题:使用n个并发,输出[]string{“a”, “b”, “c”, “d”, “e”, “f”, “g”} 代码题:实现一个简单的协程池 代码题:使用并发编排,使abc三个各输出100次 map的底层实现原理,如果一个协程在delete, 一个协程在读,会出现什么问题,原因是什么 uinptr和unsafe.Pointer的区别 singleFlight存在的坑

二月 22, 2023 · nobject

[转]golang面试 - Rune与Byte的区别

以下文章转自 https://iswbm.com/517.html 一个字符串是由若干个字符组合而成的,比如 hello,就由 5 个字符组成。 在 Go 中字符类型有两种,分别是: byte 类型:字节,是 uint8 的别名类型 rune 类型:字符,是 int32 的别名类型 byte 和 rune ,虽然都能表示一个字符,但 byte 只能表示 ASCII 码表中的一个字符(ASCII 码表总共有 256 个字符),数量远远不如 rune 多。 rune 表示的是 Unicode字符中的任一字符,而我们都知道,Unicode 是一个可以表示世界范围内的绝大部分字符的编码, 这张表里几乎包含了全世界的所有的字符,当然中文也不在话下。 能表示的字符更多,意味着它占用的空间,也要更大,所占空间是 4个 byte 的大小。 下面以一段代码来验证一下他们的占用空间的差异 1 2 3 4 5 6 7 8 var a byte = 'A' var b rune = 'B' fmt.Printf("a 占用 %d 个字节数\n", unsafe.Sizeof(a)) fmt.Printf("b 占用 %d 个字节数\n",unsafe.Sizeof(b)) // output a 占用 1 个字节数 b 占用 4 个字节数 参考文档: https://iswbm....

十一月 4, 2022 · nobject

golang面试 - channel全解析

channel 什么情况下会出现panic 1 2 3 1. 试图往已close的channel继续发送 2. 试图关闭已关闭的channel 3. 试图关闭一个nil的channel 向为nil的channel发送数据会怎样 1 2 3 给定一个 nil channel c: <-c 从 c 接收将永远阻塞 c <- v 发送值到 c 会永远阻塞 channel close后读的问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 关闭后继续读数据,已在缓冲区内的可以读取到数据,而后得到的是零值(对于int,就是0)。 z 我们可以通过for循环遍历channel,来获取到已经写入的值 for _, v := range channel{ } 对于可能获取到零值,我们可以使用断言判断,ok为false代表channel已关闭后读取的 value, ok := <- channel 也可以在close channel之后有意将channel 置为 nil, 如此再使用,并不会读取到零值 对于不带缓冲的ch,和带缓冲的一样,channel close掉之后并不影响读,只影响写入 channel的底层结构 底层结构体 1 2 3 4 5 6 7 8 9 10 11 12 13 14 type hchan struct { qcount uint // total data in the queue 代表 chan 中已经接收但还没被取走的元素的个数。内建函数 len 可以返回这个字段的值。 dataqsiz uint // size of the circular queue 循环队列的大小 buf unsafe....

十一月 4, 2022 · nobject

golang面试 - string与[]byte的区别,如何互相转化

string是一个8位字节的集合,通常但不一定代表UTF-8编码的文本。string可以为空,但是不能为nil。string的值是不能改变的。string类型本质也是一个结构体,底层本质就是一个byte类型的数组。 byte就是uint8的别名,它是用来区分字节值和8位无符号整数值 string与[]byte的区别 对于[]byte与string而言,两者之间最大的区别就是string的值不能改变。string在底层都是结构体stringStruct{str: str_point, len: str_len},string结构体的str指针指向的是一个字符常量的地址, 这个地址里面的内容是不可以被改变的,因为它是只读的,但是这个指针可以指向不同的地址。 string的好处:以在不加锁的控制下,多次使用同一字符串,在保证高效共享的情况下而不用担心安全问题 对于[]byte来说,以下操作是可行的: 1 2 b := []byte("hello world") b[1] = 'i' string修改操作是被禁止的: 1 2 s := "hello world" s[1] = 'i' string支持这样的操作: 1 2 s := "hello world" s = "iello world" string与[]byte的互相转化 string的底层数据结构类型: 1 2 3 4 type stringStruct struct { str unsafe.Pointer len int } []byte对应的底层数据结构(本质上是个slice): 1 2 3 4 5 type slice struct { array unsafe.Pointer len int cap int } 两者对应的结构非常相似。...

十一月 4, 2022 · nobject

golang面试 - 米哈游第三方编制一面复盘

代码题 以n个协程,去输出[]string{“a”,“b”,“c”,“d”,“e”, “f”, “g”}。 代码题真得很简单,自己手生的很,竟然没写出来。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func main() { var wg sync.WaitGroup sem := make(chan struct{}, 5) // 创建一个大小为5的信号量 letters := []string{"a", "b", "c", "d", "e", "f", "g"} for _, letter := range letters { sem <- struct{}{} // 尝试写入信号量,如果已满,则阻塞 wg.Add(1) go func(l string) { defer wg.Done() fmt.Println(l) time.Sleep(5 * time.Second) <-sem // 认领信号量 }(letter) } wg....

十一月 4, 2022 · nobject

golang面试 - 深拷贝与浅拷贝

深拷贝是指两个变量,如果其内部引用其他的地址,那么深拷贝会将其内部的指针所指向的值重新分配地址用于存储这个值,因此他们内部的一些引用指针会变化。 浅拷贝是指两个变量,实际上其内部引用部分的地址并没有变化。 常见的浅拷贝,像slice的赋值,map的赋值 1 2 s := []int{1,2,3} s1 = s 如上所示,s1实际上就是对s的浅拷贝 对于深拷贝,我们在代码里应该这么使用 1 2 3 s := []int{1,2,3} var s1 []int copy(s, &s1)

十一月 4, 2022 · nobject

golang面试 - gc

相关文章 https://draveness.me/golang/docs/part3-runtime/ch07-memory/golang-garbage-collector/ https://www.cnblogs.com/luozhiyun/p/14564903.html https://www.kancloud.cn/aceld/golang/1958308#Go_V13mark_and_sweep_21 https://www.bilibili.com/video/BV1wz4y1y7Kd?p=14&spm_id_from=pageDriver https://cloud.tencent.com/developer/article/1756163 https://blog.haohtml.com/archives/26358 https://mp.weixin.qq.com/s/5xjH-LJ53XiNm2sMNQZiGQ GCMark 标记准备阶段,为并发标记做准备工作,启动写屏障 STW GCMark 扫描标记阶段,与赋值器并发执行,写屏障开启 并发 GCMarkTermination 标记终止阶段,保证一个周期内标记任务完成,停止写屏障 STW GCoff 内存清扫阶段,将需要回收的内存归还到堆中,写屏障关闭 并发 GCoff 内存归还阶段,将过多的内存归还给操作系统,写屏障关闭 并发 Go 语言中对 GC 的触发时机存在两种形式: 主动触发,通过调用 runtime.GC 来触发 GC,此调用阻塞式地等待当前 GC 运行完毕。 被动触发,分为两种方式: 使用系统监控,当超过两分钟没有产生任何 GC 时,强制触发 GC。 使用步调(Pacing)算法,其核心思想是控制内存增长的比例。

十一月 4, 2022 · nobject

golang面试 - gmp

相关文章 https://mp.weixin.qq.com/s/jIWe3nMP6yiuXeBQgmePDg https://tonybai.com/2017/06/23/an-intro-about-goroutine-scheduler/ https://tonybai.com/2020/03/21/illustrated-tales-of-go-runtime-scheduler/ https://draveness.me/golang/docs/part3-runtime/ch06-concurrency/golang-goroutine/ https://www.kancloud.cn/aceld/golang/1958305#GoroutineGMP_164 https://segmentfault.com/a/1190000023869478 https://www.luozhiyun.com/archives/448 https://zboya.github.io/post/go_scheduler/ https://www.bilibili.com/video/BV19r4y1w7Nx?p=2&spm_id_from=pageDriver https://mp.weixin.qq.com/s/XiqVIR3U5ZmRD7xwJZKipA 进程,线程,协程的区别 进程: 进程是程序的一次执行过程,是程序在执行过程中的分配和管理资源的基本单位,每个进程都有自己的地址空间,进程是系统进行资源分配和调度的一个独立单位。 每个进程都有自己的独立内存空间,由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。 线程:在同一个进程中的不同线程共享相同的地址空间(虚拟内存,代表切换不需要切换页表),每个线程有独立的栈区,程序计数器(执行指令的信息),栈指针以及函数运行的寄存器。 初始栈大小8M,ulimit - s可以查看。调度由系统调度。从用户态切换到内核态,上下文切换消耗相对大 协程:用户态线程,不由内核管理,由go的底层调度器控制,协程初始栈大小2K。由用户调度 goroutine初始栈大小,上下文如何切换,存在哪里 协程初始栈大小2K,上下文由g0去调度找到对应可以运行的goroutine,存在gorountine的gobuf中 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 组织下面试的语言 gmp是go为了提高并发能力实现的协程调度模型 g代表我们常说的协程,代码里的体现就是go关键字 m代表操作系统的线程,系统最大值默认为10000 p代表逻辑处理器,默认与cpu的核数想同,当然也可以通过runtime.GOMAXPROCS()来设置p的数量,m一般需要与p关联才能执行g。 调度策略: p会切换到g0栈上,执行调度函数,查找到可执行的goroutine,然后调用gogo函数,切换到goroutine的执行流程上。 1. 每隔61次调度轮回从全局队列找,避免全局队列中的g被饿死。 2. 从p.runnext获取g,从p的本地队列中获取。 3. 调用 findrunnable 找g,找不到的话就将m休眠,等待唤醒。 findrunnable 查找G的过程: 1. 调用 runqget ,尝试从P本地队列中获取G,获取到返回 2. 调用 globrunqget ,尝试从全局队列中获取G,获取到返回 3....

十一月 4, 2022 · nobject

Golang 面试资料整理

golang基础 defer golang底层 GMP模型 Sync包实现 Interface底层实现 Map底层实现 数据库 mysql mongo redis 消息队列 rabbitMQ kafka rocketMQ 微服务 konga 服务发现 服务熔断 服务降级 docker与k8s docker docker实现原理 docker的常用命令 docker编排 k8s istio 计算机网络 算法 排序算法 查找算法 树

十月 15, 2022 · nobject