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
- 分离 JWT 将接收到的 JWT 按点 (
.) 分割成头部、有效负载和签名。 - 验证签名,使用相同的签名算法和秘密,对接收到的头部和有效负载生成签名,并与 JWT 中的签名进行比较。如果两者匹配,则验证通过。
- 验证声明,根据需求验证
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 优缺点,安全性如何
优点
-
无状态和可扩展性
- 无状态:JWT 是自包含的,不需要在服务器端存储会话状态信息,所有必要信息都包含在令牌中。
- 可扩展性:由于不需要在服务器上维护会话状态,JWT 非常适合分布式系统和微服务架构。
-
性能
- 效率高:由于服务器不需要查询数据库来验证每个请求的用户状态,只需要验证 JWT 的签名,因此可以减少服务器的负载。
-
灵活性
- 多平台支持:JWT 可以跨多个平台使用,包括 Web、移动和桌面应用。
- 广泛的语言支持:几乎所有的编程语言都支持 JWT,方便集成。
-
安全性
- 签名和加密:JWT 的有效负载经过签名,确保了数据的完整性和真实性。可以选择加密 JWT 以增加安全性。
-
简单性
- 易于实现:JWT 的结构简单明了,易于生成和解析。
缺点
-
令牌大小
- 较大:相比于传统的会话 ID,JWT 由于包含了更多的信息,通常会比较大,可能会增加传输的开销。
-
不可撤销性
- 不可撤销:一旦 JWT 签发出去,就无法在服务器端撤销(除非实现一个复杂的黑名单机制),这意味着在令牌到期之前,如果泄露了,可能会造成安全隐患。
-
有效期管理
- 短期有效:为了安全性,JWT 通常设置较短的有效期,这意味着客户端需要频繁地刷新令牌。
-
安全隐患
- 存储风险:如果 JWT 存储不当(如存储在浏览器的 localStorage 中),可能会被跨站脚本(XSS)攻击窃取。
- 算法攻击:使用不安全的签名算法(如 HS256)可能会导致安全漏洞
安全性考虑
使用 HTTPS
确保在传输 JWT 时使用 HTTPS 以防止中间人攻击。
选择安全的签名算法
- 使用强大的签名算法,如 RS256 或 ES256,避免使用已知弱点的算法如 HS256。
设置合理的过期时间
- 设置较短的过期时间
exp声明,并实现令牌刷新机制,以减少被盗用的风险。
存储安全
- 在客户端安全地存储 JWT,避免使用 localStorage,建议使用更安全的存储方式,如 HttpOnly cookie。
验证声明
- 在使用 JWT 时,验证所有必要的声明,如
iss(签发者),aud(受众),exp(过期时间)等,确保令牌的合法性。
定期轮换密钥
- 定期更换用于签名的密钥,并确保旧密钥不再被使用。