JWT

JWT(JSON Web Token)

解决问题


一种授权方式,服务方授权后不保存信息,交给用户保存

交互流程


1
2
3
4
User   ---   sign-in with id/pwd    -->   Authentication Server
<-- create and return JWT ---
--- call API with JWT -->
<-- verify JWT and process ---
  1. 用户使用用户名密码登陆
  2. 服务端通过认证后生成JWT返回
  3. 客户端携带JWT访问服务端资源
  4. 服务端验证JWT后响应请求

基本结构


用点来连接头,载荷和签名header.payload.signature
其中头和载荷都是Json对象,最终整体上采用base64编码

  • 头部结构
    包含JWT标识名和加密算法
1
2
3
4
{
"typ": "JWT",
"alg": "HS256"
}
  • 载荷结构
    JWT定义了一些标准字段,此外用户可以传递任何自定义字段比如userId
1
2
3
{
"userId": "xxx"
}
  • 签名
    以头部和载荷的base64编码作为数据,使用头部指定的算法与服务器的secret key一起进行hash计算出签名
1
2
data = base64urlEncode(header) + "." + base64urlEncode(payload)
signature = hash(data, secret);

验证


应用服务端知道secret key,收到JWT后根据头部的加密算法计算出签名, 比对是否和发来的签名一致,一致则验证通过

安全性


服务端持有密钥secret key,不向用户公开,JWT在签名时使用密钥
JWT对数据进行了编码和签名,但是并没有加密,base64编码可以轻松还原出明文,所以JWT不是为了隐藏和模糊数据
JWT签名的目的是证明信息发自可信的来源,证明没有篡改,所以不能用JWT传输登录密码等敏感信息

客户端存储和发送


浏览器在收到token后,有两种方式存储

  • localStorage or sessionStorage
    token直接在响应体返回
1
2
3
4
5
HTTP/1.1 200 OK

{
"access_token": "header.payload.signature"
}

用HTTP Authorization Header和Bearer scheme发出

1
2
3
4
5
HTTP/1.1

GET /res/goods
Host: example.com
Authorization: Bearer header.payload.signature

由于Web Storage可以被相同域下的JavaScript访问, 容易遭到cross-site scripting (XSS) 攻击而泄露,不建议使用

  • Cookie
    使用HttpOnly标记后不能被JavaScript访问。使用Secure标记进行Https传输。
1
2
3
HTTP/1.1 200 OK

Set-Cookie: access_token=header.payload.signature; Secure; HttpOnly;

用cookie发出

1
2
3
4
GET /res/goods
Host: example.com

Cookie: access_token=header.payload.signature;

优缺点


优点

  • 只用用户名密码验证了一次,以后验证时都传token,更安全
  • 载荷是一个Json对象,可以自行设计传输哪些字段,token可以包含额外信息如过期时间,访问范围等
  • JWT自解释,无需外部校验
  • 和Session相比,JWT无状态并且包含一切信息,服务端不需要额外存储配合,可以灵活扩展
  • Session机制严格绑定了浏览器行为,非浏览器环境下JWT更通用

缺点

  • 没加密,不能存敏感信息
  • 签发后只要没过期就是有效的,服务端不能废止或变更权限