搞懂 Session 与 JWT 区别 + Axios 拦截器实战

摘要:大白话讲清 Session 和 JWT 分布式痛点,以及 Axios 请求拦截器如何自动携带 Token,理清 localStorage 和 Zustand 分工,整理开发高频踩坑点。

前言

做后台系统鉴权,一定会碰到两个知识点:Session‑Cookie、JWT,同时前端通过 Axios 拦截器统一处理 token 携带。很多同学搞不懂:为什么 JWT 更适合分布式?拦截器到底干了什么?localStorage 和 Zustand 存 token 有什么不一样?本文结合课堂笔记,用大白话讲透底层逻辑。

一、Session 与 JWT 的分布式差异

笔记原文:

javascript 复制代码
sessionId -> 内存中 session会话对象 不太适合分布式
jwt 没有这个问题,任何一台服务器签发的token 都可以在任何一台服务器上 解码出来,JSON 对象

1. Session‑Cookie 模式

用户登录之后:

  1. 服务器 A 在本机内存保存 session 会话对象,里面记录当前是谁登录。
  2. 返回浏览器一个sessionId,浏览器后续请求自动带上这个 id。
  3. 服务器拿到sessionId,去本机内存查找会话,识别用户身份。

分布式场景下的致命问题 现在线上部署多台服务器 A、B 做负载均衡。

  1. 登录请求落到 A 机器,session 保存在 A 的内存。
  2. 下一次请求被转发到 B 机器。
  3. B 机器内存里没有这份 session 数据,直接判定用户未登录,登录状态丢失。

所以笔记写:不太适合分布式。 如果非要用 Session 做分布式,需要把 session 迁移到 Redis 共享存储,增加额外开发工作量。

2. JWT 如何解决分布式问题

JWT不在服务器存任何会话数据,用户身份直接打包在 token 字符串内部。

前提:集群中所有服务器必须使用同一套 secret 密钥

  1. A 服务器登录签发 JWT 令牌。
  2. 用户携带 token 发起请求,流量转发到 B 服务器。
  3. B 服务器使用相同密钥直接解码 token,读出内部 JSON 用户对象。

不需要服务器之间共享会话存储,集群任意机器都可以识别凭证,天然适配分布式。

对比表格

表格

方案 登录信息存哪里 多服务器 (分布式) 问题
SessionId 服务器本机内存 A 存的数据 B 看不到,需要 Redis 共享 session
JWT token 字符串内部 所有机器共用密钥,任意机器都可以解码读取用户 JSON

⚠️重要前提 "任意服务器都可以解码" 不是无条件的。全部后端实例 secret 密钥必须完全一样。 A 密钥aaa,B 密钥bbb,B 完全解不开 A 生成的 token,签名校验直接报错。
补充:JWT 解码得到的 JSON 对象,就是 payload 载荷,存放{id:1,username:"admin"}这类用户身份信息。

一句话总结: session 会话存在单台机器内存,多服务器部署容易登录丢失;JWT 把用户 JSON 封装在 token 本身,密钥一致,集群任意服务器都能解析身份。

二、Axios 请求拦截器:自动携带 Token

什么是请求拦截器

笔记片段代码:

ini 复制代码
//拦截每个请求 request, 使用一个配置
instance.interceptors.request.use(config => {
  const token = localStorage.getItem('token');
})

instance是我们自己创建的 axios 实例。

instance.interceptors.request.use()就是请求拦截器

每一次发送网络请求,请求还没有发给后端,先进入这个拦截器函数 ,我们在这里修改请求配置config,处理完成之后才真正发送 http 请求。

好处:不用每一个接口都手动写带上 token 的逻辑,所有请求统一处理,消除重复代码

config:请求配置对象,包含 url、headers 请求头、请求参数 method 等,我们主要修改config.headers往里面塞入 token 凭证。

localStorage.getItem('token')

ini 复制代码
const token = localStorage.getItem('token');
  • localStorage属于浏览器本地持久存储。登录成功后后端返回的 JWT 字符串就存在这里。
  • .getItem('token'):读取 key 为token的存储值,拿到 JWT 字符串。
  • 如果本地没有存储 token,拿到的值是null

⚠️上面只是笔记片段,缺少赋值 token、return config,无法直接运行。

完整可运行代码

javascript 复制代码
import axios from "axios";

