通用会话控制方案

一、会话控制概念

  • 目的:在无状态的 HTTP 请求间识别/鉴权用户身份并维持登录状态。
  • 核心问题:谁保存"用户状态"?(服务器 / 客户端 / 第三方认证服务器),以及如何安全地在多请求间传递该凭证(Cookie / Authorization header)。

二、传统方案:Cookie + 服务端 Session

1) 流程

  1. 用户登录,后端验证凭证(用户名/密码)。
  2. 后端在服务器端创建 session(比如 sessionId 对应的对象存在 Redis / DB / 内存),并返回 Set-Cookie: sessionId=xxx; HttpOnly; Secure; SameSite=Lax; Path=/ 响应头部信息。
  3. 浏览器自动带上 Cookie(同源或按 CORS 配置带上 credentials: 'include'),后端根据 sessionId 在 session store 查到用户信息并授权。

2) 示例(Express + express-session + Redis)

后端:

JavaScript 复制代码
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: process.env.SESSION_SECRET,
  name: 'sid',
  resave: false,
  saveUninitialized: false,
  cookie: {
    httpOnly: true,
    secure: true,      // 生产必须 https
    sameSite: 'lax',   // 或 'strict' / 'none'(配合跨域)
    maxAge: 24 * 3600 * 1000
  }
}));

前端(fetch):

JavaScript 复制代码
fetch('/api/profile', { credentials: 'include' })

3) 优点

  • 简单明确:后端完全控制会话(可以随时废弃/修改)。
  • 支持服务端会话失效(logout 立刻生效)。
  • 易于集成细粒度权限、会话统计与审计(存 Redis 的 session 可包含登录 IP、设备信息等)。

4) 缺点

  • 水平扩展需共享 session store(Redis)。
  • Session 存储和查询带来额外延迟与成本。
  • 跨域场景复杂:需要正确配置 Access-Control-Allow-CredentialsSet-CookieSameSite=None; Secure 等,避免安全问题。

三、JWT(JSON Web Token)方案概述

1) JWT 的基本思想(无状态 token)

  • 后端发放一个签名的 token(通常是 Access Token),包含用户 id、到期时间等。
  • 前端携带 token(通常放在 Authorization header 或者放入 HttpOnly cookie)向后端请求;后端不查 session store,而是直接验证 token 的签名与有效期,若通过则鉴权成功。

JWT 典型格式:header.payload.signature(Base64 编码)

JavaScript 复制代码
const jwt = require('jsonwebtoken');
const accessToken = jwt.sign({ uid: xxx }, process.env.JWT_SECRET, { expiresIn: '15m' });
const payload = jwt.verify(accessToken, process.env.JWT_SECRET);

2) 常见拓展

  • **短期 Access Token(如 5--15 分钟)**:放在内存或短寿命存储(不放 localStorage 更安全),用于 API 调用。
  • 长期 Refresh Token(如 14 天或更长)​:安全保存(​HttpOnly, Secure cookie),用于换取新的 Access Token。每次刷新都会发送新的 refresh token。服务端会保存 refresh token 的元数据(redis)。
  • Access Token 可放在 Authorization: Bearer <token>,或也可放在 HttpOnly cookie(同域 + credentials) 推荐将 Refresh Token 放 HttpOnly cookie,Access Token 放内存并通过 Authorization header 发送(减少 CSRF 风险)。

3) 优点

  • 无状态,后端不必保存 session(容易横向扩展,适合微服务 & 无状态后端),更适合移动端 SPA 等分布式鉴权。
  • Token 自包含(本身携带用户身份信息,后端只需验证 token 签名,就直接知道 token 是否有效以及用户信息,无需再查询数据库或者 session 存储),减少每次请求的 DB 查询。
  • 与 OAuth2 兼容,便于做 SSO 与第三方登录。

4) 缺点

  • 无法轻易撤销(access token 在有效期内一直可用,除非引入黑名单/撤销机制)。
  • 如果把 JWT 放在 localStorage(可被 JS 读取)会增加 XSS 风险;如果放 Cookie,则面临 CSRF 风险。

四、Cookie Session 与 JWT 的主要区别

表格 还在加载中,请等待加载完成后再尝试复制

五、安全策略

CSRF 防护(Cookie 自动带 credential 的情况下)

  • 优先:使用 SameSite=LaxSameSite=Strict ​
  • 对于必须 SameSite=None 的跨站场景,配合 **CSRF Token(双 submit cookie)**:
    • 后端在登录时设 Set-Cookie: CSRF-Token=xxx; HttpOnly=false; Secure(可被 JS 读取)
    • 前端每次请求在 header 中带 X-CSRF-Token: <value>,后端验证 header 与 cookie 值一致。
  • 或使用 Origin / Referer 检查(只允许来自可信域的请求)。

核心:CSRF 攻击者 ​可以发请求 ​,但 ​没法读 Cookie ​,无法把 token 放到 header 中,所以校验((req.cookies["CSRF-Token"] !== req.headers["X-CSRF-Token"]))。CSRF 攻击能发请求,但拿不到 cookie 内容 。

XSS 防护(避免 token 被窃取)

  • 对可执行 token 的存储避免使用 localStorage(HttpOnly cookie 防止 JS 读取)。

JWT 可撤销性策略

  • 短寿命 access token + refresh token (refresh token 存 HttpOnly cookie):
    • access token 过期快,可减少滥用窗口。
    • refresh token 在服务端有黑名单/DB 记录,必要时撤销(例如用户登出、密码变更)。
  • **token id(jti) + 存在服务端黑名单(Redis)**:在 logout / 强制下线时把 jti 写入黑名单并在验证时检查。黑名单可设置过期与分布式缓存。
相关推荐
卷无止境19 小时前
智能体开发环境ADE浅析,编程工具的下一次范式跃迁
后端·python
码事漫谈21 小时前
DeepSeek 这波操作很凶
后端
CharlesYu011 天前
前端性能优化的第一性原理,是不断缩短“用户发起意图 → 获得可用结果”之间的时间
前端
Bs_MoneyMagnet1 天前
基于springboot+vue的医疗健康便民服务平台的设计与实现 源码+文档
java·vue.js·spring boot·后端·spring
平头哥技术团队1 天前
Day 21 _ 页内锚点_给每段起个 id,目录写 href=_#id_,点一下页面就滚到那一段
前端·html·html5
子兮曰1 天前
Bun v1.4.1 深度解析:从 Zig 到 Rust,一场 11 天、64 个 AI 代理的语言迁徙
前端·后端·bun
人民广场吃泡面1 天前
什么是AI Agent?它又能给前端带来哪些效率提升?
前端·人工智能
中科三方1 天前
两家域名注册商资质被ICANN终止:企业域名资产安全再受关注
前端·网络·安全·域名
bug总结1 天前
uniapp vue3全局方法注册使用
前端·javascript·uni-app
华无丽言1 天前
如何在宜搭中实现获取子表中的字段值赋值到父表中?
前端·javascript·低代码