【计算机网络】万字详解,一篇带你弄懂Cookie, Session, Token!


一、Cookie

1.1 Cookie 简介

在这里插入图片描述
Cookie 是一种由服务器发送并保存在用户浏览器端的小型文本数据,通常用于记录用户身份偏好设置浏览活动等信息。它的最大容量约为 4KB(大多数浏览器支持的单个 Cookie 最大大小为 4096 字节),且只能在客户端本地存储与传输。

简单来说,Cookie 就像是网站“写给你浏览器的一张便签纸”,下次你再访问该网站时,浏览器会自动带上这张“便签纸”,帮助服务器识别你是谁。


1.2 Cookie 的工作原理

Cookie 的本质是浏览器与服务器之间自动传递的一段文本信息。它的整个生命周期包括创建存储发送删除,典型工作流程如下:

  1. 创建 Cookie(Set-Cookie 响应头)

    当用户第一次访问网站时,服务器根据业务需要生成 Cookie,并在 HTTP 响应头中通过 Set-Cookie 字段发送给浏览器。示例:

    HTTP/1.1 200 OK
    Content-Type: text/html
    Set-Cookie: sessionid=abc123; Expires=Wed, 21 Jul 2025 07:28:00 GMT; Path=/; HttpOnly; Secure
    

    这个例子里,服务器告诉浏览器:

    • Cookie 名称:sessionid
    • Cookie 值:abc123
    • 过期时间:2025年7月21日
    • 作用路径:整个网站
    • 仅限服务器访问(HttpOnly
    • 仅在 HTTPS 传输(Secure

    这样浏览器会将 sessionid=abc123 保存下来。

  2. 浏览器本地保存 Cookie

    浏览器接收到 Set-Cookie 后,会将其存储在本地 Cookie 存储区(一般是浏览器配置文件中的数据库或文本文件),并在指定有效期内保留。若未设置 ExpiresMax-Age,Cookie 默认为会话 Cookie,关闭浏览器即失效。

  3. 发送 Cookie(请求头中自动携带)

    当用户再次访问该网站或在该网站内跳转页面时,浏览器会自动将与当前 URL 匹配的 Cookie放入 HTTP 请求头 Cookie 中发送给服务器。例如:

    GET /profile HTTP/1.1
    Host: www.example.com
    Cookie: sessionid=abc123
    

    注意:

    • 浏览器只会携带与当前域名和路径匹配的 Cookie。
    • 如果有多个 Cookie,会以分号分隔。
  4. 服务器读取 Cookie

    服务器收到请求后,通过解析请求头 Cookie 字段,提取出之前发给用户的 Cookie,从而识别用户身份或状态。例如:

    • 根据 sessionid 查询服务器数据库中的会话信息。
    • 判断用户是否登录。
    • 获取用户偏好设置。
  5. 更新或删除 Cookie

    在服务器处理完请求后,也可以再次通过 Set-Cookie 指令:

    • 更新 Cookie(比如延长过期时间)。
    • 删除 Cookie(通过设置过期时间为过去的时间)。

    例如删除 Cookie:

    Set-Cookie: sessionid=; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=/
    

    浏览器在接收到这个响应后,会立刻移除本地的 sessionid Cookie。

在这里插入图片描述

注意:

  • Cookie 的匹配规则:

    • 域名(Domain):必须与当前访问的域名一致或在指定的子域名范围内。 * 路径(Path):URL 必须在 Cookie
      指定的路径范围内。 * 安全属性(Secure):如果设置了 Secure,只有 HTTPS 请求才会携带 Cookie。
  • 自动携带机制:

    • Cookie 不需要开发者手动在请求里加,浏览器会在每次请求中自动携带。
  • 生命周期:

    • 会话 Cookie:关闭浏览器即失效。 * 持久 Cookie:到指定过期时间失效。

举例:一次完整的 Cookie 流程

假设你登录一个网站:

  1. 登录请求:

    POST /login
    

    服务器验证成功后响应:

    Set-Cookie: token=xyz789; Path=/; HttpOnly; Expires=Fri, 01 Aug 2025 12:00:00 GMT
    
  2. 浏览器保存:
    保存 token=xyz789

  3. 访问个人中心:

    GET /profile
    Cookie: token=xyz789
    

    服务器通过 token 验证身份。

  4. 退出登录:

    GET /logout
    

    响应:

    Set-Cookie: token=; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=/
    

    浏览器删除 Cookie。

网站能基于上述简单流程完成 Cookie 实现“状态保持”和“用户识别”。


1.3 Cookie 的常见用途

  1. 保持登录状态: 当用户登录后,网站可以将登录状态信息(如 Session ID)保存在 Cookie 中。下次访问时,用户不需要重新登录,服务器通过Cookie 自动识别用户身份。
  2. 记住用户偏好: 如用户选择的语言、主题样式、阅读模式等设置,都可以通过 Cookie 存储,在用户下次访问时自动加载。
  3. 购物车功能: 在未登录状态下,用户将商品添加到购物车,网站通过 Cookie 存储这些商品信息,使得用户下次访问仍能看到购物车内容。

1.4 如何使用 Cookie

以下为JavaScript 操作示例

设置 Cookie:

// 设置一个名为 username 的 Cookie,值为 "Tom",过期时间为7天
document.cookie = "username=Tom; expires=Fri, 20 Jul 2025 12:00:00 UTC; path=/";

读取 Cookie:

// 获取所有 Cookie 字符串
let cookies = document.cookie;
console.log(cookies); // 输出:username=Tom

删除 Cookie:

// 将 Cookie 的过期时间设置为过去,即可删除
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/";

1.5 Cookie 的安全性与限制

安全性问题:

  • 容易被劫持:如果未使用 HTTPS,Cookie 数据在传输过程中可能被中间人攻击(MITM)。
  • 容易被 XSS 攻击访问:若未设置 HttpOnly,Cookie 可被 JavaScript 读取,从而造成信息泄露。

常用的安全属性:

  • HttpOnly:防止前端 JavaScript 访问 Cookie。
  • Secure:仅在 HTTPS 下传输 Cookie。
  • SameSite:防止跨站请求伪造(CSRF)攻击。

限制:

  • 单个 Cookie 最大容量约为 4KB。
  • 每个域名最多可存储约 20 个 Cookie。
  • Cookie 会随每次请求发送,占用带宽。

1.6 Cookie 的优缺点小结

优点 说明
简单易用 浏览器自动管理,开发者只需配置即可
支持跨页面存储 可以在同一域名下多个页面共享
持久化 可设置过期时间,实现长时间保存

缺点 说明
容量有限 单个 Cookie 体积小,无法存储大量数据
安全性较低 易受 XSS、CSRF 和中间人攻击威胁
每次请求自动发送 占用带宽,不适合频繁变化的大数据

二、Session

2.1 Session 简介

Session(会话)是服务器用于记录用户状态信息的一种机制。它通过在服务器端存储数据,配合客户端的标识(通常是 Cookie 中的 Session ID),在多次请求之间跟踪同一个用户的访问状态。相比 Cookie 把数据直接保存在浏览器里,Session 把数据放在服务器上,客户端只保存一个Session ID。这样更安全,不容易被用户篡改。


2.2 Session 的工作机制与原理

Session 的核心目标就是在一次次 HTTP 请求之间维持同一个用户的状态,实现“同一个用户多次访问时能被识别”。
由于 HTTP 协议是无状态的(每个请求彼此独立),就需要 Session 来补充状态管理。

在这里插入图片描述

下面分步骤详细讲解:

1. 用户首次访问服务器

  • 用户第一次访问网站(如访问登录页)时,服务器检测到当前请求中没有携带任何 Session ID。

  • 服务器会:

    • 创建一个新的 Session 对象(一般保存在服务器内存或 Redis 中)。
    • 为这个 Session 分配一个唯一的 Session ID(例如:ABCDEF1234567890)。

这个 Session ID 是用来标识这份 Session 数据的“钥匙”。

2. 服务器把 Session ID 返回给浏览器

  • 服务器通过 HTTP 响应头把 Session ID 返回给客户端。

  • 一般用 Set-Cookie 指令:

    Set-Cookie: JSESSIONID=ABCDEF1234567890; Path=/; HttpOnly
    

    解释:

    • JSESSIONID=...:Session ID 的名字和内容。
    • Path=/:表示这个 Cookie 在整个网站有效。
    • HttpOnly:防止 JavaScript 读取,增加安全性。

浏览器接收到响应后,会自动把这个 Cookie 存储在本地。

3. 浏览器保存 Session ID

  • 浏览器把 JSESSIONID 保存在 Cookie 中。
  • 如果这个 Cookie是会话 Cookie(没有设置 ExpiresMax-Age),那么只在浏览器进程中存在,关闭浏览器后就失效。
  • 如果配置了持久化时间(不常用),则浏览器重启后依然保留。

4. 浏览器后续请求自动带上 Session ID

  • 当用户再次访问同一网站时:

    • 浏览器会在请求头自动带上这个 Cookie:

      Cookie: JSESSIONID=ABCDEF1234567890
      
  • 无需开发者手动设置。

5. 服务器根据 Session ID 找到对应 Session

  • 服务器接收到请求后,会:

    • 读取 Cookie 中的 JSESSIONID
    • 在服务器端 Session 存储(内存或数据库)中,根据这个 Session ID 找到对应的 Session 数据。
  • 如果找不到(过期或被销毁),服务器会重新创建一个新的 Session。


Session 与 Cookie 的关系(JSESSIONID)

很多人容易混淆 Cookie 和 Session,这里特别说明:

Cookie Session
存储位置:浏览器 存储位置:服务器
数据:可以直接存储业务数据(如用户名) 数据:业务数据放在服务器,浏览器只存 Session ID
安全性:容易被伪造 安全性:只要 Session ID 保密,数据不会被篡改

JSESSIONID 就是 Session 的“身份证号码”,用来在客户端和服务器之间“对号入座”。


URL 重写

如果浏览器禁用了 Cookie(或不支持 Cookie),Session 也可以通过 URL 传递 Session ID,例如:https://www.example.com/shop/list;jsessionid=ABCDEF1234567890

这种方式叫URL 重写,不安全也不常用(容易泄露 Session ID)。


2.3 Session 的存储方式与生命周期

Session 本质上是一份服务器端的“数据字典”,以 Session ID 作为关键字保存对应的数据。

常见存储方式:

1. 内存(默认方式)

  • Session 通常保存在服务器的内存中(如 Tomcat 内存)。

  • 特点:

    • 访问速度非常快。
    • 适合小型单体应用。
  • 缺点:

    • 服务器重启会丢失所有 Session。
    • 不支持多台服务器共享 Session(集群环境)。

2. 文件存储

  • Session 会序列化后写入服务器的硬盘文件中。

  • 特点:

    • 重启服务器也能恢复 Session。
    • 适合 Session 量较小的系统。
  • 缺点:

    • 文件读写性能比内存差。
    • 部署多节点需要共享存储或同步。

3. 数据库存储

  • 将 Session 数据保存在关系型数据库(如 MySQL)。

  • 特点:

    • 跨服务器共享 Session。
    • 可以持久化历史数据。
  • 缺点:

    • 数据库连接压力大。
    • 性能比内存和 Redis差,需优化。

4. 分布式缓存(如 Redis、Memcached)

  • 将 Session 存入 Redis 集群,是目前最流行的高并发解决方案。

  • 特点:

    • 支持分布式共享。
    • 高并发读写性能。
    • 内存存储,速度快。
  • 缺点:

    • 需要额外部署 Redis。
    • Session 需序列化和反序列化。

Session 的生命周期由服务器管理,包括以下几个阶段:

1. 创建

  • 当用户第一次请求需要状态跟踪的资源时(如访问登录页),服务器通过:

    request.getSession();
    

    创建一个新的 Session 对象。

  • 同时分配一个唯一的 Session ID。

2. 活动更新

  • 每一次请求,Session 的最后访问时间会被更新。
  • 如果用户持续访问,Session 会一直处于活跃状态,不会过期。

3. 过期与销毁

Session 会在以下几种情况下销毁:

  1. 空闲超时自动失效

    • 如果在指定时间内没有访问,服务器会自动清理 Session。

    • 默认时间:30分钟(Tomcat)。

    • 可通过 web.xml 配置:

      <session-config>
        <session-timeout>30</session-timeout>
      </session-config>
      

      表示30分钟无操作即失效。

  2. 手动销毁

    • 程序调用:

      session.invalidate();
      

      会立即销毁 Session,清理所有数据。

  3. 服务器重启

    • 如果 Session 存在于内存,重启服务器会清空。
    • 若使用 Redis/数据库等持久化存储,则可恢复。

2.4 Session 常用方法示例

以 Java Servlet 为例,在 Java Web 中,HttpSession 接口提供一系列方法,方便开发者操作 Session。

以下示例基于 Servlet:

1. 获取 Session

HttpSession session = request.getSession();
  • 如果 Session 不存在,创建新的 Session。
  • 如果已有 Session,直接返回。

2. 判断 Session 是否存在

如果只想获取现有 Session,不创建新 Session:

HttpSession session = request.getSession(false);
if (session == null) {
    // Session不存在
}

3. 存储数据

把数据保存在 Session 中,类似 key-value:

session.setAttribute("username", "Alice");
session.setAttribute("role", "admin");
  • 键是字符串。
  • 值可以是任意对象(会被序列化)。

4. 获取数据

获取存储在 Session 中的数据:

String username = (String) session.getAttribute("username");

如果没有对应的键,会返回 null

5. 删除数据

移除 Session 中的某个属性:

session.removeAttribute("username");

6. 手动销毁 Session

立即清空 Session 中所有数据并让其失效:

session.invalidate();
  • 用户再次请求时,会创建一个新的 Session。

7. 获取 Session ID

获取当前 Session 的唯一标识:

String sessionId = session.getId();
  • 常用于调试或日志。

8. 判断 Session 是否是新创建

有时候需要判断当前 Session 是否是刚创建的:

boolean isNew = session.isNew();
if (isNew) {
    // 刚创建的Session
}

9. 设置 Session 最大不活动时间

动态设置超时时间(单位:秒):

session.setMaxInactiveInterval(600); // 10分钟
  • 0 表示永不超时。
  • -1 表示跟随服务器默认。

10. 示例:登录流程

// 登录校验成功
HttpSession session = request.getSession();
session.setAttribute("loginUser", "alice");

// 以后只要有Session就表示已登录

11. 示例:退出登录

HttpSession session = request.getSession(false);
if (session != null) {
    session.invalidate();
}

当然,下面我来详细写三、Token部分:


三、Token

3.1 Token 简介

Token(令牌)是一段由服务器生成的字符串,用来代表用户的身份信息或访问权限。Token 在认证和授权中常被用作用户身份的凭证,与传统 Session 不同,Token 不依赖服务器记录状态,而是通过签名和加密保障安全性。


3.2 Token 的无状态性与自包含性

无状态性 (Stateless)
Token 认证最大的特点是无状态

  • 服务器不需要保存每个用户的登录信息或会话数据。
  • 每一次请求只需要验证 Token 本身是否合法。

自包含性 (Self-contained)
Token 中包含用户的必要信息(如用户 ID、权限、过期时间等),即自包含

  • 不必再去数据库查找。
  • 签名保证内容未被篡改。

这种机制非常适合分布式系统微服务


3.3 Token 的工作流程

下面是一个典型的基于 Token 的身份认证和授权流程,分为登录签发客户端存储与请求携带服务器验证三大步骤。

在这里插入图片描述
1.用户登录与 Token 签发

这是 Token 认证的第一步,也就是获取 Token的过程:

(1)用户发起登录请求

  • 用户在客户端(浏览器、APP等)输入账号密码。

  • 客户端将用户名、密码通过 HTTPS POST 请求发送给后端:

    POST /api/login
    Content-Type: application/json
    
    {
      "username": "alice",
      "password": "password123"
    }
    

(2)服务器验证用户凭证

  • 服务器查询数据库检查用户名和密码是否正确。
  • 如果验证失败,返回401(Unauthorized)。

(3)生成 Token

  • 验证成功后,服务器根据用户信息生成一个 Token。

  • 通常使用 JWT(JSON Web Token),内容包括:

    • sub: 用户ID或用户名(Subject)
    • iat: 签发时间(Issued At)
    • exp: 过期时间(Expiration)
    • 其他自定义字段,如用户角色(role)、权限(scope)
  • 最后,服务器根据约定的**密钥(Secret Key)**进行签名,防止篡改。

(4)返回 Token

  • 服务器将 Token 放在响应体中返回:

    {
      "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
    }
    

2.客户端存储与请求携带

获取到 Token 后,客户端要安全存储并在每次请求时携带:

(1)存储位置

  • Web端常用:

    • LocalStorage(浏览器本地存储,持久化)
    • SessionStorage(浏览器会话存储,关闭浏览器失效)
    • HttpOnly Cookie(更安全,不可通过 JavaScript 访问)
  • 移动端常用:

    • 本地数据库(SQLite)
    • 安全存储组件

如果需要抵御 XSS 攻击,优先考虑 HttpOnly Cookie

(2)每次请求携带 Token

  • 客户端在调用受保护 API 时,要在 HTTP Header 中加 Authorization

    GET /api/user/profile
    Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
    
  • “Bearer”表示使用令牌认证。

(3)请求示例(JavaScript fetch)

fetch('/api/user/profile', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer ' + token
  }
})

