JWT

132 阅读9分钟

JWT(JSON Web Token)是一种用于在各方之间作为 JSON 对象安全传输信息的紧凑、URL 安全的令牌。它广泛用于身份验证和信息交换。以下是对 JWT 的详细介绍:

JWT 的组成

JWT 由三部分组成,每部分之间用点(.)分隔:

  1. Header(头部) :包含令牌的类型(通常是 "JWT")和签名算法(如 HMAC SHA256 或 RSA)。
  2. Payload(负载) :包含声明(claims),即需要传输的信息。声明可以是关于用户的信息,也可以是其他元数据。
  3. Signature(签名) :用于验证消息的发送者和确保消息在传输过程中未被篡改。

一个典型的 JWT 看起来像这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

为什么要使用 JWT

  1. 无状态性:JWT 是无状态的,服务器不需要存储会话信息。所有必要的信息都包含在令牌本身中,这使得它非常适合分布式系统。
  2. 安全性:JWT 可以使用签名来验证令牌的真实性和完整性。使用 HMAC 算法时,只有持有密钥的一方才能生成有效的签名。使用 RSA 或 ECDSA 算法时,只有持有私钥的一方才能生成有效的签名,而任何持有公钥的一方都可以验证签名。
  3. 紧凑性:JWT 是 URL 安全的,可以通过 URL、POST 参数或 HTTP 头部传输。它们的紧凑性使得它们非常适合在 HTTP 头部中传输。
  4. 跨域支持:由于 JWT 是无状态的,它们可以在不同的域之间传输,这使得它们非常适合单点登录(SSO)场景。

使用 JWT 的好处

  1. 简化身份验证:JWT 可以在不同的服务之间传递用户身份信息,简化了身份验证流程。
  2. 减少服务器负担:由于 JWT 是无状态的,服务器不需要存储会话信息,从而减少了服务器的负担。
  3. 灵活性:JWT 可以包含任意的声明,允许在令牌中传递自定义信息。
  4. 标准化:JWT 是一个开放标准(RFC 7519),有广泛的支持和实现。

在 JWT 出现之前的一般使用机制

在 JWT 出现之前,常见的身份验证机制包括:

  1. Session(会话) :服务器在用户登录时创建一个会话,并在服务器端存储会话信息。客户端通过 Cookie 或 URL 参数传递会话 ID。服务器根据会话 ID 查找会话信息来验证用户身份。
  2. OAuth 1.0:OAuth 1.0 是一种授权协议,允许第三方应用访问用户资源而不暴露用户的凭据。OAuth 1.0 使用复杂的签名机制来确保请求的安全性。
  3. API Key:API Key 是一种简单的身份验证机制,客户端在每次请求时都包含一个唯一的密钥。服务器根据密钥验证请求的合法性。

总的来说,JWT 提供了一种简洁、高效且安全的方式来进行身份验证和信息交换,特别适合现代分布式系统和微服务架构。

JWT 的生成

生成 JWT 通常包括以下几个步骤:

  1. 创建 Header:Header 是一个 JSON 对象,指定了令牌的类型和签名算法。例如:

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

    然后将这个 JSON 对象进行 Base64Url 编码。

  2. 创建 Payload:Payload 是一个 JSON 对象,包含需要传输的声明(claims)。例如:

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

    同样,将这个 JSON 对象进行 Base64Url 编码。

  3. 创建 Signature:使用指定的签名算法对 Header 和 Payload 进行签名。签名的生成方式如下:

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

    其中 secret 是服务器端的密钥。

  4. 生成 JWT:将编码后的 Header、Payload 和 Signature 用点(.)连接起来,形成最终的 JWT。例如:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
    

JWT 的解析

解析 JWT 包括以下几个步骤:

  1. 分割 JWT:将 JWT 按照点(.)分割成三部分:Header、Payload 和 Signature。

  2. 解码 Header 和 Payload:对 Header 和 Payload 进行 Base64Url 解码,得到 JSON 对象。例如:

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

    {   "sub": "1234567890",   "name": "John Doe",   "iat": 1516239022 }
    
  3. 验证 Signature:使用相同的签名算法和密钥对 Header 和 Payload 进行签名,并将生成的签名与 JWT 中的 Signature 进行比较。如果签名匹配,则说明 JWT 是有效的且未被篡改。

示例代码

以下是使用 Python 和 pyjwt 库生成和解析 JWT 的示例代码:

生成 JWT
import jwt
import datetime

# 定义密钥
secret = 'your-256-bit-secret'

# 创建 Header
header = {
    "alg": "HS256",
    "typ": "JWT"
}

# 创建 Payload
payload = {
    "sub": "1234567890",
    "name": "John Doe",
    "iat": datetime.datetime.utcnow()
}

# 生成 JWT
token = jwt.encode(payload, secret, algorithm="HS256", headers=header)
print(token)
解析 JWT
import jwt

# 定义密钥
secret = 'your-256-bit-secret'

# 解析 JWT
decoded_payload = jwt.decode(token, secret, algorithms=["HS256"])
print(decoded_payload)