// 创建axios实例
const instance = axios.create({
  baseURL: "/api",
});

// 请求拦截器
instance.interceptors.request.use((config) => {
  // 1.从浏览器本地取出token
  const token = localStorage.getItem("token");
  // 2.token存在,挂载到请求头Authorization
  if (token) {
    // Bearer后面必须保留空格
    config.headers.Authorization = `Bearer ${token}`;
  }
  // 3.必须return config,返回修改后的配置,请求才会真正发出
  return config;
});

export default instance;

完整业务流程串联

  1. 登录接口调用成功,后端返回 JWT token。前端执行 localStorage.setItem('token', res.data.token),把 token 存入浏览器本地。
  2. 页面发起任意接口请求,先走请求拦截器
  3. 拦截器内部读取 localStorage 拿到 token,添加到请求头 Authorization: Bearer eyJxxxx...
  4. return config,发送 http 请求到后端。
  5. 后端读取请求头,调用jwt.verify()校验签名、过期时间,解码拿到用户身份。

localStorage 和 Zustand 的区别

很多新手会混淆两者作用:

  • localStorage:浏览器持久存储,刷新页面数据不会丢失,用来存 token 字符串。
  • zustand:React 内存状态仓库,页面刷新直接清空,用来全局共享用户信息、登录状态。

标准业务流程:

登录成功 → localStorage 存 token,Zustand 存用户信息; 页面初始化的时候读取 localStorage 里面的 token,回填到 Zustand 全局状态,恢复登录态。

请求拦截器高频踩坑

  1. ❌忘记写return config,请求直接卡住,不会发送出去。
  2. ❌不做 token 存在判断,token 为 null,请求头变成Bearer null,后端鉴权报错。
  3. Bearer后面漏掉空格,请求头格式错误,JWT 校验失败。

✅一句话总结请求拦截器:统一拦截所有请求,从浏览器本地取出 token,自动挂载请求头,免去每个接口重复写 token 逻辑。

拓展:配套响应拦截器(处理 401 token 过期)

光有请求拦截不够,token 过期后端返回 401 未登录,需要响应拦截器统一捕获,清除凭证跳转登录页。

javascript 复制代码
instance.interceptors.response.use(
  (response) => {
    // 简化返回,业务层不需要每次写 .data
    return response.data;
  },
  (error) => {
    // token过期或者未登录状态码401
    if (error.response?.status === 401) {
      localStorage.removeItem("token");
      // 这里写路由跳转到登录页逻辑
    }
    return Promise.reject(error);
  }
);

Axios 完整生命周期

arduino 复制代码
页面调用axios发起请求
↓
【request请求拦截器】读取localStorage,挂载Authorization请求头 return config
↓
http请求发送到后端
↓
后端jwt.verify校验token,执行业务逻辑返回响应
↓
【response响应拦截器】处理返回数据,捕获401过期错误
↓
数据交给页面业务代码

总结

  1. Session 会话保存在服务器内存,多服务器分布式部署会登录失效,需要 Redis 解决;JWT 凭证放在 token 字符串,密钥一致集群任意机器都可以解析身份。
  2. Axios 请求拦截器,在请求发出之前统一修改请求头,自动带上 token,消除重复代码。
  3. localStorage 负责持久保存 token,Zustand 负责 React 组件全局状态共享,两者分工不同。
  4. 不要忘记 return config,Bearer 后面空格,做好 token 非空判断,是拦截器开发的三个关键点。
相关推荐
xiaominlaopodaren1 小时前
three.js最小地图运行时(七): MapRuntime 与地图交互
javascript·gis·three.js
sibylyue2 小时前
# Web端流媒体JS播放器开源库
前端·javascript·开源
铁皮饭盒2 小时前
网页端, 5.5mb谷歌模型, 识别躯干, 视频都不卡
前端·javascript·后端
码哥DFS3 小时前
LRU缓存
前端·javascript·缓存
zzzzzz3104 小时前
别急着把页面做成舞台:从 react-bits 看动画组件该怎么选
javascript·react.js·开源
冬夜戏雪15 小时前
agent的trace 和eval
开发语言·前端·javascript
感谢一路走过的人17 小时前
AGGrid刷新数据后执行筛选
javascript·ag-grid
水獭比特17 小时前
工具都批准了,为什么还不能执行?给 Agent 补上第二道校验
javascript·人工智能·node.js