服务器就可以识别该请求身份。


3.服务器验证

服务器在收到每个请求时,都会对 Token 进行严格验证:

(1)从请求头提取 Token

  • 读取 Authorization 请求头。
  • 判断格式是否正确(前缀是 Bearer)。

(2)解析 Token

  • 将 Token 拆解成:

    • Header
    • Payload
    • Signature
  • Base64 解码 Header 和 Payload。

(3)验证签名

  • 服务器使用相同的密钥(或公钥)重新生成签名。
  • 与 Token 中的签名比对,防止伪造。

(4)校验有效期

  • 比较 exp(过期时间)与当前时间:

    • 如果已过期,返回 401 Unauthorized。
    • 如果未过期,继续处理请求。

(5)读取用户信息

  • 从 Payload 中提取 sub(用户ID)和其他信息。
  • 可根据需要查询数据库进一步确认状态(如是否封禁)。

(6)处理业务逻辑

  • 如果验证成功,执行请求对应的操作(查询、更新等)。
  • Token 验证完全由服务器完成
  • 验证过程不依赖 Session,不占用内存。
  • 完全“无状态”。

3.4 Token 的常见类型

下面介绍几种常见的 Token 类型:

1. JWT(JSON Web Token)

  • 最流行的 Token 格式。

  • 基本结构:

    Header.Payload.Signature
    
    • Header:声明算法类型
    • Payload:数据(如用户信息)
    • Signature:签名
  • 自包含、易解析。

