前情提要
上期讲了路由,这期开始就慢慢把处理惩罚器的实现都讲完
这期改了栏目名,主要考虑到想要剖析这个项目,Gin占的文字比重反而不高 我保留了之前文章的栏目名,这样你才知道我改了名
账号与权限怎样实现?

我们从./router/router.go下这部分代码继续讲
上期我们已经相识了这段代码首先是注册了一个对应/api/auth的名为auth的路由组
并在其名下注册了/login和/register的POST处理惩罚器,并绑定了对应的处理惩罚句柄
controllers.Login和controllers.Register都实如今./controllers/auth_controller.go里
点击查察代码- package controllers
- import (
- "exchangeapp/global"
- "exchangeapp/models"
- "exchangeapp/utils"
- "net/http"
- "github.com/gin-gonic/gin"
- )
- func Register(ctx *gin.Context) {
- var user models.User
- if err := ctx.ShouldBindJSON(&user); err != nil {
- ctx.JSON(http.StatusBadRequest, gin.H{"errno": err.Error()})
- return
- }
- hashedPwd, err := utils.HashPassword(user.Password)
- if err != nil {
- ctx.JSON(http.StatusInternalServerError, gin.H{"errno": err.Error()})
- return
- }
- user.Password = hashedPwd
- token, err := utils.GenerateJWT(user.Username)
- if err != nil {
- ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
- return
- }
- if err := global.Db.AutoMigrate(&user); err != nil {
- ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
- return
- }
- if err := global.Db.Create(&user).Error; err != nil {
- ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
- return
- }
- ctx.JSON(http.StatusOK, gin.H{"token": token})
- }
- func Login(ctx *gin.Context) {
- var input struct {
- Username string `json:"username"`
- Password string `json:"password"`
- }
- if err := ctx.ShouldBindJSON(&input); err != nil {
- ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
- return
- }
- var user models.User
- if err := global.Db.Where("username = ?", input.Username).First(&user).Error; err != nil {
- ctx.JSON(http.StatusUnauthorized, gin.H{"error": "wrong credentials"})
- return
- }
- if !utils.CheckPassword(input.Password, user.Password) {
- ctx.JSON(http.StatusUnauthorized, gin.H{"error": "wrong credentials"})
- return
- }
- token, err := utils.GenerateJWT(user.Username)
- if err != nil {
- ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
- return
- }
- ctx.JSON(http.StatusOK, gin.H{"token": token})
- }
复制代码 注册一名新用户时,我们的后端都完成了什么?
从上往下来吧,先是func Register(ctx *gin.Context) {}
Context的话由web前端传递,Gin会负责监听并自动传参给我们的处理惩罚器的,如果对上下文不理解的话,可以借助AI辅助学习
Register的第一行代码是var user models.User
这是./model/user.go结构体User
也许比较难理解的反而是``裹起来的东西,也就是标签,不过这是GOLANG基础语法,不做赘述
固然创建了user变量,但我们还未赋值:
ShouldBindJSON是Gin库中定义好的方法,其本质是调用ShouldBind方法自动提取JSON的数据,并把值绑定到指定的结构体对象。
选JSON或者是XML等其实都可以,这是通过前后端的统一约定决定的,在差别项目ShouldBind的详细方法并不一致,这里的JSON不过是一个用来传递信息的包装罢了
因为想要生存用户的账号密码,我们肯定要生存到数据库里,但如果明文生存要是出了什么失误那甚至有可能要吃捞饭
所以对数据举行处理惩罚是很有必要的,这里我们最关心的是utils.HashPassword()方法
又是新库
Bcrypt是一种加盐的单向Hash,不可逆的加密算法,同一种明文(plaintext),每次加密后的密文都不一样,而且不可反向破解生成明文,破解难度很大。
总而言之,言而总之,通过Bcrypt库提供的方法我们可以将信息举行加密,也可以对同样是用Bcrypt算法加密的信息举行解密转化成明文。
但有了账号和密码也不够啊,总不能每次涉及到权限的东西都要登录一次吧,这时候便需要一种服务器和客户端都持有的、短有效期的登录凭证,在WEB中我们常用Token
要注意这里的Token和大模子谁人Token不一样,这里是JWT——JSON Web Token的略写
所以你也能看到我们生成Token的方法名为GenerateJWT,位于./ultis/ultis.go中
对于JWT我们需要相识的就是:它可以用来当做服务器和客户端之间约定的一把以字符串为载体的钥匙,至于校验方法和用途还是要我们自己来决定
GenerateJWT(){}接收一个字符串username:即为该用户生成一个专属的JWT
jwt.NewWithClaims()方法,是 Go 语言 jwt-go 库中的一个函数,用于创建一个包罗自定义声明(claims)的新 JWT,意味着我们可以通过传参来为JWT塞入加密后的我们需要的特定信息
第一个参数是method,需要实现SigningMethod接口,用以指定签名方法,常见的有jwt.SigningMethodHS256、 jwt.SigningMethodRS256,决定签名方法要根据我们自身需求举行选择,同时在解密时我们也需要指定雷同的签名方法
第二个参数是claims,需要完成Claims接口,用来自定义你需要在jwt中塞入的内容
Token的SignedString()方法用于对 JWT 举行签名并返回完整的签名字符串。
签名用的密钥也是不能够随便填写的 各种屎山协议,大模子真是救星了
我觉得这个很坑,因为一开始我以为Bearer只是一个用来方便前后端辨认的前缀而已,但我没想到它是一个约定俗成的协议:Bearer令牌 也许这就是面向字符串编程的一个比较难过的点吧
Token令牌的生成确实是个麻烦活,不过信息安满是很紧张的,隐私泄露之后的麻烦事是真的一堆 时不时就被发垃圾邮件,经历大大小小几十次被盗号,还因此CSGO被拿去开挂被封号的小透明作者一枚
回到./middlewares/auth_middleware.go的Register()
这个方法想要光看函数定义理解会非常非常麻烦,因为它是一个递归套娃,还是老样子:官方文档 or AI
什么是数据库迁移?
数据库迁移,也称为模式迁移、数据库模式迁移或简称为迁移,是为了修改关系型数据库中对象的结构而开发的可控的变动集。迁移帮助数据库模式从当前状态过渡到新的期望状态,无论这是否涉及添加表和列、删除元素、拆分字段或更改范例和约束。
迁移以编程方式管理对数据结构的增量、通常是可逆的更改。数据库迁移软件的目标是使数据库更改可重复、可共享和可测试,而不会丢失数据。通常,迁移软件生成描述将数据库从已知状态转换为新状态所需的确切操作集。这些可以检入并由正常的版本控制软件管理,以跟踪更改并在团队成员之间共享。
固然防止数据丢失通常是迁移软件的目标之一,但删除或破坏性地修改当前存储数据的结构的更改可能会导致删除。为了应对这种情况,迁移通常是一个受监视的过程,包罗检查生成的更改脚本并举行任何必要的修改以保留紧张信息。
这样我们为用户创建或者更新数据表,用以存放ta们的用户信息
Create方法无疑是用来向数据表插入记录
再成功完成一系列操作和通过一片的错误检查后,我们就可以定义这名用户注册成功了,于是我们向前端传递成功状态码尚有Token令牌
RegisterAPI也就收工了
注册不就是为了登录吗?
Register完了肯定要讲Login
创建暂时变量input,用来存放用户输入的用户账密,方便校验
从前端传入的JSON上下文中提取输入账密,存放于input
声明暂时变量user
Where方法不多说,用过mysql绝对不陌生,而First方法用来返回我们找到的第一个符合的记录到刚创建的user变量中(又由于注册时username是唯一的,所以这里无需担心找到的结果不是准确的)
CheckPassword我们定义在./middlewares/auth_middleware.go中
很简朴,password是用户此时输入的将要和注册信息对比的密码,而hash是用户注册时被加密后存放在数据库中的密码,CompareHashAndPassword就是将前者以与后者雷同的加密方式加密一遍之后,再讲二者举行比对,确保了密码校验的安全性
回到LoginAPI
还是生成JWT令牌并返回前端,毕竟你需要登录的时候便说明你此时没有令牌来作为身份验证嘛。
至此,Register和LoginAPI我们也分析完毕了,不过JWT的用法还未见着,后续肯定会写上的
结语
这两天思索完美主义,被焦虑折腾得不轻,写写文章真挺静心的,固然写的依托但能够直视自己的不完美,有助于我走出焦虑的旋涡
如果觉得我的文章写的不错麻烦各位点个赞,你们的支持是我创作的动力
各位下次再见
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |