前言
JWT全称JSON Web Token
,它本质上就是一个Token,比如下面的例子:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJuYW1lIjoiSmFsb24iLCJwaG9uZSI6MTg2NTkyNTI2Mzd9
.d2VSEkIMK_PWJeXv22mIy7VL6FEAlt8CuCNgWv7QVMM
可以看到token中有两个
.
符号,这里为了可视化效果,将其分行显示,实际使用时是单行显示;
目录
- JWT的内部结构
- JWT如何使用
- JWT的优缺点
正文
1. JWT的内部结构
JWT内部由三部分组成:Header(头部)、Payload(负载)、Signature(签名)
前言中我们看到的由.
拼接成的字符串其实是编码后的JWT:(具体的编码过程下面会介绍)
那么编码前的JWT是什么样子呢?
下面我们就来介绍下编码前的JWT的组成部分;
header 头部:
头部信息,内部是一个Json对象
,主要由 token的类型typ
和 签名算法alg
这两部分组成;
比如下面的Header例子:
{
"alg": "HS256",
"typ": "JWT"
}
其中
-
HS256
是最常用的签名算法,其他的还有RS256
、ES256
等等; -
JWT
是默认的Token类型;
Payload 负载:
负载,内部也是一个Json对象,主要包含了用户自定义的相关字段 和 官方预定义的相关字段;
- 用户自定义的字段:比如用户名等信息
- 官方预定义的字段:比如签名时间、过期时间等信息
比如下面的Payload例子:
{
"iat": 1516239022,
"name": "Jalon"
}
其中:
- iat: 就是官方预定义的字段,用来表示签发token的时间
- name: 就是用户自定义的字段,用来表示当前的用户名
有个细节:
JWT中所有官方定义的字段都是三位数,比如Header中的
alg
,typ
,Payload中的iat
;目的是为了保证JWT的紧凑性,以节省传输带宽
Siganature 签名:
签名,就是把上面的Header和Payload,通过指定的算法进行签名加密处理;
其中还会用到密钥Secret
,该密钥由后端保管,前端无感;
假设签名算法为上面提到的HS256
,那么签名的代码如下所示:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
可以看到,这里签名之前,会先把Header和Payload内容进行base64UrlEncode编码处理;
base64Url编码:类似base64编码,只是在base64的基础上,将=
转换成空字符, +
转化成-
,/
转化成_
;
转化过程如下:
function b64(str) {
return new Buffer(str).toString('base64')
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
}
之所以还要进行url转化,是因为这些特殊字符在url中有特殊的含义;比如=
用来表示参数的键值对;
而Token有时候会直接用在url中(虽然不建议这样做,因为不安全)
2. JWT如何使用
- 首先用户请求登录时,后台会根据用户的信息,生成一个Token并返回给用户,Token格式如下;
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
- 然后用户后续的请求都会带上这个Token,后台再根据校验规则进行比对,如果符合条件就放行;
下面是一个校验token的例子:用的是js中的jsonwebtoken
库;
const payload = jwt.verify(token, secret, {
algorithms: ['HS256']});
- 通常用户携带Token,都是存在Http请求的Header头中的Authorization字段中,如下所示:
header:{
Authorization: Bearer token
}
通过JWT的这种方式来进行授权访问,不用担心CSRF攻击(Cross Site Request Forgery 跨站伪造请求),因为我们没有将Token存在Cookie中;
关于通过cookie进行CSRF攻击的信息,可以参考:前端安全系列(二):如何防止CSRF攻击? - 美团技术团队 (meituan.com)
3. JWT的优缺点
优点:
- 不用担心CSRF攻击:因为token一般都是通过Header进行传递,而当我们点击不明链接(CSRF攻击的常用手段)时,只会将Cookie信息附带过去,不会附带Header中的信息,此时后台校验Token不通过,请求会失败;
- 后端不需要保存Token:
- 因为后端生成Token并返回到前端后,前端会把Token保存在某个地方(比如LocalStorage),此时后端不再需要保存这个Token,等到用户携带Token进行请求时,后端只需要用秘钥进行校验即可;
- 这样一来,就可以减轻服务端的压力,比如当存在多个服务节点时,不用考虑Token的同步问题;
- 如果是Session,那么后端还需要维护Session信息,并考虑如何同步Session信息;比如用户登录时,Session信息存储到A节点,结果下次访问服务又跑到B节点,就会提示还要登录;
- 适合移动端:因为Token可以直接存储在移动端;如果是Session,那么前端需要将后端返回的SessionId存储在Cookie中(移动端不支持Cookie);
- 适合单点登录:因为Token保存在前端,后端没有存储Token,所以当用户登录了一个系统A,那么Token就会存储在前端(比如LocalStorage中);此时用户去访问关联的系统B,也会携带刚才获取的Token,这时后端会直接校验通过,也就实现了SSO单点登录;
缺点:
- 退出登录时,token依然有效:
- 因为Token存储在前端,所以用户退出登录时(或者删除账户、修改密码等其他操作),后端对之前的Token还是会验证通过(因为在有效期),后端只知道啥时候过期;
- 这时我们可以将Token存到Redis等内存数据库中,但是这样一来就跟Session很像了
- 也可以前端退出登录时删除对应的Token信息;
- Token的续签问题:
- Token过期后如何续签,比如过期时间为30分钟,用户在界面操作了30分钟后,结果提示用户需重新登录(因为后端没有动态刷新Token),这显然是不友好的;
- 可以让后端生成两个token,一个accessToken,一个refreshToken,前者负责授权访问,过期时间短一点,比如30分钟;后者负责刷新token,过期时间长一点,比如12小时;当后端提示accessToken过期时,前端再传refreshToken,如果refreshToken没过期,后端就生成新的accessToken返回给前端,并通过验证;
总结
本篇介绍了JWT的基本信息,包括结构组成、使用方法以及优缺点;
-
JWT是由Header、Payload、Signature三部分组成;
-
通过将Token设置在Header中的Authorization字段进行使用:
-
优缺点主要是基于JWT的无状态进行分析,即后端不用保存Token;
参考
- JWT官网体验,可在线体验加密解密
- JWT官网介绍
- JWT优缺点分析
- 如何防止CSRF攻击
评论区