示例:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

2. OAuth 2.0 Token

主要用于授权(第三方登录、API访问):

  • Access Token: 访问受保护资源时携带,短有效期。
  • Refresh Token: 用于刷新新的 Access Token,有较长有效期。

常用于:

  • 微信、GitHub 登录。
  • 微服务之间授权。

3. API Token

  • 一般为随机字符串。
  • 用于接口访问授权。
  • 不一定包含用户信息,通常在数据库中验证。

示例:

123456abcdef7890

3.5 Token 的应用场景

Token 在现代 Web 和分布式系统中用途非常广泛,解决了很多传统 Session/Cookie 模式的局限性。


1.前后端分离

过去的传统 Web 应用(如 JSP、PHP)由服务器渲染页面,直接使用 Session 保存用户状态。在现代架构中,前端与后端完全分离,前端用 Vue、React、Angular 编写 SPA(Single Page Application),后端仅提供 RESTful API 或 GraphQL。

为什么适合用 Token:

  • Session 必须依赖服务器内存/存储,会话信息需要和前端共享,而 Token 是自包含无状态的。
  • 客户端只需在本地保存 Token(LocalStorage / Cookie),每次请求带上 Token 即可,无需与后端建立状态会话。

流程:

  1. 前端登录后,从后端获取一个 JWT Token。
  2. 将 Token 保存在 localStorage(或安全 Cookie)。
  3. 后续通过 Authorization: Bearer <token> 请求接口。
  4. 后端仅通过 Token 验证身份,不依赖 Session。

