JWT究竟是什么呢?
译者按:如果你还在使用session验证用户的话,是时候了解一下JWT了!
为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,翻译仅用于学习。
JWT(JSON Web Token)究竟是什么呢?它有什么用?这篇文章将为你揭开谜底。
首先,我们不妨看一下IETF(Internet Engineering Task Force)对JWT的权威定义:
JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure, enabling the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted.
啊!什么鬼?! 水平有限,这样的定义我就不翻译了…
简单地说,JWT是一个字符串,我们在发起网络请求时,将其放在header或者url中,这样可以保证传递的数据被篡改时能被我们发现,保证安全性。
示例xxxxx.yyyyy.zzzzz即为JWT:
http://www.example.com/private/?token=xxxxx.yyyyy.zzzzz
小伙伴应该注意到了,JWT由3部分组成,使用两个点区分。如下:
header.payload.signature
那么,header, payload和signature分别指的是什么呢?
Header
header所表示的JSON对象通常由2个部分组成:token的类型,即”JWT”; token所采用的hash算法,例如HMAC SHA256或者RSA。
{
"alg": "HS256",
"typ": "JWT"
}
然后,这个JSON对象采用Base64Url编码变成字符串,作为JWT的第一部分。
var header = {
alg: 'HS256',
typ: 'JWT'
};
// 将JSON对象使用base64编码为字符串
var encoded_header = Buffer.from(JSON.stringify(header)).toString('base64');
console.log(encoded_header); // 打印: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
// 将base64编码的字符串解析为JSON对象
var decoded_header = JSON.parse(Buffer.from(encoded_header, 'base64').toString());
console.log(decoded_header); // 打印: { alg: 'HS256', typ: 'JWT' }
可知,header只是进行了编码,而非加密,其过程是可逆的。
Payload
payload为JWT的第二部分,其JSON对象包含一系列键值对(key/value)。其中,有些预定义键有特殊含义,比如iat、exp等,iat表示JWT生成的实际,而exp代表JWT过期的时间。开发者可以使用其他非预定义的键用于传输数据。
{
"exp": "2100-01-01",
"message": "Hello, Fundebug!"
}
然后,这个JSON对象采用Base64Url编码变成字符串,作为JWT的第二部分。
var payload = {
exp: '2100-01-01',
message: 'Hello, Fundebug!'
}
// 将JSON对象使用base64编码为字符串
var encoded_payload = Buffer.from(JSON.stringify(payload)).toString('base64');
console.log(encoded_payload); // 打印: eyJleHAiOiIyMTAwLTAxLTAxIiwibWVzc2FnZSI6IkhlbGxvLCBGdW5kZWJ1ZyEifQ==
// 将base64编码的字符串解析为JSON对象
var decoded_payload = JSON.parse(Buffer.from(encoded_payload, 'base64').toString());
console.log(decoded_payload); // 打印: { exp: '2100-01-01', message: 'Hello, Fundebug!' }
特别注意,payload只是进行了编码,而非加密,其过程是可逆的。因此,JWT绝不是用来进行加密通信的。
Signature
signatrue,即签名,是JWT的第三部分。它由编码的header和payload,使用用户指定的密钥secret,采用header中指定的哈希算法生成。Node.js自带的Crypto可以来演示signatrue是如何生成的:
var header = {
alg: 'HS256',
typ: 'JWT'
};
// 使用base64编码header
var encoded_header = Buffer.from(JSON.stringify(header)).toString('base64');
var payload = {
exp: '2100-01-01',
message: 'Hello, Fundebug!'
}
// 使用base64编码pyload
var encoded_payload = Buffer.from(JSON.stringify(payload)).toString('base64');
const crypto = require('crypto');
var secret = '$T697UaW6QTWsw}rrt%*P6)ia';
// 生成签名signature
var signature = crypto.createHmac('sha256', secret).update(encoded_header + '.' + encoded_payload).digest('base64');
console.log(signature); // 打印: JiOyrHVgyTTSts6mYQpChRHO7J2/hNU4dOYkgeLthKE=
signature是根据payload生成的,两者是一一对应的,这样可以保证payload的数据不被篡改,除非密钥secret泄漏。
最终生成的JWT为:
var token = `${encoded_header}.${encoded_payload}.${signature}`;
console.log(token) // 打印: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOiIyMTAwLTAxLTAxIiwibWVzc2FnZSI6IkhlbGxvLCBGdW5kZWJ1ZyEifQ==.JiOyrHVgyTTSts6mYQpChRHO7J2/hNU4dOYkgeLthKE=
当收到JWT时,可以采用同样的算法和密钥生成签名signature,如果一致,说明数据没有被篡改。
很重要一点在于,JWT是用于验证而非加密,任何人即使没有密钥secret,header与payload中的数据都是可以获取的。
使用jwt.io,可以进行JWT的编码、解码和验证:
关于Fundebug
Fundebug专注于JavaScript、微信小程序、微信小游戏,Node.js和Java实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了5亿+错误事件,得到了众多知名用户的认可。欢迎免费试用!
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 《Nacos系列》之注册中心
- 《Nacos系列》之持久化
- 垃圾收集器详解及参数配置
- 《Nacos系列》之安装与启动
- 盘点 | Python自带的那些数据集
- 使用shuttle实现bytom上跨链资产交换
- (三)Mybatis-Plus代码生成器
- 【Code】关于 GCN,我有三种写法
- MySQL允许在唯一索引字段中添加多个NULL值
- [OHIF-Viewers]医疗数字阅片-医学影像-中间插播一下-es6-使用const加箭头函数声明函数相对于function声明函数有什么好处?
- 日志切割工具-Logrotate实现nginx日志切割
- JVM元数据区
- Linux下指定pip install和make install安装路径
- 日志追踪 sentry
- PHP-FPM多方面调优策略