JWT基础概念

什么是JWT

JWT (JSON Web Token) 是目前最流行的跨域认证解决方案,是一种基于 Token 的认证授权机制。 从 JWT 的全称可以看出,JWT 本身也是 Token,一种规范化之后的 JSON 结构的 Token。

JWT 自身包含了身份验证所需要的所有信息,因此,我们的服务器不需要存储 Session 信息。这显然增加了系统的可用性和伸缩性,大大减轻了服务端的压力。

可以看出,JWT 更符合设计 RESTful API 时的「Stateless(无状态)」原则

并且, 使用 JWT 认证可以有效避免 CSRF 攻击,因为 JWT 一般是存在在 localStorage 中, 每次请求在header中带上Token。

JWT解决了什么问题

  1. 身份验证(Authentication):JWT允许用户在不必存储用户会话状态或在每次请求时进行数据库查询的情况下进行身份验证。当用户成功登录后,JWT会生成一个包含用户信息的令牌,并将其发送给客户端。客户端随后可以将该令牌包含在每个后续请求中,以证明其身份。服务端可以验证令牌的完整性和签名,从而信任用户的身份。

  2. 授权(Authorization):JWT可以用于确定用户是否具有访问特定资源或执行特定操作的权限。负载中可以包含许可和角色信息,服务端可以根据这些信息来做出授权决策,而无需每次请求都进行数据库查询。

  3. 信息交换(Information Exchange):JWT是一种紧凑的数据格式,便于在不同系统和服务之间传递信息。它的通用性使得不同系统之间能够共享数据,而无需深度耦合或协商特定的数据格式。

  4. 单点登录(Single Sign-On, SSO):JWT可以用于实现单点登录,允许用户在一次登录后访问多个关联的应用程序或服务而无需重新登录。用户登录后,他们会收到一个包含其身份信息的JWT,然后可以在多个应用程序之间传递这个令牌,从而实现无缝的身份验证。

  5. 跨域通信(Cross-Origin Communication):由于JWT的自包含性,它可以在跨域通信中发挥作用。当一个前端应用程序需要与不同域的后端API通信时,可以使用JWT来传递身份验证信息。

  6. 减少服务器端存储(Reduced Server-Side State):JWT不需要在服务器端存储会话状态或用户会话信息,这有助于提高服务器的可伸缩性和减少服务器负担。

  7. 分布式系统(Distributed Systems):JWT适用于分布式系统中的身份验证和授权,因为它可以轻松传递用户信息和权限信息,而不需要在每个服务之间共享状态。

JWT由哪些部分组成

Untitled

JWT 本质上就是一组字串,通过(.)切分成三个为 Base64 编码的部分:

  • Header : 描述 JWT 的元数据,定义了生成签名的算法以及 Token 的类型。
  • Payload : 用来存放实际需要传递的数据
  • Signature(签名) :服务器通过 Payload、Header 和一个密钥(Secret)使用 Header 里面指定的签名算法(默认是 HMAC SHA256)生成。

可以在 jwt.io 这个网站上对其 JWT 进行解码,解码之后得到的就是 Header、Payload、Signature 这三部分。

Header 通常由两部分组成:

  • typ(Type):令牌类型,也就是 JWT。
  • alg(Algorithm) :签名算法,比如 HS256。

示例:

1
2
3
4
{
"alg": "HS256",
"typ": "JWT"
}

JSON 形式的 Header 被转换成 Base64 编码,成为 JWT 的第一部分。

Payload

Payload 也是 JSON 格式数据,其中包含了 Claims(声明,包含 JWT 的相关信息)。

Claims 分为三种类型:

  • Registered Claims(注册声明) :预定义的一些声明,建议使用,但不是强制性的。
  • Public Claims(公有声明) :JWT 签发方可以自定义的声明,但是为了避免冲突,应该在 IANA JSON Web Token Registry 中定义它们。
  • Private Claims(私有声明) :JWT 签发方因为项目需要而自定义的声明,更符合实际项目场景使用。

下面是一些常见的注册声明:

  • iss(issuer):JWT 签发方。
  • iat(issued at time):JWT 签发时间。
  • sub(subject):JWT 主题。
  • aud(audience):JWT 接收方。
  • exp(expiration time):JWT 的过期时间。
  • nbf(not before time):JWT 生效时间,早于该定义的时间的 JWT 不能被接受处理。
  • jti(JWT ID):JWT 唯一标识。

示例:

1
2
3
4
5
6
7
8
{
"uid": "ff1212f5-d8d1-4496-bf41-d2dda73de19a",
"sub": "1234567890",
"name": "John Doe",
"exp": 15323232,
"iat": 1516239022,
"scope": ["admin", "user"]
}

Payload 部分默认是不加密的,一定不要将隐私信息存放在 Payload 当中!!!

JSON 形式的 Payload 被转换成 Base64 编码,成为 JWT 的第二部分。

Signature

Signature 部分是对前两部分的签名,作用是防止 JWT(主要是 payload) 被篡改。

这个签名的生成需要用到:

  • Header + Payload。
  • 存放在服务端的密钥(一定不要泄露出去)。
  • 签名算法。

签名的计算公式如下:

1
2
3
4
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用”点”(.)分隔,这个字符串就是 JWT 。

如何通过JWT进行身份验证

Untitled

两点建议:

  1. 建议将 JWT 存放在 localStorage 中,放在 Cookie 中会有 CSRF 风险。
  2. 请求服务端并携带 JWT 的常见做法是将其放在 HTTP Header 的 Authorization 字段中(Authorization: Bearer Token)。

如何防止JWT被篡改

即使JWT被截获,也很难进行篡改内容,因为服务器颁发JWT Token时会通过Header+paload+加密算法+secret对生成一个签名,当服务器接收到一个JWTtoken后会将payload与header的内容重新进行签名,然后拿新的签名和jwt中的签名做对比,如果不相同就说明JWT的内容被篡改了

所以要保管好服务器的密钥

如何加强JWT的安全性

  1. 使用安全系数高的加密算法。
  2. 使用成熟的开源库,没必要造轮子。
  3. JWT 存放在 localStorage 中而不是 Cookie 中,避免 CSRF 风险。
  4. 一定不要将隐私信息存放在 Payload 当中。
  5. 密钥一定保管好,一定不要泄露出去。JWT 安全的核心在于签名,签名安全的核心在密钥。
  6. Payload 要加入 exp (JWT 的过期时间),永久有效的 JWT 不合理。并且,JWT 的过期时间不易过长。