2.单点登录(SSO:Single Sign-On)

企业或组织内部有多个系统(门户、OA、CRM等),用户只需登录一次,就可以在不同系统之间共享身份。

  • 中央认证服务器颁发一个 Token。
  • 各子系统接收到 Token 后验证签名与有效期,确认用户身份。

3.跨域认证

前端和后端分别部署在不同域名(如 api.example.comwww.example.com),传统 Cookie 依赖同源策略,跨域使用困难。

  • Token 通过请求头携带,不依赖 Cookie。
  • 跨域也能验证身份,只需配置 CORS。

4.微服务架构

后端拆分为多个微服务,每个微服务负责部分业务。=

  • 请求先到 API 网关。
  • 网关验证 Token。
  • 将用户信息(如 userId)传递到下游微服务。
  • Token中可封装权限、角色等信息。=

5.移动端、第三方接入

移动端与后端交互没有 Cookie,Token成为身份验证的标准方式。

  • 提供 API Token,允许开发者调用 API。
  • 通过签名和过期机制保证安全。

3.6 Token 的优缺点小结

优点 说明
无状态 服务器无需存储会话数据,减轻内存/存储压力。
灵活 天生支持分布式部署和跨域调用,适合微服务、前后端分离场景。
自包含 Token 内含用户信息、权限、过期时间等,可减少数据库或缓存查询。

