JWT(JSON Web Token)

266 阅读8分钟

        JWT(JSON Web Token)是一种用于在各方之间作为 JSON 对象安全地传输信息的紧凑、URL 安全的方式。 JWT 由于其紧凑和自包含的特点,非常适合于在移动设备等资源受限的环境中使用。

使用场景

使用场景主要集中在身份验证和信息交换:

身份验证 - 单点登录(SSO)- JWT 常用于实现单点登录(Single Sign-On)机制,用户只需一次登录即可访问多个应用程序。JWT 作为令牌在各个系统之间传递,简化了身份验证过程。

Web 和移动应用- 在用户登录后,服务器生成一个 JWT 并返回给客户端。客户端将 JWT 存储在本地存储或会话存储中,每次发送请求时,将 JWT 添加到 HTTP 请求头的 Authorization 字段中。服务器通过验证 JWT 来识别用户身份,而无需每次请求都查询数据库。

安全数据传输- JWT 可以用于在各方之间安全地传输信息,因为它可以被签名并且经过加密处理。这确保了数据在传输过程中不被篡改,并且只有预定的接收者才能读取内容。

API 认证和授权 - API 服务通常使用 JWT 来认证和授权客户端请求。客户端在调用 API 时,将 JWT 附加到请求头中,API 服务通过验证 JWT 来确定请求的合法性和权限。

在微服务架构中,服务之间的通信可以通过 JWT 来进行认证和授权。每个服务可以生成和验证 JWT,从而减少对集中式身份验证服务器的依赖,提高系统的可靠性和可扩展性。

通信在一些分布式系统中,各个服务之间需要相互认证和授权,通过 JWT 可以实现这一目的。每个服务在发起请求时附加一个 JWT,接收服务通过验证 JWT 来确认请求的来源和合法性。

临时访问令牌 - JWT 可以用作临时访问令牌,为用户提供短期的权限。例如,用户请求一个临时链接来重置密码或访问某个资源,服务器生成一个短期有效的 JWT 并发送给用户,用户通过这个令牌可以在有限时间内完成相应操作。

无状态会话 - JWT 的自包含特性使其非常适合于无状态会话管理。服务器不需要在会话期间存储用户的会话数据,只需验证每次请求中的 JWT。这样可以减轻服务器的负担,提高系统的可扩展性。

安全性考虑

1. 秘密管理:确保用于签名的秘密密钥安全存储,不泄露给第三方。

2. 算法选择:选择安全的签名算法,避免使用已知弱点的算法。

3. 声明验证:在使用 JWT 时,验证所有的声明,特别是 exp(过期时间),以防止令牌重放攻击。

JWT 结构

具体结构可分为三个部分:头部(Header)、有效负载(Payload)和签名(Signature);

头部(Header)

头部通常由两部分组成:令牌类型(即 JWT)和签名算法(如 HMAC SHA256 或 RSA);

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

头部使用 Base64Url 编码。

Payload

声明(claims):声明有三种类型:

  • Registered claims:预定义的声明,如 iss(签发者),exp(过期时间),sub(主题),aud(受众)。
  • Public claims:可以在 IANA JSON Web Token Claims 注册表中注册的声明。
  • Private claims:双方之间自定义的声明。

示例:

{
"sub": "1234567890", // 主题声明,表示 JWT 的主题。通常是用户的唯一标识符
"name": "John Doe", // 自定义声明,表示用户的姓名
"admin": true //自定义声明,表示用户的管理员状态
} 使用 Base64Url 编码。

上述具体解释:

  • sub: "1234567890"

    • 含义:这个字段表示用户的唯一标识符。在此例中,用户的 ID 是 1234567890。
    • 用途:可以用于服务器端识别用户,例如从数据库中查询用户信息。
  • name: "John Doe"

    • 含义:这个字段表示用户的姓名。
    • 用途:可以用于显示或记录用户的姓名,例如在用户界面显示用户名称。
  • admin: true

    • 含义:这个字段表示用户是否具有管理员权限。
    • 用途:可以用于权限控制,例如在应用程序中决定用户是否可以访问管理员功能。

Signature(签名)

对头部和Payload进行签名,以确保 JWT 未被篡改。签名的生成过程如下:

  • 对头部和Payload进行 Base64Url 编码。
  • 使用指定的算法对编码后的头部和有效负载进行签名。
  • 将签名附加在 JWT 的最后一部分。

例如,对于使用 HMAC SHA256 算法生成的签名:

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

