本文收录于
Github.com/niumoo/JavaNotes
,Java 系列文档,数据结构与算法!
本文收录于网站:
https://www.wdbyte.com/
,我的公众号:
程序猿阿朗

JSON Web Token

JWT
)是一种可以在多方之间安全共享数据的开放标准,JWT 数据经过编码和数字签名生成,可以确保其真实性,也因此 JWT 通常用于身份认证。这篇文章会介绍什么是 JWT,JWT 的应用场景以及组成结构,最后分析它的优点及局限性。

传统认证方式的问题

在介绍 JWT 之前,先看下传统认证方式有什么问题,这里的传统认证方式是指
Session-Cookie
方式。有小伙伴可能要说了,传统认证方式能有什么问题,如果有问题肯定是你代码写的不好。其实不是,有些问题存在于方案本身。

先来看一下传统的认证方式流程:

  1. 客户端输入用户名密码,点击登录。
  2. 服务端验证用户名密码,校验通过,服务端存储 Session 数据,如身份,权限。
  3. 服务端响应 Cookie,一般内容是一个 Session ID,客户端收到 Cookie 后存储。
  4. 客户端后续请求携带 Cookie 作为身份认证凭据,服务端验证 Cookie 得知用户身份。

这是常见的认证流程,但是这种认证方式存在下面几个问题。

状态存储负担

Session-Cookie
方式因为服务端要存储当前会话信息,而且必不可少, 这就额外增加了存储负担,而且在分布式系统中,还要考虑不同机器之间的会话状态同步问题。有时还需要部署独立的认证服务。不易维护。

跨域问题

基于 Cookie 会话的认证方式,在进行跨域请求时存在难点,Cookie 不会跟随跨域请求。认证信息带不过去,当然,聪明的小伙伴可以通过设置客户端参数,配置服务端参数等操作来允许跨域,不过有点麻烦了。

CSRF 攻击风险

CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种常见的 Web 安全漏洞。有些小伙伴不知道是什么意思,看下面流程:

  1. 你登录了 QQ 空间,传统认证方式,客户端成功存储了 Cookie。
  2. 你的 “好友X” 给你发了一个链接,标题 “专家:吃淀粉肠活不过三年”。
  3. 突然你感觉仿佛昨晚淀粉肠的香味还没散去,你迫不及待的点开了,没想到自动跳转到了 QQ 空间,你感觉莫名其妙,大失所望,立马关掉。
  4. 但是没想到的是这个跳转请求了空间说说发表接口,因为你之前登录过,Cookie 状态还在。说说直接发表成功了。那马上可能就有好友问你空间发的乱七八糟的内容是什么意思了。

QQ 空间曾经也确实出现过 CSRF 漏洞。

在解决这几个问题上,JWT 具有天然优势,它存储在客户端,服务端无状态。Token 可以不存在 Cookie 中,轻松跨域又减少了 CSRF 风险。

JWT 是什么

JWT(JSON Web Tokens)它定义了一种
紧凑

自包含
的方式用于在各方之间作为 JSON 对象安全地传递信息。紧凑意味着内容尽可能的短小。自包含意味着内容中包含了身份信息。这个信息可以用于
身份认证
,也可以用于
信息交换
。由于信息会使用
密钥进行数字签名
,因此JWT 可以
被验证
以及信任。

JWT 应用场景

常见的 JWT 应用常见有 JWT 授权和信息交换:

  • 授权
    :JWT 被应用最多的场景,用户登录后服务端响应一个 JWT,后续的请求都携带 JWT内容,以此验证用户身份。使用 JWT 可以进行单点登录,可以跨域。
  • 信息交换
    :因为 JWT 需要使用密钥进行签名,因此使用 JWT 安全的传输信息也是一个好方法,签名可以确保消息发送人没有问题,确保消息没有被篡改。

JWT 组成结构

JWT 由
小数点
分割的三部分组成,如
xxxxx.yyyyy.zzzzz
,这三部分对应的是的
标头(Header)、负载(Payload)、签名(Signature)
,每部分使用
Base64Url
进行编码,

下面是一个真实 JWT 示例:

注意:JWT 为紧凑形式,没有换行,这里为了方便阅读,进行了换行。

标头 Header

Header 部分 Base64Url 解码后可以看到两个字段,
alg
指定签名算法,
typ
指定 Token 类型。

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