缺点 说明
无法主动失效 颁发后在有效期内有效,需额外设计黑名单/撤销列表来提前失效。
泄露风险高 一旦被截获,攻击者可以在有效期内随意使用,需配合 HTTPS、短过期时间。
体积较大 比传统 Session ID(短字符串)要大,尤其是带 payload 的 JWT。

四、Cookie、Session、Token 的比较

4.1 存储位置对比

技术 数据存储位置
Cookie 客户端(浏览器),通过请求头自动携带
Session 服务器端(内存、数据库、缓存等),客户端仅保存 Session ID
Token 客户端(LocalStorage/Cookie),服务器不保存状态

4.2 安全性对比

技术 安全性特点
Cookie 数据暴露在客户端,易被查看或篡改;需配合 HttpOnly、Secure 等属性提高安全
Session 数据保存在服务器,相对安全;Session ID 若被盗,仍可能被伪造请求
Token 自包含、签名验证,防篡改;若泄露则有效期内可被利用;需 HTTPS、短有效期防护

4.3 性能与扩展性对比

技术 性能与扩展性说明
Cookie 不依赖服务器存储,性能影响小;数据随每个请求发送,增加带宽开销
Session 服务端需维护 Session,单机性能高;分布式部署需做共享或粘性会话
Token 无状态,适合分布式和微服务架构;每次验证 Token,可能增加 CPU 负担