QA 1

Question

所以密钥是生成方和解析方沟通好后各自保管的?

Answer

是的,密钥在 JWT 的生成和解析过程中起着至关重要的作用。具体来说:

对称加密(如 HMAC SHA256)

在使用对称加密算法(如 HMAC SHA256)时,生成方和解析方使用相同的密钥。流程如下:

  1. 生成方:使用密钥生成 JWT。
  2. 解析方:使用相同的密钥验证 JWT 的签名。

在这种情况下,密钥必须在生成方和解析方之间安全地共享和保管。任何持有密钥的一方都可以生成和验证 JWT。

非对称加密(如 RSA 或 ECDSA)

在使用非对称加密算法(如 RSA 或 ECDSA)时,生成方和解析方使用不同的密钥对:

  1. 生成方:使用私钥生成 JWT。
  2. 解析方:使用公钥验证 JWT 的签名。

在这种情况下,私钥应由生成方妥善保管,不得泄露。公钥可以公开发布,供解析方使用。非对称加密的优势在于,公钥可以安全地分发,而不必担心泄露私钥。

示例代码

以下是使用 Python 和 pyjwt 库的示例代码,展示了对称加密和非对称加密的生成和解析过程:

对称加密(HMAC SHA256)
import jwt
import datetime

# 定义密钥
secret = 'your-256-bit-secret'

# 创建 Payload
payload = {
    "sub": "1234567890",
    "name": "John Doe",
    "iat": datetime.datetime.utcnow()
}

# 生成 JWT
token = jwt.encode(payload, secret, algorithm="HS256")
print("Generated JWT:", token)

# 解析 JWT
decoded_payload = jwt.decode(token, secret, algorithms=["HS256"])
print("Decoded Payload:", decoded_payload)
非对称加密(RSA)
import jwt
import datetime

# 生成 RSA 密钥对(在实际应用中,应使用安全的密钥生成和存储方式)
private_key = '''-----BEGIN RSA PRIVATE KEY-----MIIEpAIBAAKCAQEA1...-----END RSA PRIVATE KEY-----'''

public_key = '''-----BEGIN PUBLIC KEY--MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1...-----END PUBLIC KEY-----'''

# 创建 Payload
payload = {
    "sub": "1234567890",
    "name": "John Doe",
    "iat": datetime.datetime.utcnow()
}

# 生成 JWT
token = jwt.encode(payload, private_key, algorithm="RS256")
print("Generated JWT:", token)

# 解析 JWT
decoded_payload = jwt.decode(token, public_key, algorithms=["RS256"])
print("Decoded Payload:", decoded_payload)

QA 2

Question

为什么在非对称加密中,公钥可以随意发放,为什么不怕泄漏公钥?

Answer

在非对称加密中,公钥可以随意发放的原因是公钥本身并不能用于解密或伪造签名。非对称加密使用一对密钥:公钥和私钥。它们具有以下特性:

  1. 公钥加密,私钥解密:使用公钥加密的数据只能使用对应的私钥解密。公钥公开发布后,任何人都可以使用它加密数据,但只有持有私钥的人才能解密这些数据。
  2. 私钥签名,公钥验证:使用私钥生成的签名可以使用对应的公钥进行验证。公钥公开发布后,任何人都可以使用它验证签名的真实性,但只有持有私钥的人才能生成有效的签名。

公钥的作用

  1. 加密数据:公钥用于加密数据,确保只有持有私钥的一方可以解密。
  2. 验证签名:公钥用于验证签名,确保签名是由持有私钥的一方生成的,并且数据未被篡改。

私钥的作用

  1. 解密数据:私钥用于解密由公钥加密的数据。
  2. 生成签名:私钥用于生成签名,确保签名的唯一性和数据的完整性。

为什么不怕泄漏公钥

公钥的设计初衷就是公开发布,它的泄漏不会带来安全风险,因为:

  • 公钥不能解密数据:公钥只能加密数据,不能解密数据。只有私钥才能解密由公钥加密的数据。
  • 公钥不能生成签名:公钥只能验证签名,不能生成签名。只有私钥才能生成有效的签名。

因此,公钥的泄漏不会影响数据的安全性和签名的完整性。相反,私钥必须严格保密,因为私钥的泄漏会导致数据被解密或签名被伪造。

示例

假设我们有一对密钥对:公钥和私钥。

  • 加密和解密

    • 使用公钥加密数据,任何人都可以加密,但只有持有私钥的人才能解密。
    • 公钥泄漏不会影响数据的安全性,因为没有私钥,数据无法解密。
  • 签名和验证

    • 使用私钥生成签名,只有持有私钥的人才能生成有效签名。
    • 使用公钥验证签名,任何人都可以验证签名的真实性。
    • 公钥泄漏不会影响签名的完整性,因为没有私钥,无法生成有效签名。

总结

非对称加密的安全性依赖于私钥的保密性,而公钥的设计目的是公开发布,以便其他人可以加密数据或验证签名。因此,公钥的泄漏不会带来安全风险。