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
载荷负责存放有效信息,实际上包括标准声明和非标准声明
- 标准声明(建议并不强制使用)
- iss: jwt签发者
- sub: jwt所面向的用户
- aud: 接收jwt的一方
- exp: jwt的过期时间,这个过期时间必须要大于签发时间
- nbf: 定义在什么时间之前,该jwt都是不可用的.
- iat: jwt的签发时间
- jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
- 非标准部分可以写入用户的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
- 将加密后的第一部分和第二部分使用.连接形成字符串
$header_payload_str = $header_str . '.' . $payload_str;
- 使用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
提供两个解决方法
-
一个域名登录成功,在客户端针对其他每个站点在浏览器端设置cookie信息
-
借助单独的sso服务器,只需存储该sso服务器域名下的cookie信息,在这种模型下,针对任何站点的请求都将会先重定向到SSOsite去验证一个身份验证cookie是否存在。如果存在,则验证过的页面将会发送给浏览器。否则用户将会被重定向到登录页面。
5. 关于token泄露
token如果泄露,别人就可以通过token来进行非法操作,因为服务端只判定token是否合法,并不会验证使用者。
泄露一般存在两个地方
1.传输层
token在传输层被拦截,传统的cookie存储的sessionId也有一样的问题,所以建议使用https协议,安全性更有保证。
2.客户端
如果cookie可以被窃取,token也没办法解决这个问题。拦截到cookie后,把cookie放在另一台电脑上也是可以使用的,这个跟token是一样的。
6. 总结
- jwt是一个标准,可以跨语言使用。
- jwt构成简单,这用字节小,便于传输,不会因为每次请求头都带着token过于影响性能。
- 不需要在服务端保存会话信息,减轻服务器压力同时易于扩展。
- payload部分是对称解密,不应该存储敏感信息。
- 保护好私钥,否则别人可以伪造token