4.4 适用场景对比

技术 典型适用场景
Cookie 简单用户偏好存储(主题、语言)、低安全要求的状态数据
Session 中小型系统、登录态保持(如传统单体应用)
Token 前后端分离、移动端、跨域、微服务、单点登录(SSO)等现代场景

4.5 优缺点总结

技术 优点 缺点
Cookie 简单易用,浏览器自动携带 容易被篡改、存储空间有限、安全性差
Session 安全性好、可存储任意对象 占用服务器资源、分布式扩展复杂
Token 无状态、支持跨域分布式、灵活 无法主动失效、泄露风险高、体积大

当然,下面给你写一个4.6 三者之间的关系,我会尽量详细、清晰、全面地阐述:


4.6 三者之间的关系

Cookie、Session、Token虽然都是在多次HTTP请求中维持用户状态的机制,但它们相互配合、各司其职,经常组合使用而不是完全替代。可以从底层原理、演化背景、协作方式来理解三者关系:


1.Cookie 是最基础的状态保持手段

  • HTTP 是无状态协议,每次请求服务器都无法识别是谁发的。
  • Cookie的出现,就是在浏览器端存储小段文本数据(键值对),并在请求时自动带到服务器,让服务器“记住”客户端。
  • Cookie既可以直接保存用户数据(如userId、token等),也可以作为Session ID的载体

示例:

Cookie: sessionid=abcd1234

2.Session 是在服务器端“保存用户状态”的方案

  • Session实际上就是服务器用来保存用户状态的容器

  • 服务器在创建Session时,会生成一个唯一的Session ID

  • 这个Session ID必须让客户端带回来,最常见的就是放进Cookie里(如Set-Cookie: JSESSIONID=...)。

  • 所以,Session和Cookie往往配合使用:

    • Cookie负责保存和传递Session ID。
    • Session存储用户状态和业务数据。
  • 如果不用Cookie,也可以把Session ID放在URL参数里(URL重写),但这不安全。
    在这里插入图片描述

简化流程:

客户端 → 请求 → 服务器创建Session → 返回带Session ID的Cookie → 客户端保存 → 下次请求带Session ID → 服务器根据ID找到Session。


3.Token 是在“无状态化”趋势下的演进产物

  • 随着前后端分离、移动应用、微服务的发展,传统Session遇到服务器负载、分布式共享问题。

  • Token(特别是JWT)提出了无状态认证:所有需要的信息都放在Token里,服务器不再存储会话。

  • Token本质上依然要在客户端“保存和传递”,这部分往往依赖Cookie或者LocalStorage:

    • 如果用Cookie存Token,浏览器会自动在请求中带上。
    • 如果用LocalStorage,需在每次请求时在Header里手动附带。
  • 所以,Token也经常和Cookie结合:只是Cookie里保存的不是Session ID,而是Token。

示例:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6...

4.从演化脉络来看

  1. Cookie:

    • 最原始的解决方案,直接把状态存在浏览器里。
  2. Session:

    • 把状态放到服务器里,用Cookie传Session ID。
  3. Token:

    • 把状态又放回客户端(Token里自包含用户信息),但多了签名、加密,保证安全性和一致性。

这是一种演化和适应:

  • Cookie:单纯的客户端状态。
  • Session:客户端-服务器状态分离,依赖Session ID。
  • Token:彻底无状态,服务器只验证签名。

5.三者组合使用的典型方式

在实际应用中,三者经常组合使用,例如:

  • Session + Cookie:

    • 传统单体应用最常见,登录后服务器建Session,客户端用Cookie带Session ID。
  • Token + Cookie:

    • 把Token(如JWT)放在HttpOnly Cookie里,自动携带且防XSS。
  • Token + LocalStorage:

    • 前端自己存Token,每次请求在Header里手动携带。

