前端登录必懂:JWT vs Session 全解析(含实战代码+避坑指南)

0 阅读12分钟

前端登录必懂:JWT vs Session 全解析(含实战代码+避坑指南)

做前端开发,登录功能是绕不开的核心模块,而 JWT 和 Session 作为两种最主流的登录认证机制,几乎是前端面试和项目开发的“必考题”。

很多新手会困惑:两者到底有什么区别?项目里该怎么选?为什么现在大部分前后端分离项目都用 JWT?

今天就从「定义→原理→实战→优缺点→避坑」,一步步把这两种机制讲透,既有理论又有可直接复用的代码,新手也能轻松看懂、快速上手。

一、先明确核心:两种机制的本质区别

不管是 JWT 还是 Session,核心目的都是「确认用户身份,维持登录状态」,但两者的实现思路完全相反:

  • Session:服务器存状态,客户端只存一个“身份编号”(SessionId)
  • JWT:客户端存状态,服务器不存任何用户信息,只负责验证

一句话总结:Session 是“服务器记账”,JWT 是“客户端带身份证”。下面我们逐个拆解细节。

二、JWT 详解:前端主流的无状态认证方案

2.1 什么是 JWT?

JWT(JSON Web Token),是一种轻量级、自包含、跨语言的令牌(Token),用于在网络应用中安全传递身份信息,核心作用是「用户认证」和「信息交换」。

简单理解:用户登录成功后,后端会给前端发一张“加密身份证”(JWT),前端每次请求接口时都带着这张“身份证”,后端通过验证“身份证”的合法性,就能认出用户,无需反复查询数据库。

2.2 JWT 核心登录流程(必记)

这是面试高频考点,也是项目实战的核心逻辑,用流程图+文字拆解,一眼看懂:

image.png

拆解成步骤,更清晰:

  1. 用户在前端输入登录信息(如用户名+密码),发起登录请求;
  2. 后端接收请求,验证用户名密码的合法性;
  3. 验证通过后,后端生成 JWT 令牌,返回给前端;
  4. 前端将 JWT 存储在 localStorage/sessionStorage/Cookie 中;
  5. 后续前端发起任何需要登录权限的请求时,都在请求头中携带 JWT;
  6. 后端接收请求后,验证 JWT 的签名和过期时间,合法则放行并返回数据,不合法则拒绝(如返回 401 未授权)。

核心亮点:服务器不存储任何用户状态,所有身份信息都加密在 JWT 中,减轻服务器压力,适配分布式、微服务架构。

2.3 JWT 结构:三段式字符串(面试必背)

JWT 不是加密字符串,而是一段用 “.” 分隔的明文+签名组合,共三部分:Header.Payload.Signature,每一部分都有明确作用,缺一不可。

① Header(头部)

作用:描述 JWT 的「加密算法」和「令牌类型」,格式为 JSON,然后通过 Base64URL 编码(注意:是 Base64URL,不是普通 Base64,避免特殊字符影响传输)。

{
  "alg": "HS256",  // 签名算法:常用 HMAC-SHA256(对称加密),也可用 RS256(非对称加密)
  "typ": "JWT"     // 令牌类型,固定为 JWT
}
② Payload(负载/数据)

作用:存放需要传递的用户身份信息(如用户 ID、用户名、角色),格式也是 JSON,同样通过 Base64URL 编码。

重点避坑:Payload 只是「编码」,不是「加密」!任何人拿到 JWT 后,都能通过 Base64URL 解码看到里面的内容,所以 绝对不能存放密码、密钥等敏感信息

常用标准字段(推荐使用,规范且通用):

  • iss:签发人(如后端服务域名);
  • exp:过期时间(必填,时间戳格式,避免 Token 永久有效);
  • sub:主题(通常是用户 ID);
  • iat:签发时间。
{
  "userId": 10086,    // 用户唯一标识(核心信息)
  "username": "zhangsan",  // 用户名(非敏感信息)
  "role": "admin",     // 用户角色(用于权限控制)
  "exp": 1743206400    // 过期时间(必填,这里是示例时间戳)
}
③ Signature(签名)

作用:防止 JWT 被篡改,是 JWT 安全的核心,由后端通过固定算法生成,只有服务器知道密钥。

生成公式(以 HS256 算法为例):

Signature = HMACSHA256(
  Base64URL(Header) + "." + Base64URL(Payload),
  服务器密钥(secret)
)

原理:后端验证时,会重新计算签名,如果计算结果和 JWT 中的 Signature 不一致,说明 Token 被篡改,直接拒绝请求。

注意:服务器密钥(secret)必须复杂且保密,绝对不能硬编码在前端,否则会导致 JWT 被伪造,整个系统都不安全。

2.4 前端实战:JWT 的具体使用(可直接复用)

结合前端常用的 Fetch 和 Axios,整理了最实用的代码片段,直接复制到项目中即可使用。

