JWT的入门介绍

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情

文章首发于个人博客:JWT的入门介绍 (javalover.cc)

前言

JWT全称JSON Web Token,它本质上就是一个Token,比如下面的例子:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJuYW1lIjoiSmFsb24iLCJwaG9uZSI6MTg2NTkyNTI2Mzd9
.d2VSEkIMK_PWJeXv22mIy7VL6FEAlt8CuCNgWv7QVMM
复制代码

可以看到token中有两个.符号,这里为了可视化效果,将其分行显示,实际使用时是单行显示;

目录

  1. JWT的内部结构
  2. JWT如何使用
  3. JWT的优缺点

正文

1. JWT的内部结构

JWT内部由三部分组成:Header(头部)、Payload(负载)、Signature(签名)

前言中我们看到的由.拼接成的字符串其实是编码后的JWT:(具体的编码过程下面会介绍)

image-20220512162756723

那么编码前的JWT是什么样子呢?

下面我们就来介绍下编码前的JWT的组成部分;

header 头部:

头部信息,内部是一个Json对象,主要由 token的类型typ 和 签名算法alg这两部分组成;

比如下面的Header例子:

{
  "alg": "HS256",
  "typ": "JWT"
}
复制代码

其中

  • HS256是最常用的签名算法,其他的还有RS256ES256等等;
  • 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;

参考

猜你喜欢

转载自juejin.im/post/7102668398185676837
今日推荐