对上述标头对象进行 Base64Url 编码以形成 JWT 的第一部分。

负载 Payload

第二部分中存放了实际需要的数据,用户可以自定义内容,如用户身份信息。

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

同时 JWT 也规定了几个官方字段:

iss (issuer):签发人
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
exp (expiration time):过期时间
iat (Issued At):签发时间
jti (JWT ID):编号

可以看到不管第一部分还是第二部分,字段名都是三位字母,这也是为了内容的紧凑。对 JWT 负载进行 Base64Url 编码以形成 JWT 的第二部分。

特别注意:由于只要 Base64Url 解码就可以看到第二部分内容,因此不能在 Payload 中存储敏感信息。

签名 Signature

签名 Signature 的生成依赖
标头 Header

负载 Payload
,同时要有拥有用于签名的
密钥
,因此签名可以用于验证 JWT 的发送者是否正确,并确保消息没有被篡改。

Signature = HMACSHA256(
  base64UrlEncode(header) + '.' +
  base64UrlEncode(payload),
  secret)

JWT 官网也提供
JWT 在线解码验证工具
,可以访问查看。

JWT 身份认证

使用 JWT 进行身份认证的工作流程如下:

  1. 用户使用登录凭证(如用户名和密码)进行登录。

  2. 服务器验证凭证的正确性,并创建一个包含用户信息的 JWT。

  3. 服务器对 JWT 进行签名,然后将其发送回用户。

  4. 用户将 JWT 存储在客户端(如 localStorage),并在随后的请求中随同发送。

    如添加到请求头:
    Authorization: Bearer <token>

  5. 服务器在接收到请求后,验证 JWT 的签名并解析其内容,确认用户的身份,然后返回请求的数据。

  6. JWT 可能在一定时间后过期,用户需要重新登录获取新的 JWT。

JWT 的特点

JWT 有下面几个特点。

  1. 紧凑
    :JWT 设计十分紧凑,结果较小,可以在参数,请求头中传输。
  2. 自包含
    :JWT 自身包含了用户验证的所需信息,避免了多次查询数据库。
  3. 跨语言
    :JWT 使用 JSON 格式,现代编程语言都有对 JSON 的支持。
  4. 安全性
    :JWT 需要使用密钥进行数据签名,密钥不泄露,JWT 就是安全的。但是因为 JWT 自包含和 Base64Url 编码特性,JWT 中的信息可以被直接读取,因此建议使用 HTTPS 协议。如果对安全性要求较高,
    还可以对 JWT 内容在进行一次加密
    (如 AES)。
  5. 分布式环境友好
    :因为 JWT 在服务端无状态,因此 JWT 适用于单点登录,同时可以
    跨域
  6. 不可撤销
    :一旦 JWT 签发了,在有效期内将会一直有效,除非服务器增加额外逻辑来强制撤销某个 JWT Token,如黑名单机制。
  7. 性能问题
    :虽然避免了查询数据库,但是服务器仍需对每个请求中的 JWT 进行解码和验证,如果请求量巨大,这也可能成为性能瓶颈。

JWT 最佳实践

JWT 存在优点,也有很多风险与挑战,参考前人的最佳实践可以少走弯路。

  1. 内容紧凑最小化:最小限度的减少 JWT 负载中的内容,避免存储敏感数据,只存储重要数据。某些服务器不接受大于 8KB 的请求头。
  2. 验证必不可少:每次收到 JWT 都要进行验证,内容验证,过期时间验证。发行者验证。
  3. 使用 JWT 库:不要自己编写 JWT 类库,密码学和安全都是非常复杂的东西,使用专业的类库好过自己编写。
  4. JWT 过期时间:设计合理的过期时间,因为 JWT 一旦颁发,无法删除。过长的有效期存在风险。

总体而言,JWT 提供了一种相对简单且有效的方式来处理身份验证问题,但是需要注意JWT 安全性和细节问题,以确保 JWT 可以在应用中正确且安全地使用。

预告:下一篇文章会介绍如何在 Java 中使用 JWT 进行身份验证。

参考

预告下一篇:Java 中如何使用 JWT 进行身份认证

本文收录于
Github.com/niumoo/JavaNotes
,Java 系列文档,数据结构与算法!
本文收录于网站:
https://www.wdbyte.com/
,我的公众号:
程序猿阿朗

标签: none

添加新评论