JSON Web Token(JWT)简单介绍

2,184 阅读8分钟

新的知识仓库 前端从入门到入土 求关注、star及提建议,不定时更新~

你们都不看的总集篇: 从零开始的大前端筑基之旅(深入浅出,持续更新~)
觉得不错就顺手点个赞吧~

JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案,

该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

一、跨域认证的问题

传统的session认证

http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行。

一般请求流程是下面这样。

  1. 用户向服务器发送用户名和密码。
  2. 服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间等等。
  3. 服务器向用户返回一个 session_id,写入用户的 Cookie。
  4. 用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。
  5. 服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。

session认证所显露的问题

  • 服务端开销:每个用户经过应用认证之后,应用都要在服务端做一次记录,以方便用户下次请求的鉴别。通常session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。
  • 扩展性: 如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。
  • 另一种方案是 session 数据持久化,写入数据库或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层万一挂了,就会单点失败。
  • CSRF: 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。

基于token的鉴权机制

基于token的鉴权机制类似于http协议也是无状态的,但它不需要在服务端去保留用户的认证信息或者会话信息。

流程上是这样的:

  1. 用户使用用户名密码来请求服务器
  2. 服务器验证用户的信息
  3. 服务器通过验证,给用户返回一个token 4, 客户端存储token,并在每次请求时附送上这个token值
  4. 服务端验证token值,并返回数据

服务端要支持CORS(跨来源资源共享)策略,一般在服务端设置Access-Control-Allow-Origin: *就可以了。

JWT

服务器认证以后,生成一个 JSON 对象,返回给用户,就像下面这样。

 {
   "姓名": "张三",
   "角色": "管理员",
   "到期时间": "2018年7月1日0点0分"
 }

以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名。

JWT 的数据结构

JWT是由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

它是一个很长的字符串,中间用点(.)分隔成三个部分。注意,JWT 内部是没有换行的,这里只是为了便于展示,将它写成了几行。

JWT 的三个部分依次如下。

  • Header(头部)
  • Payload(负载)
  • Signature(签名)

Header

Header 部分是一个 JSON 对象,描述 JWT 的元数据,

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

其中,alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT。

然后将头部进行base64 URL加密(该加密是可以对称解密的),构成了第一部分。

Payload

载荷就是存放有效信息的地方。这些有效信息包含三个部分

  • 标准中注册的声明
  • 公共的声明
  • 私有的声明

标准中注册的声明 (建议但不强制使用) :

  • iss: jwt签发者
  • sub: jwt所面向的用户
  • aud: 接收jwt的一方
  • exp: jwt的过期时间,这个过期时间必须要大于签发时间
  • nbf: 定义在什么时间之前,该jwt都是不可用的.
  • iat: jwt的签发时间
  • jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

公共的声明 : 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

私有的声明 : 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。

定义一个payload:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

然后将其进行base64 URL加密,得到Jwt的第二部分。

signature

jwt的第三部分是一个签证信息,用于防止数据篡改。这个签证信息由三部分组成:

  • header (base64后的)

  • payload (base64后的)

  • secret

首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,

然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

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

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。

Base64URL

Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。

JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+/=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-/替换成_ 。这就是 Base64URL 算法。

JWT 的使用方式

客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。

客户端每次与服务器通信,都要带上这个 JWT。你可以把它

  • 放在 Cookie 里面自动发送,但是这样不能跨域,
  • 放在 HTTP 请求的头信息Authorization字段里面。Authorization: Bearer <token>
  • 跨域的时候,放在 POST 请求的数据体里面。

JWT 的几个特点

  1. JWT 默认是不加密。生成原始 Token 以后,可以用密钥再加密一次。

  2. JWT 不加密的情况下,不能将秘密数据写入 JWT。

  3. JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。

  4. 由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。

  5. JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。

  6. 为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。

如果你收获了新知识,请点个赞吧~

本文收纳于: 从零开始的大前端筑基之旅(深入浅出,持续更新~)

推荐阅读:

参考文档

  1. 什么是 JWT -- JSON WEB TOKEN
  2. JSON Web Token 入门教程