《Hertz框架 抖音项目2 | 青训营笔记》

145 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 7 天。

讲一下jwt+中间件实现token鉴权:

1.jwt编码函数,通过编码具有声明的用户ID和用户名来生成JSON Web Token(JWT)。声明包括用户ID和用户名,到期时间(设置为当前时间24小时后),发行者名称(设置为“daniel”),不早于时间(设置为当前时间),以及其他元数据(如签发时间,预期的接收方,唯一标识符)。使用HMAC-SHA256算法与秘密密钥(keyinfo字节片)对JWT进行签名。生成的JWT作为字符串返回。如果在JWT生成期间发生错误,则会进行记录,并返回空字符串。:

func Encodetoken(userid string, username string) string {
   keyinfo := []byte("3.1415926+0.618+qweasd")
   temp := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
      "userid":   userid,
      "username": username,
      // exp: jwt的过期时间,这个过期时间必须要大于签发时间
      "exp": time.Now().Unix() + 3600*24,
      // iss: jwt签发者
      "iss": "daniel",
      // nbf: 定义在什么时间之前,该jwt都是不可用的.
      "nbf": time.Now().Unix(),
      // sub: jwt所面向的用户
      // aud: 接收jwt的一方
      // iat: jwt的签发时间
      // jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
   })
   token, err := temp.SignedString(keyinfo)
   if err != nil {
      log.Println(err.Error())
      return ""
   }
   return token
}

2.解码函数,这个函数是用来解码JWT(JSON Web Token)令牌的。它需要一个字符串作为参数,即JWT令牌。该函数使用"3.1415926+0.618+qweasd"作为密钥,对令牌进行解码。如果解码成功,则将返回一个字符串数组,该数组包含JWT中的用户ID和用户名。如果解码失败,则返回一个包含两个空字符串的数组。:

// Decodetoken 此函数用于做jwt解码,返回解码后得到的用户id与用户名
func Decodetoken(token string) []string {
   keyinfo := []byte("3.1415926+0.618+qweasd")
   //parse函数会自动判断jwt令牌是否生效、过期等
   parse, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
      return keyinfo, nil
   })
   if err != nil {
      log.Println(err.Error())
      return []string{"", ""}
   }
   return []string{parse.Claims.(jwt.MapClaims)["userid"].(string), parse.Claims.(jwt.MapClaims)["username"].(string)}

}

3.核验函数,这个函数检查请求中的token是否合法。它从请求参数中获取token字符串,并调用Decodetoken函数进行解码。如果解码失败,返回falsenil。如果解码成功,将用户ID和用户名从结果中取出,并在数据库中查找对应的用户信息。如果查询到的用户信息不为空,则说明token是合法的,返回true和该用户的信息;否则,返回falsenil

func Checktoken(c *app.RequestContext) (bool, *user.Userinfo) {
   token := c.Query("token")
   p := Decodetoken(token)
   if p[0] == "" {
      return false, nil
   }
   userid, _ := strconv.Atoi(p[0])
   var userinfo user.Userinfo
   Connect2sql().Where("ID = ? AND username = ?", userid, p[1]).Find(&userinfo)
   if userinfo.Username != "" {
      return true, &userinfo
   } else {
      return false, nil
   }
}

4.中间件,这个函数是一个Middleware,用于检查请求的token是否正确。如果正确,则将用户信息存储在请求上下文中,如果不正确,则返回错误信息,并终止请求的处理。:

func _infoMw() []app.HandlerFunc {
   // your code
   return []app.HandlerFunc{func(ctx context.Context, c *app.RequestContext) {
      flag, userinfo := model.Checktoken(c)
      if flag {
         c.Set("userinfo", userinfo)
      } else {
         c.JSON(http.StatusOK, user.UserResponse{
            Response: handler.Response{StatusCode: 1, StatusMsg: "User doesn't exist"},
         })
         c.Abort()
      }
   },
   }
}

5.路由配置,这里只有login操作需要进行token鉴权,可以看到/douyin/user/login/路由中注册了刚刚实现的_loginMw()函数,表明通过/douyin/user/login/的请求都要先前往_loginMw()函数进行过滤。

func Register(r *server.Hertz) {

   root := r.Group("/douyin", rootMw()...)
   root.GET("/user/", append(_infoMw(), user.UserInfo)...)
   root.POST("/user/register/", append(_registerMw(), user.Register)...)
   root.POST("/user/login/", append(_loginMw(), user.Login)...)

}