JWT(JSON Web Token)是一种用于在各方之间作为 JSON 对象安全传输信息的紧凑、URL 安全的令牌。它广泛用于身份验证和信息交换。以下是对 JWT 的详细介绍:
JWT 的组成
JWT 由三部分组成,每部分之间用点(.)分隔:
- Header(头部) :包含令牌的类型(通常是 "JWT")和签名算法(如 HMAC SHA256 或 RSA)。
- Payload(负载) :包含声明(claims),即需要传输的信息。声明可以是关于用户的信息,也可以是其他元数据。
- Signature(签名) :用于验证消息的发送者和确保消息在传输过程中未被篡改。
一个典型的 JWT 看起来像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
为什么要使用 JWT
- 无状态性:JWT 是无状态的,服务器不需要存储会话信息。所有必要的信息都包含在令牌本身中,这使得它非常适合分布式系统。
- 安全性:JWT 可以使用签名来验证令牌的真实性和完整性。使用 HMAC 算法时,只有持有密钥的一方才能生成有效的签名。使用 RSA 或 ECDSA 算法时,只有持有私钥的一方才能生成有效的签名,而任何持有公钥的一方都可以验证签名。
- 紧凑性:JWT 是 URL 安全的,可以通过 URL、POST 参数或 HTTP 头部传输。它们的紧凑性使得它们非常适合在 HTTP 头部中传输。
- 跨域支持:由于 JWT 是无状态的,它们可以在不同的域之间传输,这使得它们非常适合单点登录(SSO)场景。
使用 JWT 的好处
- 简化身份验证:JWT 可以在不同的服务之间传递用户身份信息,简化了身份验证流程。
- 减少服务器负担:由于 JWT 是无状态的,服务器不需要存储会话信息,从而减少了服务器的负担。
- 灵活性:JWT 可以包含任意的声明,允许在令牌中传递自定义信息。
- 标准化:JWT 是一个开放标准(RFC 7519),有广泛的支持和实现。
在 JWT 出现之前的一般使用机制
在 JWT 出现之前,常见的身份验证机制包括:
- Session(会话) :服务器在用户登录时创建一个会话,并在服务器端存储会话信息。客户端通过 Cookie 或 URL 参数传递会话 ID。服务器根据会话 ID 查找会话信息来验证用户身份。
- OAuth 1.0:OAuth 1.0 是一种授权协议,允许第三方应用访问用户资源而不暴露用户的凭据。OAuth 1.0 使用复杂的签名机制来确保请求的安全性。
- API Key:API Key 是一种简单的身份验证机制,客户端在每次请求时都包含一个唯一的密钥。服务器根据密钥验证请求的合法性。
总的来说,JWT 提供了一种简洁、高效且安全的方式来进行身份验证和信息交换,特别适合现代分布式系统和微服务架构。
JWT 的生成
生成 JWT 通常包括以下几个步骤:
-
创建 Header:Header 是一个 JSON 对象,指定了令牌的类型和签名算法。例如:
{ "alg": "HS256", "typ": "JWT" }然后将这个 JSON 对象进行 Base64Url 编码。
-
创建 Payload:Payload 是一个 JSON 对象,包含需要传输的声明(claims)。例如:
{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }同样,将这个 JSON 对象进行 Base64Url 编码。
-
创建 Signature:使用指定的签名算法对 Header 和 Payload 进行签名。签名的生成方式如下:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret )其中 secret 是服务器端的密钥。
-
生成 JWT:将编码后的 Header、Payload 和 Signature 用点(.)连接起来,形成最终的 JWT。例如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
JWT 的解析
解析 JWT 包括以下几个步骤:
-
分割 JWT:将 JWT 按照点(
.)分割成三部分:Header、Payload 和 Signature。 -
解码 Header 和 Payload:对 Header 和 Payload 进行 Base64Url 解码,得到 JSON 对象。例如:
{ "alg": "HS256", "typ": "JWT" }和
{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 } -
验证 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)时,生成方和解析方使用相同的密钥。流程如下:
- 生成方:使用密钥生成 JWT。
- 解析方:使用相同的密钥验证 JWT 的签名。
在这种情况下,密钥必须在生成方和解析方之间安全地共享和保管。任何持有密钥的一方都可以生成和验证 JWT。
非对称加密(如 RSA 或 ECDSA)
在使用非对称加密算法(如 RSA 或 ECDSA)时,生成方和解析方使用不同的密钥对:
- 生成方:使用私钥生成 JWT。
- 解析方:使用公钥验证 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
在非对称加密中,公钥可以随意发放的原因是公钥本身并不能用于解密或伪造签名。非对称加密使用一对密钥:公钥和私钥。它们具有以下特性:
- 公钥加密,私钥解密:使用公钥加密的数据只能使用对应的私钥解密。公钥公开发布后,任何人都可以使用它加密数据,但只有持有私钥的人才能解密这些数据。
- 私钥签名,公钥验证:使用私钥生成的签名可以使用对应的公钥进行验证。公钥公开发布后,任何人都可以使用它验证签名的真实性,但只有持有私钥的人才能生成有效的签名。
公钥的作用
- 加密数据:公钥用于加密数据,确保只有持有私钥的一方可以解密。
- 验证签名:公钥用于验证签名,确保签名是由持有私钥的一方生成的,并且数据未被篡改。
私钥的作用
- 解密数据:私钥用于解密由公钥加密的数据。
- 生成签名:私钥用于生成签名,确保签名的唯一性和数据的完整性。
为什么不怕泄漏公钥
公钥的设计初衷就是公开发布,它的泄漏不会带来安全风险,因为:
- 公钥不能解密数据:公钥只能加密数据,不能解密数据。只有私钥才能解密由公钥加密的数据。
- 公钥不能生成签名:公钥只能验证签名,不能生成签名。只有私钥才能生成有效的签名。
因此,公钥的泄漏不会影响数据的安全性和签名的完整性。相反,私钥必须严格保密,因为私钥的泄漏会导致数据被解密或签名被伪造。
示例
假设我们有一对密钥对:公钥和私钥。
-
加密和解密:
- 使用公钥加密数据,任何人都可以加密,但只有持有私钥的人才能解密。
- 公钥泄漏不会影响数据的安全性,因为没有私钥,数据无法解密。
-
签名和验证:
- 使用私钥生成签名,只有持有私钥的人才能生成有效签名。
- 使用公钥验证签名,任何人都可以验证签名的真实性。
- 公钥泄漏不会影响签名的完整性,因为没有私钥,无法生成有效签名。
总结
非对称加密的安全性依赖于私钥的保密性,而公钥的设计目的是公开发布,以便其他人可以加密数据或验证签名。因此,公钥的泄漏不会带来安全风险。