搞懂 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 非空判断,是拦截器开发的三个关键点。
相关推荐
kyriewen6 小时前
我花3天抓了一个幽灵bug,凶手藏在第4层
前端·javascript·程序员
mONESY7 小时前
LangGraph.js 从零上手:把 Agent 工作流从一条线变成一张网
javascript
默_笙7 小时前
⚓ AI 的"官方答题卡":withStructuredOutput 与结构化输出的终局之战
前端·javascript
我家猫叫佩奇9 小时前
🦭 厌倦了千篇一律的线性图标?Naive Icons 正式开源
前端·javascript·css
梦诺11 小时前
vue3 keepAlive+记录滚动条
前端·javascript·vue.js
天若有情67312 小时前
开源轻量双语工具|一键批量查询 NPM 包历史下载量,支持按作者批量统计
javascript·npm·github pages·开源工具·netlify·前端开源·npm克隆量
福兮说14 小时前
纯前端把图片压缩到指定体积:canvas.toBlob 配合二分查找
前端·javascript·canvas·图片处理
志尊宝14 小时前
Vue3 零基础每日笔记(018):按键与鼠标修饰符——回车搜索、Ctrl+S 保存这样写
javascript·vue.js·笔记
可乐鸡翅yeah_15 小时前
HLS流媒体首屏起播慢深度优化,从分片、索引、播放器全链路调优
开发语言·javascript·ecmascript·m3u8·m3u8在线
开开心心就好15 小时前
低年级识字练笔顺工具,电脑和安卓端都能用
前端·javascript·网络·安全·scala·erlang·语音识别