对比维度 Cookie Session Token
本质 客户端存储状态 服务器存储状态 客户端存储状态(自包含)
是否需要Cookie 是(直接或间接存储数据) 是(存Session ID) 可选(可存在Cookie或LocalStorage)
是否无状态
安全性依赖 需要配合安全属性(HttpOnly、Secure) Session ID安全性 签名防篡改

如果把状态维持比作“存放钥匙”:

  • Cookie: 把钥匙和房间信息都给你(客户端全权负责)。
  • Session: 把钥匙交给你(Session ID),房间信息放在服务器保管。
  • Token: 把钥匙和房间信息做成一张防伪证书给你(Token里自带信息和签名)。

当然!下面我给你写Java 示例,演示三种场景:


五、示例

5.1 使用 Session 实现登录验证(Java 示例)

这是最常见的基于 Session 的登录流程

流程概述:

  1. 用户提交用户名和密码。
  2. 服务器验证成功后,将用户信息写入 Session。
  3. 后续请求,通过 Session 判断是否登录。

示例代码(Servlet):

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        // 假设验证用户名和密码(此处仅示例)
        if ("admin".equals(username) && "123456".equals(password)) {
            // 获取 Session
            HttpSession session = request.getSession();
            // 存储登录标识
            session.setAttribute("user", username);
            response.getWriter().println("Login success");
        } else {
            response.getWriter().println("Invalid credentials");
        }
    }
}

访问受保护资源时:

@WebServlet("/profile")
public class ProfileServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

        HttpSession session = request.getSession(false);
        if (session != null && session.getAttribute("user") != null) {
            String username = (String) session.getAttribute("user");
            response.getWriter().println("Welcome, " + username);
        } else {
            response.getWriter().println("Please login first.");
        }
    }
}

5.2 使用 Cookie 实现“记住我”功能

场景:
用户勾选“记住我”,服务器写入一个长期有效的 Cookie,下一次访问时自动填充用户名。

示例代码:

@WebServlet("/login-cookie")
public class LoginCookieServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String remember = request.getParameter("remember"); // "on"表示勾选

        if ("admin".equals(username) && "123456".equals(password)) {
            if ("on".equals(remember)) {
                // 创建Cookie
                Cookie cookie = new Cookie("rememberUser", username);
                cookie.setMaxAge(7 * 24 * 60 * 60); // 7天
                cookie.setPath("/");
                response.addCookie(cookie);
            }
            response.getWriter().println("Login success with remember me");
        } else {
            response.getWriter().println("Invalid credentials");
        }
    }
}

后续访问时读取 Cookie:

@WebServlet("/home")
public class HomeServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        
        String username = null;
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie c : cookies) {
                if ("rememberUser".equals(c.getName())) {
                    username = c.getValue();
                    break;
                }
            }
        }

        if (username != null) {
            response.getWriter().println("Welcome back, " + username);
        } else {
            response.getWriter().println("Hello, guest");
        }
    }
}

5.3 使用 JWT 实现无状态登录验证(示意流程)

要点:

  • 登录成功后生成JWT。
  • 客户端保存Token(通常放在前端LocalStorage)。
  • 每次请求通过Authorization头携带Token。
  • 服务端验证Token,无需Session。

⚠ Java中生产JWT通常用jjwt库io.jsonwebtoken:jjwt

依赖示例(Maven):

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

示例代码:

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.Claims;

@WebServlet("/login-jwt")
public class LoginJwtServlet extends HttpServlet {
    private static final String SECRET_KEY = "mysecretkey123456";

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

        String username = request.getParameter("username");
        String password = request.getParameter("password");

        if ("admin".equals(username) && "123456".equals(password)) {
            // 生成JWT
            String jwt = Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + 60 * 60 * 1000)) // 1小时
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
            
            response.getWriter().println("Token: " + jwt);
        } else {
            response.getWriter().println("Invalid credentials");
        }
    }
}

验证Token:

@WebServlet("/secure")
public class SecureServlet extends HttpServlet {
    private static final String SECRET_KEY = "mysecretkey123456";

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        
        String authHeader = request.getHeader("Authorization");
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            String token = authHeader.substring(7);
            try {
                Claims claims = Jwts.parser()
                    .setSigningKey(SECRET_KEY)
                    .parseClaimsJws(token)
                    .getBody();
                
                String username = claims.getSubject();
                response.getWriter().println("Hello, " + username);
            } catch (Exception e) {
                response.getWriter().println("Invalid token");
            }
        } else {
            response.getWriter().println("Authorization header missing");
        }
    }
}

