likes
comments
collection
share

API身份认证还得看我JWT

作者站长头像
站长
· 阅读数 12

概述

JSON Web Token (简称JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

JWT主要适用场景包括:

  • 身份认证:适用较广的是在SSO场景,在客户端认证成功后,服务端签发jwt给客户端,后续客户端凭借jwt来代表自己的身份,那客户端就可以访问所有接受这套jwt签发系统的服务端(可以跨域);当前,如果jwt被泄漏,在有效期内,任何一个获得jwt的人都可以以客户端的身份和服务端合法通信
  • 交换信息:在jwt payload中携带信息从而进行信息交换。因为jwt是有数字签名的,不可被篡改;因为payload是明文,一般在payload中不要放置敏感信息

结构

首先JWT大概长这个样子:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

结构上长这样xxxxx.yyyyy.zzzzz,用两个.划分为三个部分:分别是Header、Payload以及Signature。可以通过JWT官方提供的在线工具jwt.io/#debugger-i…进行在线解密插件(这也说明了jwt的信息都是明文的 建议不要放敏感信息)

API身份认证还得看我JWT

Header

头部信息。主要包含了JWT类型以及签名算法,签名算法有HMAC SHA256、RSA、ES等系列,推荐使用ES512

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

然后再使用Base64Url编码得到第一节信息:

$ echo -n '{"alg":"HS256","typ":"JWT"}' | base64 | tr '/+' '_-' | tr -d '='
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Payload:

声明式信息,一般叫做claims,主要由两部分构成:

  • Registered claims:官方预定义的一些字段,如iss(issuer签发者)、exp(expiration time本JWT过期时间)、sub(subject主体信息)等
  • Private claims:应用自定义信息,在典型的SSO登录场景中,例如username、userid等信息。Payload信息是明文的,不建议放一些secret等敏感信息

上述示例中的Payload有

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

然后再使用Base64Url编码得到第二节信息:

$ echo -n '{"sub":"1234567890","name":"John Doe","iat":1516239022}' | base64 | tr '/+' '_-' | tr -d '='
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ

Signature

签名是为了保证JWT是不可被篡改的,JWT一般由服务端的认证系统通过私钥统一签发,其他使用到JWT的系统可以通过对应的公钥进行验证,这背后的原理是基于非对称加密的签名算法,感兴趣的同学可以阅读下之前的文章juejin.cn/post/721633…

以HS256(HMAC+SHA256算法组合)签名算法为例,其计算逻辑如下:

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

剖析

JWT流行的原因在于其无状态,具备去中心化的认证优势。

回想下传统的基于session+cookie的认知方法,一是有服务端横向扩展限制,一般需要引入redis来存储这些session信息;二是在涉及跨域场景中会有问题,因为cookie的跨域限制。

JWT优势在于通过统一的认证中心签发JWT后,当用户携带这些JWT访问子资源或者其他SSO内站点时,这些服务端系统可以通过JWT签名验证客户端身份,这避免了中心化的认知流程,降低了系统间的耦合度。

但是JWT也不是非常的完美,一是一旦JWT被泄漏,拥有JWT方就能掌握你的全部认证信息,进行敏感资源操作,所以一般JWT的过期时间不宜太长;二是JWT的撤销机制比较差,一旦JWT被签发出来,由于都是基于公钥的验证机制,此时如果管理员由于泄漏风险需要撤销此JWT,单纯依赖JWT的机制是不能实现的,只能引入第三方的消息通知机制一一通知到下游系统撤销该JWT,整个过程会比较繁琐。

所以建议在极端敏感的场景下,推荐基于共享session方案实现跨域认证。实际大部分业务中,通过签发短实效性的JWT是完成能够满足安全性要求的。

应用

如果在不同后端系统间调用时需要使用JWT,其实现方案一般是每个系统维护自己的一套公私钥,并提供获取公钥方法(可以提前将公钥注册到其他系统,或者提供API以获取公钥)。

假设有A、B两系统,A需要访问B的API,则A系统提前在B系统上注册好自己的身份,并注册公钥绑定到其身份上。请求前,A利用私钥签发JWT,并包含身份信息,将JWT放置在header中和请求一起发送给B。B收到后,先从JWT中获取身份信息,并利用其身份对应的公钥验证JWT,验证通过即完成了对请求方A身份的验证

拓展

除了JWT外,常常还能看到这些名词JWS JWE,其含义如下:

  • JWT是一种标准,其中header中制定了签名算法,当签名算法为none时,此时的JWT也被称为非安全的JWT;反之有数据签名也被称为JWS(JWT Signature)
  • JWE(JSON Web Encryption)是一种对payload进行加密的方式(默认的JWT的payload是明文的),此时的JWT被称为JWE

关于JWT和Oauth2的关系:

  • 首先JWT是一种token格式,Oauth2则是一种认证协议
  • JWT更加适用于API调用的场景,用于身份认证;Oauth2适用于在浏览器、移动端、PC端等需要多端共享资源或者认证的情况下使用,Oauth2的典型场景是第三方登录(如某博客网站可以利用微信账号进行登录)