① 登录成功后存储 Token
// 登录请求(Fetch 版本)
async function login(username, password) {
  try {
    const res = await fetch("/api/login", {
      method: "POST",
      headers: {
        "Content-Type": "application/json", // 必须指定请求头
      },
      body: JSON.stringify({ username, password }),
    });
    const data = await res.json();
    
    // 登录成功,存储 JWT(推荐 localStorage,适配多页面共享)
    if (data.token) {
      localStorage.setItem("token", data.token);
      // 可选:存储 refreshToken(用于无感刷新)
      if (data.refreshToken) {
        localStorage.setItem("refreshToken", data.refreshToken);
      }
      // 跳转到首页
      window.location.href = "/home";
    }
  } catch (err) {
    console.error("登录失败:", err);
    alert("用户名或密码错误");
  }
}
② 请求拦截器:自动携带 Token

实际项目中,我们不会每次请求都手动添加 Token,而是用 Axios 拦截器统一处理,更高效。

// 引入 Axios(需先安装:npm install axios)
import axios from "axios";

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

// 请求拦截器:每次请求自动携带 Token
request.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem("token");
    if (token) {
      // 规范格式:Bearer + 空格 + Token(后端需对应解析)
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (err) => {
    return Promise.reject(err);
  }
);

// 响应拦截器:处理 Token 过期(可选,优化用户体验)
request.interceptors.response.use(
  (res) => res.data,
  (err) => {
    // Token 过期(后端返回 401)
    if (err.response?.status === 401) {
      // 清除过期 Token,跳转到登录页
      localStorage.removeItem("token");
      localStorage.removeItem("refreshToken");
      window.location.href = "/login";
      alert("登录已过期,请重新登录");
    }
    return Promise.reject(err);
  }
);

export default request;
③ 退出登录:清除 Token
function logout() {
  // 清除存储的 Token
  localStorage.removeItem("token");
  localStorage.removeItem("refreshToken");
  // 跳转到登录页
  window.location.href = "/login";
  // 可选:清除其他用户相关信息(如用户信息缓存)
  localStorage.removeItem("userInfo");
}

三、Session 详解:传统项目的有状态认证方案

3.1 什么是 Session?

Session(会话),是服务器为每个访问的浏览器用户,单独创建的一块「存储区域」,用于保存用户的登录状态、临时信息等。

简单理解:用户第一次访问服务器时,服务器会给用户建一个“专属档案”(Session),然后给这个档案分配一个唯一编号(SessionId),再把编号发给浏览器;后续浏览器每次请求,都带着这个编号,服务器通过编号找到对应的“档案”,就能认出用户。

核心亮点:用户真实数据都存在服务器,客户端只存一个 SessionId,安全性更高(相对 JWT 而言,不易被篡改)。

3.2 Session 核心登录流程

  1. 用户第一次访问服务器(或发起登录请求);
  2. 服务器验证用户身份(如用户名密码),验证通过后,创建一个 Session,生成唯一的 SessionId;
  3. 服务器通过 Set-Cookie 响应头,将 SessionId 发送给浏览器;
  4. 浏览器自动保存这个 Cookie(Cookie 名称通常是 JSESSIONIDconnect.sid);
  5. 之后浏览器每次发起请求,都会自动携带这个 Cookie(SessionId);
  6. 服务器接收请求后,通过 Cookie 中的 SessionId,找到对应的 Session,确认用户身份,合法则放行。

3.3 Session 的存储位置与结构

① 存储位置
  • 默认:服务器内存(开发环境常用,缺点:服务器重启后 Session 丢失);
  • 生产环境:通常存在 Redis、MongoDB 或数据库中(持久化存储,支持分布式共享)。
② 结构(简单理解)

服务器中的 Session 本质是一个键值对集合,键是 SessionId,值是用户相关信息,大致结构如下:

sessions: {
  "abc123": { userId: 1001, username: "zhangsan", loginAt: 1690000000, expires: 1690086400 },
  "def456": { userId: 1002, username: "lisi", loginAt: 1690000100, expires: 1690086500 },
  // 更多用户 Session...
}

浏览器端只存储 SessionId(如 abc123),不存储任何用户真实信息。

3.4 Session 的关键依赖:Cookie

Session 强依赖 Cookie,因为 SessionId 是通过 Cookie 传递的——如果用户浏览器禁用了 Cookie,Session 将无法正常工作。