六、安全性与最佳实践

6.1 HTTPS 的重要性

HTTPS(HTTP Secure) 是所有认证机制的基石:

防止中间人攻击(MITM)

  • HTTP 明文传输时,攻击者可以窃取 Cookie、Session ID 或 Token。
  • HTTPS 使用 SSL/TLS 加密数据传输,即使被拦截也无法解密。

数据完整性

  • 防止请求或响应在传输过程中被篡改。

身份验证

  • 确保客户端连接的是合法服务器(通过证书校验)。

实践建议:

  • 所有登录、敏感数据传输都必须使用 HTTPS。
  • 将 HTTP 自动重定向到 HTTPS。

6.2 如何防止 CSRF 与 XSS

CSRF(跨站请求伪造)
利用用户已登录状态,诱导用户在其他网站发起请求。

防御措施:

  • 对敏感操作使用 CSRF Token(每个请求附带随机令牌)。
  • 配置 SameSite=Strict Cookie,禁止第三方请求携带 Cookie。
  • 对操作使用 双重验证(验证码/确认对话框)

XSS(跨站脚本攻击)
攻击者将恶意脚本注入网页,被其他用户执行。

防御措施:

  • 对所有用户输入进行转义或过滤。

  • 输出 HTML 时使用安全模板或 escapeHtml()

  • 设置 HTTP 响应头:

    Content-Security-Policy: default-src 'self';
    
  • 禁用不必要的 HTML 标签。


6.3 Token 失效与撤销机制

Token(如 JWT)天然是无状态的,签发后服务器不再记录,失效机制需要额外设计:

短过期时间

  • Access Token 建议 15~30 分钟有效期。

Refresh Token

  • 长期有效(如7天),只能用于换取新的 Access Token。

黑名单

  • 服务端维护黑名单(撤销Token)。
  • 典型场景:用户主动退出、敏感操作(改密码)后立即失效。

版本号或撤销标识

  • Token中存储用户的token版本,数据库记录最新版本,旧token即失效。

示例:

  • token_version列=3,JWT payload也有version=3,若不一致则判定失效。

6.4 Cookie 安全属性(HttpOnly、Secure、SameSite)

1.HttpOnly

防止 XSS 窃取 Cookie

  • 设置后,JavaScript 无法通过 document.cookie读取。

示例:

Set-Cookie: sessionid=abc123; HttpOnly

2.Secure

仅在 HTTPS 下传输

  • 防止中间人攻击拦截。

示例:

Set-Cookie: sessionid=abc123; Secure

3.SameSite

防御 CSRF

  • 控制 Cookie 在跨站请求中是否发送。

可选值:

  • Strict:完全禁止跨站点发送 Cookie。
  • Lax:部分允许(GET请求等)。
  • None:允许跨站请求(需 Secure)。

示例:

Set-Cookie: sessionid=abc123; SameSite=Strict

当然!这是你最后一部分 总结 的简洁归纳:


七、总结

  1. 不同技术在现代 Web 的角色:

    • Cookie: 最基本的客户端状态存储,适合保存用户偏好、简单信息,也用于携带 Session ID 或 Token。
    • Session: 传统 Web 的核心状态管理方式,状态保存在服务器,依赖 Cookie 存 Session ID,安全性较高,适合单体应用。
    • Token(尤其是 JWT): 现代无状态认证的主流方案,自包含用户信息,支持分布式、前后端分离、移动端、跨域和微服务架构。
  2. 如何根据业务场景选择合适方案:

    • 中小型单体应用: 使用 Session + Cookie 实现登录态维护简单可靠。
    • 前后端分离或移动端: 使用 Token(JWT)+ LocalStorage 或 HttpOnly Cookie 更灵活。
    • 分布式/微服务: Token 无状态特性更适合横向扩展;若用 Session,需 Redis 等集中存储。
    • 高安全要求: 无论使用哪种,务必结合 HTTPS、短有效期和防护机制(HttpOnly、SameSite、签名验证)。
  3. 身份验证技术的发展趋势:

    • 纯 CookieSession + CookieToken(无状态),逐步向分布式、无状态、跨平台演进。
    • OAuth2.0 和 JWT 已成为现代 API 和 SSO 的事实标准。
    • 组合使用(如 HttpOnly Cookie 存 Token)越来越常见,用于兼顾安全与便利。

其他

【计算机网络】计算机网络基础知识——万字详解!!!

【计算机网络】IPv4地址 & 子网掩码 & MAC地址 & 网关 & DNS解析

【计算机网络】本机DNS服务器与解析地址查看

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