Skip to main content

JWT

JWT 定义

JWT(JSON Web Token) 是一个基于 JSON 的开放标准(RFC 7519),用于创建access token,是目前最流行的跨域认证解决方案。简单来说,一个 JWT 就是一个字符串,形式如下:

header.payload.signature;

session 认证的问题

如上图,传统使用session进行身份认证的流程如下:

  1. 客户端向服务器发送用户名和密码。
  2. 服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户名、过期时间等。
  3. 服务器通过传送Set-Cookiesession_id写入用户的 Cookie
  4. 用户随后的每一次请求,都会通过 Cookiesession_id传回服务器。
  5. 服务器收到 session_id,通过与数据库保存的数据进行匹配,由此认证用户的身份。

通过上述流程可以看出,服务器需要对用户的session信息进行存储,并通过匹配session_id来辨别用户身份,因此,服务器需要维护大量的session信息。而对于服务器集群来说,由于不同的系统之间session是不共享的,因此需要将所有系统的session做抽象处理,大大的加大了开发难度。

除此之外,我们知道cookie是不能够跨域的,也就是上面的模型对于不同域名的应用并不适用。

为什么使用 JWT

JWT-Token认证流程

如图所示,存在 3 个角色:

  • Authentication server (认证服务器)
  • User(用户)
  • Application server (应用服务器)

步骤:

  1. 用户使用账号密码在登录页登录,并将账号和密码传给认证服务器。
  2. 认证服务器生成Token并将其传给用户。
  3. 用户通过携带TokenAPI请求访问应用服务器。
  4. 应用服务器通过 JWT 来判断用户身份,通过验证后将数据返回给用户。

JWT有如下几个优点:

  • JWT一套无状态的验证机制,用户访问时通过Token进行身份验证,服务器不用为了存储用户状态来维护session,服务器可以做到更多的解耦和扩展
  • JWT中的Token可以通过URL和请求头字段Authorization进行传送,可以避免跨域问题。
  • JWT 可以通过payload保存用户的数据,减少数据库访问。

JWT 创建 Token

创建 header

header 部分是一个 JSON 对象,用来描述 JWT 的元数据,如下所示:

{
"typ": "JWT", // 表明是 JWT
"alg": "HS256" // 代表生成 signature 所用的哈希算法,这里是 HMAC-SHA256
}

生成Token时需要,将上面的 JSON 对象使用 Base64URL 算法转成字符串。

创建 payload

JWTpayload 部分用来存放实际需要传递的数据。

比如我们这里存储了用户 ID:

{
"userId": "b08f86af-35da-48f2-8fab-cef3904660bd"
}

你可以在 payload 里存储大量信息,但大量信息会降低性能,增加延迟。

header相同,payload也需要使用Base64URL 算法转成字符串。

计算生成 signature

headerpayload 使用的是 Base64URL 编码,所以将两者分别传入base64UrlEncode中,并结合服务器生成的secret使用下面的公式计算出signature。其中HMAC SHA256Header 里面默认指定的签名算法。

// signature algorithm
HMACSHA256(base64UrlEncode(header) + '.' + base64UrlEncode(payload), secret);

组装 headerpayloadsignature。把 headerpayloadsignature. 相连即最终的 JWT token

例如:当header的值为"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"payload的值为"eyJ1c2VySWQiOiJiMDhmODZhZi0zNWRhLTQ4ZjItOGZhYi1jZWYzOTA0NjYwYmQifQ"signature的值为"-xN_h82PHVTCMA9vdoHrcZxH-x5mb11y1537t3rGzcM"时,生成的Token如下所示:

// header.payload.signature;
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJiMDhmODZhZi0zNWRhLTQ4ZjItOGZhYi1jZWYzOTA0NjYwYmQifQ.-xN_h82PHVTCMA9vdoHrcZxH-x5mb11y1537t3rGzcM

JWT 使用方式

客户端收到认证服务器返回的 Token,可以将其存在 CookielocalStorage/sessionStorage中。

当客户端访问应用服务器时,每次请求都需要带上Token。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求头的Authorization字段里面,如下所示:

Authorization: Bearer <token>

在跨域时,除了上述用法外,还可以把Token就放在 POST 请求的数据体里。

验证 JWT token

因为应用服务器知道验证服务器哈希计算 signaturesecret,所以应用服务器可以用 secret 去重新计算 signature (用户发送过来的 token 里有 headerpayload),并与用户发送过来的 tokensignature 比较,最终验证是否合法。

安全性

我们在前面提到的headerpayload并没有进行加密,只是经过了base64URL的编码,所以“黑客”不需要secret就能获取到headerpayload中的敏感信息。此外,“黑客”还可以通过修改headeralg的参数值为none,对于那些支持alg字段为none的服务器,后端将不会进行签名验证。

除了上述两种案例外,还有很多其他的风险案例,如果感兴趣可以参考这篇文章:JWT 介绍及其安全性分析。因此,为了保证数据的安全性,JWT一般需要结合https一起使用。

参考资料

  1. JSON Web Token 入门教程,by 阮一峰
  2. Token Authentication: The Secret to Scalable User Management,by Lindsay Brunner
  3. JWT HANDBOOK, by Sebastián E. Peyrott
  4. 使用 jwt 完成 sso 单点登录,by 秦梁的小站
  5. JWT 介绍及其安全性分析,by 不瘦二十斤不改名