Fork me on GitHub

JWT

1. 简介

JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。

实际上就只规范如何产生一个加密的字符串 token,它就长这个样子

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImlzcyI6Imh0dHA6Ly8xMC4zLjE5Ljg2OjkwODAvYXBpL3NpZ25pbiIsImlhdCI6MTUyMTYxODc3MSwiZXhwIjoxNTIxNjMzMTcxLCJuYmYiOjE1MjE2MTg3NzEsImp0aSI6ImhjMHNQMFdXcnVHbmJ4SFMifQ.Yc9X6q0A1QRGoEXfzS63cbeICoZPB_7vWkRqBypdiiU

2. token的组成

上面的token由.分隔成三段,第一部分称为头部(header),第二部分们称为载荷(payload),第三部分是签证(signature)

2.1 header

jwt的头部承载两部分信息:

  • 声明类型,这里是jwt
  • 声明加密的算法 通常直接使用 HMAC SHA256
{
  'typ': 'JWT',
  'alg': 'HS256'
}

将头部信息进行base64加密(可以对称解密),构成第一部分,暂且记做$header_str

2.2 payload

载荷负责存放有效信息,实际上包括标准声明和非标准声明

  1. 标准声明(建议并不强制使用)
  • iss: jwt签发者
  • sub: jwt所面向的用户
  • aud: 接收jwt的一方
  • exp: jwt的过期时间,这个过期时间必须要大于签发时间
  • nbf: 定义在什么时间之前,该jwt都是不可用的.
  • iat: jwt的签发时间
  • jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
  1. 非标准部分可以写入用户的id等需要的信息
{
    "iss": "test",
    "iat": 1441593502,
    "exp": 1441594722,
    "aud": "www.example.com",
    "user_id": 1,
}

将载荷信息进行base64机密,构成第二部分,暂且记做$payload_str

2.3 signature

jwt的第三部分是一个签证信息,这个签证信息由三部分组成:

  • header (base64后的)
  • payload (base64后的)
  • secret
  1. 将加密后的第一部分和第二部分使用.连接形成字符串
$header_payload_str = $header_str . '.' . $payload_str;
  1. 使用header中声明的加密方式加密,形成第三部分,记做$signature
$signature = HMACSHA256($header_payload_str, 'secret');

最后将上面加密后的三部分使用.连接,构成最终的token

$token = $header_str . '.' . $payload_str . '.' . $signature;

3. 使用

用户进行登录操作,如果登录信息合法,服务端生成token字符串,响应中将token信息传递给客户端,客户端并将token信息存储在客户端,以后每次请求的时候带着token信息,服务端收到每次请求后都验证token是否存在并且合法有效,来确认用户是否登录

一般是在请求头中加入,比如我在vue中axios拦截中每次请求带着token信息

axios.interceptors.request.use(
    config => {
        let token = sessionStorage.getItem('token');
        if (token) {
            config.headers['Authorization'] = 'Bearer ' + token
        }
        return config
    },
    err => {
        return Promise.reject(err);
    }
)

4. 单点登录

4.1 同一顶级域名下

举个例子

  • www.taobao.com 服务器www
  • a.tabao.com 服务器a
  • b.tabao.com 服务器b

不同的二级域名对应不同的服务器,如果使用服务器存储session的方式,就需要我们在多台服务器上同步session信息,但是使用jwt就没有这个问题,jwt_token已经记录了用户信息,并且已经存储在客户端,我们只需要将token信息存储在顶级域名下即可。

比如

Set-Cookie: jwt=lll.zzz.xxx; HttpOnly; max-age=980000; domain=.taobao.com

注意domain必须设置为一个点加顶级域名,即.taobao.com。这样,taobao.com和*.taobao.com就都可以接受到这个Cookie,并获取JWT了

4.2 跨域单点登录

举个例子

  • www.taobao.com 服务器www
  • a.com 服务器a
  • b.com 服务器b

提供两个解决方法

  1. 一个域名登录成功,在客户端针对其他每个站点在浏览器端设置cookie信息

  2. 借助单独的sso服务器,只需存储该sso服务器域名下的cookie信息,在这种模型下,针对任何站点的请求都将会先重定向到SSOsite去验证一个身份验证cookie是否存在。如果存在,则验证过的页面将会发送给浏览器。否则用户将会被重定向到登录页面。

5. 关于token泄露

token如果泄露,别人就可以通过token来进行非法操作,因为服务端只判定token是否合法,并不会验证使用者。

泄露一般存在两个地方

1.传输层

token在传输层被拦截,传统的cookie存储的sessionId也有一样的问题,所以建议使用https协议,安全性更有保证。

2.客户端

如果cookie可以被窃取,token也没办法解决这个问题。拦截到cookie后,把cookie放在另一台电脑上也是可以使用的,这个跟token是一样的。

6. 总结

  1. jwt是一个标准,可以跨语言使用。
  2. jwt构成简单,这用字节小,便于传输,不会因为每次请求头都带着token过于影响性能。
  3. 不需要在服务端保存会话信息,减轻服务器压力同时易于扩展。
  4. payload部分是对称解密,不应该存储敏感信息。
  5. 保护好私钥,否则别人可以伪造token
posted @ 2018-12-04 20:32  archer-wong  阅读(223)  评论(0编辑  收藏  举报