仿抖音后端项目中JWT的使用笔记 | 青训营

207 阅读4分钟

仿抖音后端项目中JWT的使用笔记 | 青训营

一、JWT简介

​ JWT(JSON Web Token)是一种用于在网络应用中进行身份验证和信息传递的开放标准。它由三个部分组成:头部(Header)、载荷(Payload)和签名(Signature)。

  • 头部包含了标识令牌类型以及所使用的加密算法。常见的加密算法有HMAC SHA256和RSA。
  • 载荷是令牌的主要内容,可以包含一些声明,例如用户的身份信息、权限等。JWT规定了一些标准的声明,如iss(令牌的发行者)、exp(令牌的过期时间)、sub(令牌的主题)等,同时也可以自定义私有声明。
  • 签名是为了验证令牌的真实性和完整性而生成的。使用头部和载荷以及一个密钥,通过指定的加密算法生成签名。服务器在接收到JWT后可以使用相同的密钥进行验证,确保令牌未被篡改。

​ JWT的优势在于它的轻巧、可扩展和无状态性。由于令牌中包含了所有必要的信息,服务器不需要在自身存储会话信息,也无需频繁地查询数据库,因此可以减轻服务器的负担。另外,JWT可以用于跨域通信,并且易于实现和使用。

二、常见登录认证方案

1、基于session的身份认证

​ 浏览器向服务器发送登录请求时,服务器验证通过后,会将用户信息存入session中,服务器会生成一个sessionid放入cookie中,返回给浏览器,浏览器再次发送请求时,会携带cookie,cookie中有sessionid,会一并发送给服务器

session登录.png

2、基于JWT的身份认证

​ JWT全称JSON Web Token,前台在登录时,将用户信息发送给服务器,服务器将用户信息进行加密,返回JWT给前台,前台将其存到localStorage中,前台在axios中二次封装,拦截请求,后续发送请求时,在Authorization Header中携带上JWT,后台通过提前协商的密钥,解密JWT,验证用户信息,验证成功,响应请求数据,验证失败,返回登录页重新登录。

20201201162345993.png

三、结合GoLang在项目中实现JWT

1、jwt库的引入

​ 开源Gin项目和一些博客都使用的是dgrijalva/jwt-go这个jwt库

go get -u github.com/dgrijalva/jwt-go

2、自定义Claims结构体

​ 需要在Token中保存用户ID信息

type Claims struct {
    UserId int64
    jwt.StandardClaims
}

3、生成Token

​ 考虑手机端应用的需求,生成标准claim时过期时间为一周。生成Token时调用jwt库的NewWithClaims即可,传入签名算法和claims结构体,签名算法用得最多的是HS256。

func GenerateToken(user dao.UserLogin) (string, error) {
	// 设置过期时间为一周
	expirationTime := time.Now().Add(24 * 7 * time.Hour)
	claims := &Claims{
		UserId: user.UserInfoId,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: expirationTime.Unix(),
			IssuedAt:  time.Now().Unix(),
			Issuer:    "happy_boy_111",
			Subject:   "L_Q__",
		}}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenString, err := token.SignedString(jwtKey)
	if err != nil {
		return "", err
	}
	return tokenString, nil
}

4、校验Token

​ 将前端传来的token字符串传入解析校验函数,校验函数调用ParseWithClaims进行解析,此时有两种解析方法,一种是将解析结果保存到claims变量中,另一种是从ParseWithClaims返回的Token结构体中取出Claims结构体。这里选择第二种,若token字符串合法但过期claims也会有数据,err会提示token过期。

func ParseToken(tokenString string) (*Claims, error) {
    // 解析token
    token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
       return jwtKey, nil
    })
    if err != nil {
       return nil, err
    }
    if claims, ok := token.Claims.(*Claims); ok && token.Valid {
       return claims, nil
    }
    return nil, errors.New("invalid token")
}

5、设置中间件拦截请求校验Token

​ 当Token校验失败,无论是过期还是不合法直接拒绝用户请求,并将user_id信息保存到应用上下文context中

func JwtAuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
       tokenStr := c.Query("token")
       if tokenStr == "" {
          tokenStr = c.PostForm("token")
       }
       // token不存在
       if tokenStr == "" {
          c.JSON(http.StatusOK, dao.CommonResponse{StatusCode: 401, StatusMsg: "用户未登录"})
          c.Abort()
          return
       }
       // 解析token
       token, err := ParseToken(tokenStr)
       if err != nil {
          c.JSON(http.StatusOK, dao.CommonResponse{StatusCode: 403, StatusMsg: "token不正确"})
          c.Abort()
          return
       }
       // token超时检验
       if time.Now().Unix() > token.ExpiresAt {
          c.JSON(http.StatusOK, dao.CommonResponse{
             StatusCode: 404, StatusMsg: "token过期",
          })
       }
       // 将当前请求的userId信息保存到请求的上下文c上
       c.Set("user_id", token.UserId)
       c.Next() // 后续的处理函数可以用过c.Get("user_id")来获取当前请求的用户信息
    }
}

四、笔记总结

​ 在仿抖音后端项目中,我们使用JWT(JSON Web Token)来实现用户身份验证和权限管理。JWT是一种轻量级且安全的身份验证方式,它使得服务器无需存储会话信息,减轻了服务器的负担,并且可以在分布式系统中实现跨域通信。通过合理使用JWT,我们可以构建更安全、可扩展的应用程序。

五、附录

  • JWT 官方网站:jwt.io/ 这是JWT的官方网站,提供了JWT的介绍、规范、库和工具等资源,是一个全面且权威的学习来源。
  • JSON Web Token (JWT) - Introduction: www.youtube.com/watch?v=7Q1… 这是一段YouTube视频介绍JWT的基本概念和原理,适合初学者入门。
  • JSON Web Token (JWT) Authentication Tutorial: www.youtube.com/watch?v=mbs… 这是一段关于JWT身份验证的视频教程,演示了如何使用JWT进行用户身份验证和保护API。
  • JSON Web Token (JWT) - Full Course: www.youtube.com/watch?v=7Q1… 这是一门较长的完整课程,涵盖了JWT的各个方面,包括生成和验证令牌、令牌刷新、权限管理等。