登录方案: JWT

介绍

JSON Web Token (JWT) 是一种开放标准,它定义了一种紧凑且独立的方式,用于在各方之间以 JSON 对象的形式安全地传输信息。该信息可以被验证和信任,因为它是经过数字签名的。官网Jwt.io

它是一个很长的字符串,中间用点(.)分隔成三个部分。注意,JWT 内部是没有换行的,这里只是为了方便展示,而将它写成了几行。

JWT 的三个部分依次如下。

  • Header(头部)
  • Payload(负载)
  • Signature(签名)

写成一行,就是下面的样子。

java 复制代码
Header.Payload.Signature

Header 部分是一个 JSON 对象,描述 JWT 的元数据,通常是下面的样子。

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

上面代码中,alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT。

最后,将上面的 JSON 对象使用 Base64URL 算法(详见后文)转成字符串。

Payload

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。

  • iss (issuer):签发人
  • exp (expiration time):过期时间
  • sub (subject):主题
  • aud (audience):受众
  • nbf (Not Before):生效时间
  • iat (Issued At):签发时间
  • jti (JWT ID):编号

除了官方字段,你还可以在这个部分定义私有字段,下面就是一个例子。

json 复制代码
{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。

这个 JSON 对象也要使用 Base64URL 算法转成字符串。

Signature

Signature 部分是对前两部分的签名,防止数据篡改。

首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

scss 复制代码
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。

Base64URL

前面提到,Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。

JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+、/和=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成_ 。这就是 Base64URL 算法。

JWT 的使用方式

客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。

此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。

js 复制代码
const axios = require("axios");
axios
  .get({
    url: "https://yourdomain.com/api/v1/your/resources",
    headers: {
      Authorization: "Bearer ID_TOKEN",
    },
  })
  .then((res) => {
    // custom codes
  });

另一种做法是,跨域的时候,JWT 就放在 POST 请求的数据体里面。

下面是使用JWT登录流程图

问题

JWT优点在于无状态,服务端不需要存储相关的状态,只需要服务端采用了自己指定的私钥便可以进行认证。同样的,JWT的缺点也在于无状态。Token一旦被颁发,就无法修改其过期时间。如果需要刷新Token,则必须生成一个新的Token,不过之前丢弃的Token也依旧有效。也就是说,用户登录后的不久,浏览了许久的商品点击购买,突然发现自己登录失效,这对用户体验很不好。

我们可以引入 refreshToken 的概念,用来做刷新 token 的凭证,当 token 过期时,我们就用 refreshToken 来获取一个新的 Token。

refresh token

用户登录的时候,服务端返回两个 token

  • 一个是 accessToken,用来做用户的鉴权,过期时间比较短,比如 10分钟过期
  • 一个是 refreshToken,用来刷新 accessToken,过期时间比较长,比如 30 天过期

客户端把这两个 token 都存在本地,每次访问都将 accessToken 传给服务端,服务端校验 accessToken 有效后,响应。

如果服务端校验accessToken过期后,会要求客户端带上refreshToken传递给服务端,如果refreshToken 是有效的,那么服务端就会返回一个新的 accessToken,客户端就使用新的 accessToken 继续访问服务。

如此以来,就实现了浏览器无感刷新。

这里如果过期一共会发送 2 次 HTTP 请求,第一次过期被拒绝要求客户端返回新Token,第二次刷新 Token。

需要注意的是,为了保证安全性,刷新令牌通常会更长,并且只能用来获取新的访问令牌。而且,在设计刷新令牌时要考虑到其失效时间、有效范围等方面的控制,以减少安全风险。

这里可能会有疑惑,两个 Token 都需要存在客户端,这样岂不是很不安全吗?

确实如此,但是由于 JWT 的出现就是为了解决服务端存储 session 的问题,所以这个问题没有很好的解决方案,我们只能让 refreshToken 更加的安全一些,也尽量少传输。

例如:可以通过绑定客户端的 client_id 和 client_secret 来保证 refreshToken 的安全性,这样就可以保证 refreshToken 只能在特定的客户端使用。

总结

这部分我们介绍了 JWT 的验证流程,JWT Token 的生成规则,退出登录的实现,以及无感刷新的实现。

我们需要明确一点就是,JWT 是无状态的,服务端不需要存储任何信息,所以 JWT 的安全性取决于 Token 的安全性。

相关推荐
zhanshuo6 分钟前
不依赖框架,如何用 JS 实现一个完整的前端路由系统
前端·javascript·html
火柴盒zhang7 分钟前
websheet在线电子表格(spreadsheet)在集团型企业财务报表中的应用
前端·html·报表·合并·spreadsheet·websheet·集团财务
khalil9 分钟前
基于 Vue3实现一款简历生成工具
前端·vue.js
拾光拾趣录15 分钟前
浏览器对队头阻塞问题的深度优化策略
前端·浏览器
用户81221993672216 分钟前
[已完结]后端开发必备高阶技能--自研企业级网关组件(Netty+Nacos+Disruptor)
前端
万少20 分钟前
2025中了 聊一聊程序员为什么都要做自己的产品
前端·harmonyos
abigale032 小时前
webpack+vite前端构建工具 -11实战中的配置技巧
前端·webpack·node.js
专注API从业者3 小时前
构建淘宝评论监控系统:API 接口开发与实时数据采集教程
大数据·前端·数据库·oracle
Joker`s smile3 小时前
Chrome安装老版本、不同版本,自制便携版本用于前端调试
前端·chrome
weixin_416639973 小时前
爬虫工程师Chrome开发者工具简单介绍
前端·chrome·爬虫