Go Web 开发 Demo【用户登录、注册、验证】(2)https://developer.aliyun.com/article/1534273
4、密码加密以及登录测试
4.1、注册加密
在 controller 的注册方法( Register )中修改创建用户的代码,对将要插入数据库中的代码进行加密:
// 创建用户 hasedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { ctx.JSON(http.StatusInternalServerError, gin.H{ "code": 500, "msg": "加密错误", }) return } newUser := model.User{ Name: name, Phone: phone, Password: string(hasedPassword), } DB.Create(&newUser)
4.2、登录方法
func Login(ctx *gin.Context) { DB := common.GetDB() // 获取参数 phone := ctx.PostForm("phone") password := ctx.PostForm("password") // 数据验证 if len(phone) != 11 { ctx.JSON(http.StatusUnprocessableEntity, gin.H{ "code": 422, "msg": "手机号必须为11位!", }) return } if len(password) < 6 { // gin.H 等同于 map[string]any ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{ "code": 422, "msg": "密码不能少于6位!", }) return } // 判断手机号是否存在 user := model.User{} DB.Where("phone = ?", phone).First(&user) if user.ID == 0 { ctx.JSON(http.StatusUnprocessableEntity, gin.H{ "msg": "用户不存在", }) return } // 判断密码是否正确 if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil { ctx.JSON(http.StatusBadRequest, gin.H{ "code": 400, "msg": "密码错误", }) return } // 返回 token 给前端 token := "11" // 返回结果 ctx.JSON(200, gin.H{ "code": 200, "data": gin.H{ "token": token, }, "msg": "登录成功", }) }
4.3、注册请求
把我们的 Login 方法注册到 /login 地址(只需要在 routes.go 文件的 CollectRoute 函数中添加一行即可):
测试
查看数据库:
登录测试
5、jwt 实现用户认证
jwt 地址:github.com/dgrijalva/jwt-go
5.1、发放 token
在 common 包下来创建一个 jwt.go 文件,定义发放 token 的函数:
package common import ( "com.lyh/goessential/model" "github.com/dgrijalva/jwt-go" "time" ) var jwtKey = []byte("a_secret_crect") type Claims struct { UserId uint jwt.StandardClaims } func ReleaseToken(user model.User) (string, error) { expirationTime := time.Now().Add(7 * 24 * time.Hour) // 设置token有效期7天 claims := &Claims{ UserId: user.ID, StandardClaims: jwt.StandardClaims{ ExpiresAt: expirationTime.Unix(), // 过期时间 IssuedAt: time.Now().Unix(), // 发放的时间 Issuer: "lyh", // 发送者 Subject: "user token", // 发送主题 }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, err := token.SignedString(jwtKey) if err != nil { return "", err } return tokenString, nil } // 从 tokenString 中解析出 claims 然后返回 func ParseToken(tokenString string) (*jwt.Token, *Claims, error) { claims := &Claims{} token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { return jwtKey, nil }) return token, claims, err }
5.2、设置返回 token
在之前 controller 层下用户模块中的登录请求(/login)中设置返回 token(之前随便写了个 "11"):
// 返回 token 给前端 token, err := common.ReleaseToken(user) if err != nil { ctx.JSON(http.StatusInternalServerError, gin.H{ "code": 500, "msg": "系统异常", }) log.Printf("token generate error : %v", err) return } // 返回结果 ctx.JSON(200, gin.H{ "code": 200, "data": gin.H{ "token": token, }, "msg": "登录成功", })
测试登录用户,拿到 token :
token 由三部分组成:
- 协议头(token 使用的加密协议)
- 我们给token中存储的信息(解密后是 JSON 格式的数据)
- 前两部分加上key通过hash后的值
5.3、定义用户认证中间件
如果不加中间件的话,前端请求时携带token,返回的 user 是 null ,因为我们没有往上下文存储 user 的信息。
中间件为的是把前端请求时,authorization 中携带的 token 信息解析出来验证并保存到上下文。在 middleware 包下创建 AuthMiddleware.go:
package middleware import ( "com.lyh/goessential/common" "com.lyh/goessential/model" "github.com/gin-gonic/gin" "net/http" "strings" ) // 自定义中间件用于用户验证:相当于SpringBoot中的拦截器 func AuthMiddleware() gin.HandlerFunc { return func(ctx *gin.Context) { // 获取 authorization header tokenString := ctx.GetHeader("Authorization") //eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOjIsImV4cCI6MTcxNTE1MDIyNCwiaWF0IjoxNzE0NTQ1NDI0LCJpc3MiOiJseWgiLCJzdWIiOiJ1c2VyIHRva2VuIn0.C6yH99IZDjj6_FnpHaREVPmoCX82nYWv1OZao171iPg // 验证格式 if tokenString == "" || !strings.HasPrefix(tokenString, "Bearer ") { ctx.JSON(http.StatusUnauthorized, gin.H{ "code": 401, "msg": "权限不足", }) ctx.Abort() return } tokenString = tokenString[7:] // Bearer+' '一共7位 token, claims, err := common.ParseToken(tokenString) if err != nil || !token.Valid { ctx.JSON(http.StatusUnauthorized, gin.H{ "code": 401, "msg": "权限不足", }) ctx.Abort() return } // 验证通过后获取claims 中的 userId userId := claims.UserId DB := common.GetDB() var user model.User DB.First(&user, userId) // 用户不存在 if user.ID == 0 { ctx.JSON(http.StatusUnauthorized, gin.H{"code": 401, "msg": "权限不足"}) ctx.Abort() return } // 用户存在 将user信息写入上下文 ctx.Set("user", user) ctx.Next() } }
Go Web 开发 Demo【用户登录、注册、验证】(4)https://developer.aliyun.com/article/1534275