补充:早期有“URL 重写”方案(将 SessionId 拼在 URL 后面,如 http://xxx.com?sid=abc123),但这种方式不安全(URL 可能被泄露)、不优雅,现在几乎不用。

3.5 Session 安全机制(面试高频)

Session 的安全性,主要依赖 Cookie 的安全属性,常用配置如下(后端设置):

  • HttpOnly:禁止 JS 读取 Cookie,防止 XSS 攻击(避免 SessionId 被恶意脚本窃取);
  • Secure:只在 HTTPS 协议下传输 Cookie,防止 HTTP 协议下被截获;
  • SameSite:限制 Cookie 跨域传递,防止 CSRF 攻击;
  • 过期时间:服务器会给 Session 设置过期时间,超时后自动销毁,避免 Session 长期有效。

四、JWT vs Session 终极对比(项目选型必看)

很多新手纠结“该用 JWT 还是 Session”,其实核心看项目架构——前后端分离、微服务、移动端项目,优先用 JWT;传统单体项目、管理后台,可用 Session

下面用表格对比两者的核心差异,面试直接背就能用:

对比维度JWTSession
状态存储客户端存储,无状态服务器存储,有状态
数据位置客户端(JWT 中)服务器(内存/Redis/数据库)
传输方式请求头携带(Authorization: Bearer Token)Cookie 自动携带(SessionId)
跨域支持支持,无跨域限制不友好,Cookie 跨域限制严格
分布式/集群支持天然支持,无需额外配置需做 Session 共享(如 Redis),配置复杂
主动销毁无法主动销毁,只能等过期支持,后端可直接删除 Session
安全性依赖签名和 HTTPS,Payload 可解码较高,数据在服务器,依赖 Cookie 安全属性
适用场景前后端分离、微服务、APP、小程序传统单体项目、管理后台、内部系统

五、JWT 与 Session 优缺点详解(避坑重点)

5.1 JWT 优缺点

优点
  • 无状态:服务器不存储会话信息,减轻服务器压力,支持高并发;
  • 跨域友好:不依赖 Cookie,支持前后端分离、跨域项目;
  • 通用性强:支持 Web、APP、小程序等所有前端场景;
  • 自包含:一次性携带所有需要的用户信息,无需反复查数据库。
缺点
  • 无法主动作废:一旦签发,在过期前一直有效(解决方案:用短过期时间 + Refresh Token 无感刷新);
  • Payload 可解码:不能存放密码、密钥等敏感信息;
  • 体积较大:比 Cookie 大,每次请求都携带,会占用少量流量;
  • 密钥风险:一旦服务器密钥泄露,攻击者可伪造 JWT,整个系统不安全。
JWT 安全优化方案(必做)
  • 设置短过期时间(如 1-2 小时),配合 Refresh Token 实现无感刷新;
  • 使用 HTTPS 协议,防止 JWT 在传输过程中被截获;
  • Payload 只存非敏感信息(如 userId、角色),绝对不存密码;
  • 服务器密钥要复杂(如随机字符串+加密),绝不泄露、不硬编码在前端;
  • 前端存储:优先存在 HttpOnly Cookie 中(防 XSS),而非 localStorage。

5.2 Session 优缺点

优点
  • 安全性高:真实用户数据在服务器,客户端只存 SessionId,不易被篡改;
  • 可主动销毁:支持退出登录、强制下线、踢人等场景,灵活可控;
  • 天然支持状态管理:适合需要维护用户临时状态的场景(如购物车);
  • 前端无需额外操作:浏览器自动携带 Cookie,开发成本低。
缺点
  • 服务器有状态:用户量增大时,服务器存储压力增大,影响性能;
  • 分布式/集群困难:多服务器部署时,Session 无法共享,需额外配置 Redis 等中间件;
  • 跨域麻烦:Cookie 跨域限制严格,不适合前后端分离、跨域项目;
  • 依赖 Cookie:用户禁用 Cookie 后,Session 无法使用。

六、常见误区澄清(面试避坑)

  • 误区 1:用了 JWT 就不能用 Session? 答:不是绝对,但没必要。两者解决的是同一个问题(登录认证),混用会增加复杂度,项目中一般二选一。
  • 误区 2:JWT 比 Session 更安全? 答:不一定。JWT 若密钥泄露、Payload 存敏感信息,安全性会很差;Session 配置好 Cookie 安全属性,安全性更高。
  • 误区 3:JWT 必须存在 localStorage 中? 答:不是。更安全的方式是存在 HttpOnly Cookie 中,防止 XSS 攻击;localStorage 只是使用更方便。
  • 误区 4:Session 只能存在服务器内存中? 答:不是。生产环境中,为了持久化和分布式共享,通常存在 Redis 中。

七、总结:项目中该怎么选?

一句话选型建议,直接套用:

  • 如果是 前后端分离、微服务、APP、小程序 → 优先用 JWT(无状态、跨域友好);
  • 如果是 传统单体项目、管理后台、内部系统 → 可用 Session(开发简单、安全性高);
  • 如果需要 强制下线、踢人 等场景 → 优先用 Session(可主动销毁);
  • 如果追求 高并发、分布式部署 → 优先用 JWT。

最后补充:现在前端主流项目(尤其是前后端分离),几乎都用 JWT,所以掌握 JWT 的原理、实战和安全优化,是前端开发者的必备技能。

如果觉得本文对你有帮助,欢迎点赞、收藏,关注我,后续分享更多前端实战干货和面试技巧~