用户鉴权之 JWT

214 阅读4分钟

JWT(JSON Web Tokens)是一种用于在网络应用程序之间安全传输信息的开放标准。它作为一个紧凑的、自包含的方式,将信息作为 JSON 对象进行编码,以数字签名的形式进行验证和信任。在本文中,我们将深入探讨 JWT 的原理、优缺点,并提供一个使用 Go 语言实现 JWT 的示例。

JWT 原理

JWT 由三个部分组成,分别是 Header、Payload 和 Signature。

Header

Header 部分通常由两部分信息组成:令牌的类型(即 JWT)和使用的加密算法,例如 HMAC SHA256 或 RSA。

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload

Payload 包含所传递的数据。该数据称为声明,有三种类型:注册声明、公共声明和私有声明。

注册声明是预定义的声明,它们不是必需的,但是推荐使用,因为它们有助于确保 JWT 的兼容性。

公共声明包含有关 JWT 的信息,例如发行人、过期时间等。

私有声明包含与使用 JWT 的应用程序相关的信息。但要注意的是,私有声明中的键名不能与注册声明或公共声明中的键名相同,以避免冲突。

下面是一个示例 Payload。

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

Signature

Signature 是使用 Header 和 Payload 中的数据以及一个秘钥,通过指定的加密算法计算出来的。

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

最终生成的 JWT 就是由这三部分组成的一个字符串,各部分之间使用点号(.)连接起来。

JWT 优缺点

优点

  • 轻量级:JWT 只是一个字符串,比较轻量级,方便传输。
  • 可扩展性:可以添加自定义的声明,方便传递各种数据。
  • 不需要在服务端保存会话信息:JWT 是自包含的,所以服务端不需要保存任何会话信息。
  • 支持跨语言使用:JWT 是基于标准的 JSON 格式,所以它可以被多种编程语言支持。

缺点

  • 安全性:JWT 在传输过程中使用的是 Base64 编码,而不是加密。如果密钥被泄漏,那么所有使用该密钥的 JWT 都会受到威胁。
  • 无法撤销:一旦签发了 JWT,就无法撤销它,除非它的过期时间到了。
  • 增加了网络负载:由于 JWT 中包含了一些声明信息,因此它的大小可能会比其他的认证方案更大,从而增加了网络负载。

Go 实现 JWT

在 Go 中,可以使用第三方库来实现 JWT 的生成和验证。在本文中,我们将使用 github.com/dgrijalva/jwt-go 这个库来实现 JWT 的生成和验证。

首先,需要安装该库:

go get github.com/dgrijalva/jwt-go

然后,可以使用以下代码来生成 JWT:

package main

import (
    "fmt"
    "time"

    "github.com/dgrijalva/jwt-go"
)

func main() {
    // 创建一个签名密钥
    mySigningKey := []byte("mysupersecretkey")

    // 创建一个 JWT
    claims := jwt.MapClaims{
        "username": "bob",
        "exp":      time.Now().Add(time.Hour * 24).Unix(),
    }
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    signedToken, err := token.SignedString(mySigningKey)
    if err != nil {
        fmt.Println("Error signing token:", err)
        return
    }

    // 输出 JWT
    fmt.Println(signedToken)
}

以上代码创建了一个包含 usernameexp 两个声明的 JWT,并使用 HS256 算法对其进行签名。最后输出的就是这个 JWT。

下面是使用相同密钥对 JWT 进行验证的示例代码:

package main

import (
    "fmt"
    "time"

    "github.com/dgrijalva/jwt-go"
)

func main() {
    // 创建一个签名密钥
    mySigningKey := []byte("mysupersecretkey")

    // 要验证的 JWT
    tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImJvYiIsImV4cCI6MTYyMzA2NDAyMn0.ry7VcJtRiZ7zfpYdSJ1knxVUKFtA9TwOj3qZ-XGDBjE"

    // 解析 JWT
    token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        // 检查签名算法是否为 HS256
        if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
        }
        // 返回签名密钥
        return mySigningKey, nil
    })
    if err != nil {
        fmt.Println("Error parsing token:", err)
        return
    }

    // 检查 JWT 是否有效
    if _, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
        fmt.Println("Token is valid")
    } else {
        fmt.Println("Token is invalid")
    }
}

以上代码将先使用相同的密钥对 JWT 进行解析,然后检查 JWT 是否有效。如果 JWT 是有效的,那么输出的就是 Token is valid

结论

在本文中,我们深入探讨了 JWT 的原理、优缺点,并提供了一个使用 Go 语言实现 JWT 的示例。JWT 是一种安全、灵活、简单的认证方案,可以帮助开发者构建安全的分布式系统。它具有以下几个优点:

  • 简单:JWT 可以使用 JSON 作为载荷,易于理解和使用。
  • 安全:JWT 可以使用密钥进行签名,保证数据的完整性和真实性。
  • 可扩展:JWT 中的载荷可以包含任意的信息,便于扩展。

当然,JWT 也有一些缺点:

  • 不适合存储敏感数据:由于 JWT 的信息是通过 Base64 编码后存储在 token 中的,因此不适合存储敏感数据,比如密码等。
  • 需要使用 HTTPS:由于 JWT 中的信息可以被解密,因此需要使用 HTTPS 来保证数据的安全传输。
  • Token 大小较大:由于 JWT 中包含了签名、头部和载荷信息,因此 Token 的大小较大,可能会增加网络负载。

总的来说,JWT 是一种非常有用的认证方案,特别是在构建分布式系统时。如果使用得当,它可以提高系统的安全性和可扩展性。

参考文献