一、定义
JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。
优点:
1.payload 可存储业务逻辑信息(非敏感性)
2.JWT可以使用一种加密算法比如HMAC 算法,也可以使用公钥/私钥的非对称算法
3.因为JWT签名后的信息够短,可以放在url里、request body里、http header里,传输够快。
4.不需要存储在服务端,易于扩展
二、使用场景
下列场景中使用JSON Web Token是很有用的:
- Authorization (授权) : 这是使用JWT的最常见场景。一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用。
- Information Exchange (信息交换) : 对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。因为JWTs可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。
三、JWT流程
- 用户使用用户密码来请求服务器
- 服务器进行验证用户信息
- 服务器通过验证发送用户一个token
- 客户端存储token,并在每次请求时在HTTP头附送上这个token值
- 服务端验证token值,从token解析到用户信息,处理后并返回数据
四、JWT结构
JSON Web Token由三部分组成,它们之间用圆点(.)连接。这三部分分别是:
- Header
- Payload
- Signature
因此,一个典型的JWT看起来是这个样子的:
xxxxx.yyyyy.zzzzz
接下来,具体看一下每一部分:
1.Header
JWT 的 header 中承载了两部分信息
{
"alg": "RS256",
"typ": "JWT"
}
- alg: 声明加密的算法
- typ: 声明类型
对这个头部信息进行 base64,即可得到 header 部分
const headerBuff = Buffer.from(
JSON.stringify({
alg: "RS256",
typ: "JWT"
})
);
const header = headerBuff.toString("base64");
console.log(header);
// eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9
2.Payload
payload 是主体部分,意为载体,承载着有效的 JWT 数据包,它包含三个部分
- 标准声明
- 公共声明
- 私有声明
标准声明的字段
interface Stantar {
iss?: string; // JWT的签发者
sub?: string; // JWT所面向的用户
aud?: string; // 接收JWT的一方
exp?: number; // JWT的过期时间
nbf?: number; // 在xxx日期之间,该JWT都是可用的
iat?: number; // 该JWT签发的时间
jti?: number; //JWT的唯一身份标识
}
标准中建议使用这些字段,但不强制。
公共声明的字段
interface Public {
[key: string]: any;
}
公共声明字段可以添加任意信息,但是因为可以被解密出来,所以不要存放敏感信息。
私有声明的字段
interface Private {
[key: string]: any;
}
私有声明是 JWT 提供者添加的字段,一样可以被解密,所以也不能存放敏感信息。
上面的 JWT 的 payload 结构是这样的
{
"ip": "127.0.0.1",
"uuid": "ff1212f5-d8d1-4496-bf41-d2dda73de19a",
"iat": 1527523017
}
同样是通过 base64 加密生成第二部分的 payload
const payloadBuffer = Buffer.from(
JSON.stringify({
ip: "127.0.0.1",
uuid: "ff1212f5-d8d1-4496-bf41-d2dda73de19a",
iat: 1527523017
})
);
const payload = payloadBuffer.toString("base64");
console.log(payload);
// eyJpcCI6IjEyNy4wLjAuMSIsInV1aWQiOiJmZjEyMTJmNS1kOGQxLTQ0OTYtYmY0MS1kMmRkYTczZGUxOWEiLCJpYXQiOjE1Mjc1MjMwMTd9
3.Signature
signature 是签证信息,该签证信息是通过header
和payload
,加上secret
,通过算法加密生成。
公式 signature = 加密算法(header + "." + payload, 密钥);
上面的 header 中,我们已经定义了加密算法使用 RS256
,也已经实现了生成header
和payload
,下面我们来生成 signature
const crypto = require("crypto");
const sign = crypto.createSign("SHA256");
const secret = `私钥,太长我就不贴出来了`;
sign.write(header + "." + payload);
sign.end();
const signature = sign
.sign(secret, "base64")
// 在JWT库中,已经把这些字符过滤掉了
.replace(/=/g, "")
.replace(/\+/g, "-")
.replace(/\//g, "_");
console.log(signature);
到此,已经实现了如何生成一个 JWT 的 token.
五、JWT与Session的区别
- Session 通过 cookie 传输,存储在服务器,服务器通过 cookie 中的 sessionID 获取当前会话用户,对于单台服务器没问题,但多服务器就涉及到共享 Session ,而且认证用户多时 Session 会占用大量服务器内存
- JWT 存储在客户端,服务器不需要存储 JWT ,JWT 里有用户 ID,服务器拿到 JWT 后验证可以获得用户信息,也就实现了 Session 功能,但是是无状态的,只要签名秘钥足够安全就能保证 JWT 可靠性
六、JWT与OAuth的区别
- OAuth2是一种授权框架 ,JWT是一种认证协议
- 无论使用哪种方式切记用HTTPS来保证数据的安全性
- OAuth2用在使用第三方账号登录的情况(比如使用weibo, qq, github登录某个app),而JWT是用在前后端分离, 需要简单的对后台API进行保护时使用。