Uroboros

博客开发 · BLOG-DEVELOPMENT2026.07.29
54 次阅读

博客开发日志 - 最终话

总算把RBAC+jwt鉴权这块完善了,之前总觉得差点什么...

   折腾了好几年的博客,终于在今天做出了相对完整的一版了,作为一个开发,想做自己的博客很久了,一开始其实折腾过wordpress和一些静态博客,但始终感觉差一些意思,再怎么折腾也是别人的东西,自由度上肯定是会受了一定的限制的。

   一开始是用nestjs+typeorm来做后端的,老实说,从nestjs开始学习后端,曲线很平滑,因为你只需要定义好entity模型,也不用自己去操作数据库,语法也是相对简单的,nestjs一条命令就能生成模版,上手很快,typeorm的缺点是对ts支持不够好,很多时候改了数据库模型,你的query却是纯字符串,等到调用的时候才知道报错,而且有很多奇奇怪怪的bug,于是后面我就替换成了prisma

https://static.ua42.com/blog/article/l8703L3FhZyqx_X0iSRda.png

   prisma 这东西怎么说呢,用起来很舒服,他能根据你的模型自动生成对应的方法,但确实让我又爱又恨,一是下载模型需要一定的网络环境,这点在部署上很吃亏,二是容易查询出嵌套数据,一旦涉及到连表查更容易出现这种问题,而我感觉后端去打平数据,是有点别扭的,而且还无法指定时区,查了很久没查到解决办法。

   对于node项目,部署的问题是我觉得最难受的,如果使用常规pm2去做,每次部署都要替换一堆文件,并且重新执行npm install,后面使用docker 部署时,也遇到了镜像体积和内存占用的问题,一开始打包一个简单的node项目,发现占用了好几百m的内存,镜像也是异常巨大,因为我习惯本地打包docker镜像,如何将镜像包传到服务器上都是个问题,当时我的服务器带宽才8m,传这么大的镜像需要很久,后面在掘金上拜读了一篇帖子,一点点优化镜像大小,最终优化到了100多m的样子,勉强算是能接受,其实也研究过将node服务打包成一个可执行文件,只是目前能用的库,比如pkg,好像已经不维护了,我尝试打包过好几次,也以失败告终。

   后面闲得没事去研究rust,虽说不适合用来写web,但我对他最终构建的产物大小很满意,只是学了三四次都没入门,只能转而求其次选择了go,因为前端项目经常使用ts,所以我对强类型语言也没那么排斥,简单跑起一个服务还挺简单的,一开始选择的是gin+gorm,不过我后面替换成了chi+sqlc,至于为什么选择chi我也有点忘了,也许是打包体积?也许是chi性能更好?不过这都无所谓了,至于选择sqlc的原因很明确,gorm无法区分0还是null(其实这不是gorm的问题,后面才知道),于是我开始尝试写sql,因为一开始在node项目里做过RBAC权限,所以这块我其实很熟悉,无非是5表关联,但是用惯了orm,真要自己手动去联表操作还是有点麻烦的,不过还好,复杂的sql可以交给ai。

   对于权限管理,麻烦的不是查询和写入,而是状态清理,因为没有用外键,删除用户、角色、权限、都得清理脏数据,这点不拉个清单,很容易漏掉,而且频繁查库也对数据库有压力,引入缓存的话,清理的成本更是翻倍。我也是花了好久才把这块弄好。目前的权限是:角色与菜单和api分别关联,菜单权限与接口权限分开,这样接口权限不影响页面元素展示,我觉得是相对合理的了。给用户返回的是菜单权限的编码,用这个去判断页面元素是否展示,而后端缓存了角色拥有的接口列表,之所以没有给接口权限加code,是因为go里面没有类似java和ts之类的注解,也不用硬编码去判断权限。

   权限管理这块其实在之前的node项目里已经做的相对比较完善了,只有jwt这块始终差一点,因为刚入行时是微服务兴起的时候,很多国内的项目自然而然用上了jwt,其实我也不知道我为什么要用jwt,这东西怎么用在网上的讨论很多,我也是看了好几年没看到有人讨论出个结果,大概讲一下我目前的流程吧。

bash
复制代码
用户登录 
-> 根据传进来的用户密码,hash后与数据库的password_hash比较,如果验证通过 
-> 签发access_token + refresh_token 
-> 服务端refresh_token到cookies中,并存储hash后的refresh_token到redis里
-> 客户端则保存access_token,如果调用接口前判断到过期时间大于当前时间 - 30s,则调用refresh接口
-> 服务端从cookies里拿refresh_token,拿到以后获取hash值与redis里的做比对,比对成功后签发新的token

# access_token 这个的过期时间设置得要相对短一些(15min), 虽然做不到立即禁用用户,他能操作的时间也大大缩短了
# 之前做nodejs项目时是把access_token存到了redis里,这样好拉黑用户,但我都这样做了,为什么不直接用session呢
# 后面考虑做refresh_token轮转,这样就能长期登录不掉线了,但我项目目前复杂还没到那个程度,目前的设计已经够用了

https://static.ua42.com/blog/article/9GIdC_xnT76herX1hyj3-.pnghttps://static.ua42.com/blog/article/nWDnx_IGbaYtVSsZYl7e9.png

   最终的结果就是这样,redis里存的是token的 hash,即使泄露也没有敏感信息,refresh_token在前端也只是一串神秘字符串,没有任何有效信息。

   部署后的服务内存占用:

https://static.ua42.com/blog/article/ShzGGPhSmTHDMH9KgNQej.png

大概25m左右,光是对比服务端渲染的blog,都已经足够小了

RedisGolang