JWT介绍
JWT (JSON Web Token) 是目前最流行的跨域认证解决方案之一,它是一个开放标准 (RFC 7519),定义了一种紧凑且自包含的方式,用于在各方之间安全地传输信息。 由于信息是经过数字签名的,所以可以被验证和信任。
JWT 的核心优势在于其无状态性。在传统的基于 Session 的认证中,服务器需要存储用户的会话信息。 而 JWT 自身就包含了所有需要验证的信息,服务器无需在后端保留任何用户状态,这极大地减轻了服务器的压力,并提高了系统的可伸缩性和可用性。
JWT 的核心结构
一个 JWT 本质上是一个由点 (.) 分隔的长字符串,这个字符串由三部分经过 Base64Url 编码后连接而成:
Header.Payload.Signature
一个典型的 JWT 如下所示:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
下面我们来逐一解析这三个部分:
-
头部 (Header) 头部通常由两部分组成:令牌的类型 (
typ),固定为 "JWT";以及所使用的签名算法 (alg),例如 HMAC SHA256 (HS256) 或 RSA (RS256)。json { "alg": "HS256", "typ": "JWT" }这个 JSON 对象会经过 Base64Url 编码,构成 JWT 的第一部分。 -
载荷 (Payload) 载荷部分包含了“声明 (Claims)”,这些是关于实体(通常是用户)和其他附加元数据的陈述。 声明分为三种类型:
- 注册声明 (Registered Claims):这是一组预定义的、非强制但建议使用的声明,以提供一组有用的、可互操作的声明。 常见的有:
iss(Issuer): 令牌的签发者。sub(Subject): 令牌的主题,通常是用户的唯一标识符。aud(Audience): 令牌的接收者。exp(Expiration Time): 令牌的过期时间戳。iat(Issued At): 令牌的签发时间。nbf(Not Before): 定义在此时间之前,令牌不可用。jti(JWT ID): 令牌的唯一标识符。
- 公共声明 (Public Claims):可以由使用者自由定义,但为了避免冲突,应该在 IANA JSON Web Token Registry 中注册或使用包含抗冲突命名空间的 URI。
- 私有声明 (Private Claims):这是在通信双方之间自定义的声明,用于共享信息。
重要提示:载荷部分同样是经过 Base64Url 编码的,这意味着它可以被轻易地解码和读取。 因此,绝对不能在载荷中存放任何敏感信息,如密码。
- 注册声明 (Registered Claims):这是一组预定义的、非强制但建议使用的声明,以提供一组有用的、可互操作的声明。 常见的有:
-
签名 (Signature) 签名的作用是保证 JWT 的完整性和真实性,防止数据在传输过程中被篡改。 它的生成过程如下:
- 获取经过 Base64Url 编码的头部。
- 获取经过 Base64Url 编码的载荷。
- 将两者用点 (
.) 连接起来。 - 使用头部 (
alg字段) 中指定的签名算法,配合一个只有服务器知道的密钥 (secret),对连接后的字符串进行签名。
例如,使用 HS256 算法的签名伪代码为:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
JWT 的工作与认证流程
JWT 主要应用于身份验证和信息交换。 其典型的认证流程如下:
- 用户登录:用户使用其凭据(如用户名和密码)向服务器发起登录请求。
- 令牌生成与分发:服务器验证用户凭据。如果成功,服务器会创建一个 JWT,其中包含用户的标识和其他必要信息,然后通过响应将其发送回客户端。
- 客户端存储:客户端收到 JWT 后,通常会将其存储在本地,例如浏览器的 Local Storage、Session Storage 或一个安全的 HttpOnly Cookie 中。
- 授权请求:当客户端需要访问受保护的 API 或资源时,它会在请求的
Authorization头中携带 JWT,通常使用Bearer模式。Authorization: Bearer <token> - 服务器验证:服务器在收到请求后,会从请求头中提取 JWT。接着,服务器会使用自己的密钥来验证令牌的签名。如果签名有效,服务器就可以信任该令牌中的信息,并根据其中的声明(如用户 ID、权限等)来授权用户的请求。
- 响应资源:一旦令牌验证成功,服务器就会处理该请求并返回相应的资源或数据。
如何保证 JWT 的安全
JWT 的安全性依赖于其签名机制和一系列安全最佳实践的结合:
- 核心安全机制:数字签名:签名确保了 JWT 在传输过程中没有被篡改(数据完整性),并且可以验证其签发者(认证与防伪)。 攻击者因为没有服务器的密钥,无法伪造出有效的签名。
- 始终使用 HTTPS:为了防止中间人攻击和令牌窃听,JWT 必须通过加密的 HTTPS 连接进行传输。
- 选择强签名算法:避免使用
none算法,并优先选择如 RS256(非对称)或 HS256(对称)等强加密算法。服务器应强制校验算法,不接受客户端指定的算法。 - 密钥管理:签名密钥必须被严格保密且足够复杂。定期轮换密钥是一个很好的安全习惯。
- 设置合理的过期时间 (
exp):为令牌设置一个较短的有效期,可以大大降低令牌被盗用后所带来的风险。 - 刷新令牌机制 (Refresh Token):为了平衡安全性和用户体验,通常会使用两种令牌:
- 访问令牌 (Access Token):生命周期短(如15分钟),用于访问资源。
- 刷新令牌 (Refresh Token):生命周期长(如7天),用于在访问令牌过期后获取新的访问令牌。
- 不在载荷中存放敏感数据:再次强调,JWT 的载荷是公开可读的,切勿存放密码、个人身份信息等敏感数据。
- 客户端安全存储:在 Web 应用中,将 JWT 存储在
HttpOnly类型的 Cookie 中可以有效防止 XSS (跨站脚本) 攻击窃取令牌。