深入理解JWT(JSON Web Token)的工作原理 | 青训营

231 阅读4分钟

深入理解JWT(JSON Web Token)的工作原理 | 青训营

JSON Web Token(JWT)是一种用于在网络应用间传递信息的开放标准。它通过数字签名或加密的方式来确保信息的安全性,被广泛应用于身份验证和授权场景。本文将深入探讨JWT的工作原理,从其结构、生成流程到验证过程进行详细解析。

JWT的结构

JWT由三部分组成,通过点号(.)分隔开:

  1. Header(头部):通常由两部分组成,令牌的类型(即“JWT”)和所使用的签名算法,例如HMAC SHA256或RSA。

  2. Payload(负载):包含了一些称为“声明”(claims)的信息,声明可以被用来提供用户的身份信息、权限等。有三种类型的声明:注册声明(registered claims)、公共声明(public claims)和私有声明(private claims)。

  3. Signature(签名):使用指定的算法和密钥对头部和负载进行签名,以验证信息的完整性和真实性。

JWT的生成流程

  1. 选择算法:在创建JWT之前,需要选择合适的签名算法,例如HMAC SHA256、RSA等。

  2. 构建头部:创建一个JSON对象,包含所选择的算法和令牌类型。然后,将该JSON对象进行Base64编码,得到JWT的第一部分。

  3. 构建负载:创建一个JSON对象,包含要传递的信息(声明)。将该JSON对象进行Base64编码,得到JWT的第二部分。

  4. 生成签名:将编码后的头部和负载以及密钥传递给选择的签名算法,生成签名。

  5. 拼接JWT:将编码后的头部、负载和签名用点号连接起来,形成完整的JWT。

JWT的验证过程

  1. 拆分JWT:接收到JWT后,通过点号将其拆分为头部、负载和签名。

  2. 验证签名:使用与生成JWT时相同的算法和密钥,对接收到的头部和负载进行签名验证,确保JWT的完整性和真实性。

  3. 检查声明:解码负载,获取其中的声明信息。这些声明可能包括用户ID、角色、权限等。

  4. 可选的过期检查:如果在负载中存在过期时间(exp)声明,可以检查JWT是否已过期,以确保令牌的有效性。

JWT的优势与注意事项

优势

  • 无需存储状态:JWT本身包含了所需的信息,服务端不需要在数据库中存储会话状态。

  • 跨域支持:由于JWT是基于标准的JSON格式,易于在不同域之间传递。

  • 轻量且自包含:JWT包含了头部、负载和签名,可以直接传递,减少了网络传输的开销。

注意事项

  • 信息安全性:JWT的负载虽然经过Base64编码,但并未加密,因此敏感信息不应放置在负载中。
  • 签名算法选择:选择合适的签名算法非常重要,不同场景可能需要不同级别的安全性。
  • 过期时间设置:合理设置JWT的过期时间,以确保令牌不会在长时间内持续有效。

github.com/dgrijalva/jwt-go/v4

在Go语言中,可以使用第三方库"github.com/dgrijalva/jwt-go/v4"来简化JWT的生成和验证过程。下面将使用该库来重新说明JWT的生成流程和验证过程。

JWT的生成流程(使用 "github.com/dgrijalva/jwt-go/v4")

  1. 导入必要的包:
import (
    "github.com/dgrijalva/jwt-go/v4"
)
  1. 创建一个Claims结构体,用于存储JWT的声明信息:
type MyClaims struct {
    jwt.StandardClaims
    UserID   int    `json:"user_id"`
    Username string `json:"username"`
}
  1. 生成JWT:
func GenerateJWT(userID int, username string, secretKey []byte) (string, error) {
	claims := MyClaims{
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: jwt.At(time.Now().Add(time.Hour * 24)), // 设置过期时间为一天
		},
		UserID:   userID,
		Username: username,
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	signedToken, err := token.SignedString(secretKey)
	if err != nil {
		return "", err
	}

	return signedToken, nil
}

JWT的验证过程(使用 "github.com/golang-jwt/jwt/v5")

  1. 导入必要的包:
import (
    "github.com/dgrijalva/jwt-go/v4"
)
  1. 定义验证函数:
func ValidateJWT(tokenString string, secretKey []byte) (*MyClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (interface{}, error) {
		return secretKey, nil
	})

	if err != nil {
		return nil, err
	}

	if claims, ok := token.Claims.(*MyClaims); ok && token.Valid {
		return claims, nil
	}

	return nil, jwt.ErrSignatureInvalid
}

在这个过程中,jwt.ParseWithClaims 函数将根据传入的token字符串、声明类型和验证函数来解析JWT。验证函数返回使用的密钥,以供库进行签名验证。

以上,我们使用 "github.com/golang-jwt/jwt/v5" 库重新描述了JWT的生成流程和验证过程。使用此库可以简化Go语言中JWT的处理,但仍需注意安全实践,例如正确设置过期时间、选择安全的密钥等。

结论

JWT作为一种轻量且安全的身份验证和授权方案,广泛应用于现代的网络应用中。了解其结构、生成流程和验证过程,能够帮助开发者更好地利用JWT来确保信息的安全传递。然而,在应用中使用JWT时,仍需注意合适的安全实践,以最大程度地保障系统的安全性。