签名之后的数据示例: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

JWT 实现步骤

创建头部:

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

将其进行 Base64Url 编码: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

创建有效负载:

{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
将其进行 Base64Url 编码:eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

创建签名

使用编码后的头部和有效负载及秘密生成签名:

HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
) 生成的签名也进行 Base64Url 编码。

组合 JWT

将编码后的头部、有效负载和签名用点 (.) 连接起来,形成最终的 JWT:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

验证 JWT

  1. 分离 JWT 将接收到的 JWT 按点 (.) 分割成头部、有效负载和签名。
  2. 验证签名,使用相同的签名算法和秘密,对接收到的头部和有效负载生成签名,并与 JWT 中的签名进行比较。如果两者匹配,则验证通过。
  3. 验证声明,根据需求验证 exp(过期时间)、iss(签发者)等声明。

下面是一个使用 Python 的示例,展示如何创建和验证 JWT:

import jwt
import datetime

# 创建 JWT
def create_jwt(secret):
header = {"alg": "HS256", "typ": "JWT"}
payload = {
"sub": "1234567890",
"name": "John Doe",
"admin": True,
"exp": datetime.datetime.utcnow() + datetime.timedelta(hours=1)
}
token = jwt.encode(payload, secret, algorithm="HS256", headers=header)
return token

# 验证 JWT
def verify_jwt(token, secret):
try:
payload = jwt.decode(token, secret, algorithms=["HS256"])
return payload
except jwt.ExpiredSignatureError:
return "Token has expired"
except jwt.InvalidTokenError:
return "Invalid token"

secret = "my_secret_key"
token = create_jwt(secret)
print("JWT:", token)

result = verify_jwt(token, secret)
print("Verification result:", result)
上述示例中,使用了 PyJWT 库来创建和验证 JWT。你可以根据需求调整声明和签名算法。

JWT 优缺点,安全性如何

优点

  1. 无状态和可扩展性

    • 无状态:JWT 是自包含的,不需要在服务器端存储会话状态信息,所有必要信息都包含在令牌中。
    • 可扩展性:由于不需要在服务器上维护会话状态,JWT 非常适合分布式系统和微服务架构。
  2. 性能

    • 效率高:由于服务器不需要查询数据库来验证每个请求的用户状态,只需要验证 JWT 的签名,因此可以减少服务器的负载。
  3. 灵活性

    • 多平台支持:JWT 可以跨多个平台使用,包括 Web、移动和桌面应用。
    • 广泛的语言支持:几乎所有的编程语言都支持 JWT,方便集成。
  4. 安全性

    • 签名和加密:JWT 的有效负载经过签名,确保了数据的完整性和真实性。可以选择加密 JWT 以增加安全性。
  5. 简单性

    • 易于实现:JWT 的结构简单明了,易于生成和解析。

缺点

  1. 令牌大小

    • 较大:相比于传统的会话 ID,JWT 由于包含了更多的信息,通常会比较大,可能会增加传输的开销。
  2. 不可撤销性

    • 不可撤销:一旦 JWT 签发出去,就无法在服务器端撤销(除非实现一个复杂的黑名单机制),这意味着在令牌到期之前,如果泄露了,可能会造成安全隐患。
  3. 有效期管理

    • 短期有效:为了安全性,JWT 通常设置较短的有效期,这意味着客户端需要频繁地刷新令牌。
  4. 安全隐患

    • 存储风险:如果 JWT 存储不当(如存储在浏览器的 localStorage 中),可能会被跨站脚本(XSS)攻击窃取。
    • 算法攻击:使用不安全的签名算法(如 HS256)可能会导致安全漏洞

安全性考虑

使用 HTTPS

确保在传输 JWT 时使用 HTTPS 以防止中间人攻击。

选择安全的签名算法

  1. 使用强大的签名算法,如 RS256 或 ES256,避免使用已知弱点的算法如 HS256。

设置合理的过期时间

  1. 设置较短的过期时间 exp 声明,并实现令牌刷新机制,以减少被盗用的风险。

存储安全

  1. 在客户端安全地存储 JWT,避免使用 localStorage,建议使用更安全的存储方式,如 HttpOnly cookie。

验证声明

  1. 在使用 JWT 时,验证所有必要的声明,如 iss(签发者),aud(受众),exp(过期时间)等,确保令牌的合法性。

定期轮换密钥

  1. 定期更换用于签名的密钥,并确保旧密钥不再被使用。