JWT - JSON Web Token

JWT

Json Web Token ,是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519),被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。

单点登录(Single Sign On,以下简称SSO),是指在多系统应用群中登录一个系统,便可在该应用群其他所有系统中得到授权而无需再次登录。

 

单系统登录(session)认证和单点(token)认证

 

session 认证

我们知道,http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行,因为根据http协议,我们并不知道是哪个用户发出的请求,所以为了让我们的应用能识别出是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存到Cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来是自哪个用户。

传统session的问题

开销:用户经过认证后,要在服务端做一次记录,即创建一个session对象,以便用户下次请求的鉴别,通常而言,这个session对象保存在内存中,而随着认证用户的增多,服务器的开销会明显增大。
扩展性(scaling):用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这就意味着用户下次请求时还必须请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡的能力,同时也限制了跨域认证的问题,也就意味着限制了应用的扩展能力。
CSRF:因为是基于Cookie 来进行用户识别的,所以如果Cookie 被截获,用户很容易受到跨站请求伪造的攻击。


token 的认证

基于token 认证是将验证用户全交给一个单独的认证中心,只有认证中心能接收用户的用户名和密码等安全信息,认证中心验证成功后,创建授权令牌(token),服务器通过令牌,在认证中心校验,通过后才返回给用户受保护资源。

JWT 就是类似于token 的认证的一种实现方式
JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取受保护资源,也可以增加一些额外的其他业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。


JWT的原理

用户通过服务器认证,生成一个JSON 对象,返回给用户,以后用户与服务器端通信的时候,都要携带这个JSON 对象,服务器完全靠这个对象认定用户身份,为了防止用户篡改数据,服务器端在生成这个JSON 对象的时候,会加上签名,如下图

JWT - JSON Web Token

服务器不再保存任何session 数据,也就是说,服务器变成无状态了,节省服务器开销的同时,使其变得容易实现扩展


JWT的数据构成

JWT 是由三段信息构成,这三段信息用“.”连接,整体就构成了JWT 字符串,如下图:

 

JWT - JSON Web Token

 

header

被称为JWT 的头部,主要承载两部分信息
  1.声明类型,比如JWT
  2.声明加密的算法,通常直接使用HMAC SHA256
  完整的头部就像下面的JSON对象:
  {
    “typ”:“JWT”,
    “alg”:“HS256”
  }
  上面JSON 中alg (algorithm)属性表示签名的算法,typ 属性表示这个token 的类型,JWT 类型统一为“JWT”
  然后对这个JSON对象进行Base64URL加密,就构成了JWT的第一部分,header

 

playload

被称为JWT的载荷,是用来存放实际需要传递的数据
  标准声明(官方字段,建议但不强制使用)
  iss(issuer):JWT签发者 
  sub(subject):主题
  aud(audience):接收JWT的一方
  iat(issued at):JWT的签发时间
  exp(expiration time):JWT的过期时间,这个过期时间必须要大于签发时间
  nbf(not before):定义在什么时间之前,该JWT都是不可用的
  jti(jwt id):JWT的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
  其他声明
  除了标准声明中的官方字段,还可以添加部分私有字段,一般添加用户的相关信息或其他业务需要的必要信息,但不建议添加敏感信息,因为该部分在客户端可以解密。
  比如定义一个playload
  {
    “sub”:“123456789”,
    “id”:“6”,
    “name”:“Lucy”
  }
  然后对这个JSON 对象进行Base64URL 加密,就构成了JWT 的第二部分


signature

JWT 的第三部分是一个签证信息,这个签证信息由三部分组成:
  Base64URL 加密后的header
  Base64URL 加密后的payload
  secret
  这个部分需要Base64URL 加密后的header 和Base64URL 加密后的payload 使用“.” 连接组成的字符串,然后通过header 中声明的加密方式进行,如:
  HMACSHA256(base64UrlEncode(header) + '.' + Base64UrlEncode(payload), secret)
  通过加密算出签名后,再把Header、Payload、Signature 三部分拼成通过两个“.” 拼接成一个字符串,就构成了JWT


扩展:Base64URL

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

注意:secret 是保存在服务器端的,JWT的签发生成也是在服务器端的,secret 就是用来进行JWT的签发和JWT的验证的,所以,在任何场景都不应该流露出去,一旦客户端得知这个secret,那就意味着客户端是可以自我签发JWT 了


JWT 的使用方式

客户端收到服务器返回的JWT,可以存储在Cookie 里面,也可以存储在LocalStorage 里面
此后,客户端每次与服务器通信,都要带上这个JWT。可以通过Cookie 里面自动发送,但是这样不能跨域,更好的做法是放在HTTP 请求头信息的"Authorization" 字段里面
另一个做法就是,跨域的时候,JWT 就放在POST请求的数据体里面


JWT 的特点及总结

1.不需要在服务器端保存会话信息,所以易于应用的扩展
2.JWT 的构成非常简单,字节占用很小,所以它是非常便于传输的
3因为有Payload 部分,所iJWT 可以自身存储一些其他业务逻辑所必要的非敏感信息
4.因为JSON 的通用性,所以JWT 是可以进行跨语言支持的,像Java、JavaScript、PHP、Python等很多语言都可以使用
5.JWT 不仅可以用于认证,也可以用于交换信息,有效的使用JWT,可以降低服务器查询数据库的次数
6.JWT 默认是不加密的,但是也可以加密的,生成原始Token 以后,还可以使用**再加密一次
7.JWT 不加密的情况下,不能将敏感数据写入JWT
8.JWT 最大的缺点就是,由于服务器不保存session 状态,因此无法再使用过程中废止某个token,或更改token 的权限,也就是说,一旦JWT 签发了,在到期之前就会始终有效,除非服务器额外的处理逻辑。
9.JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限,为了减少盗用,JWT 的有效期应该设置得比较短,对于一些比较重要的权限,使用时应该再次对用户进行认证。
10.为了减少盗用,JWT 不应该使用HTTP 协议明码传输,要使用HTTPS 协议传输。

 

参考:JSON Web Token 入门教程http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html