读完这篇,你能做什么
能追踪登录写入和读取,并明确浏览器凭据与服务端用户态各自生命周期。
学习理由、准备要求与知识点
为什么学它
身份标识与服务端会话边界影响登录、退出与多实例状态一致性。
先读这些内容
知识点清单
- Cookie 作用域
- 会话 ID 与对象
- Session 属性读写
- DTO 类型闭环
- Cookie 与会话有效期
- invalidate 退出
- Secure
/HttpOnly/SameSite - 多实例会话与安全边界
运行前先确认以 Java 17、Jakarta Servlet 6.0、Tomcat 10.1.44 为边界。算法是离线会话 ID 轮换与闲置过期模型,测试 ID 由固定样例提供;生产会话令牌应使用容器或可靠机制生成,不以测试字符串作为认证凭据。
01先弄懂它是什么
网站怎样知道这次请求还是刚才登录的人?浏览器按域名、路径和传输规则保存并携带 Cookie;服务端用 Session 保存会话状态,两者常靠一个会话 ID 联系。这个 ID 不是完整的用户对象。浏览器删除 Cookie 不等于服务端会话已删除;服务端会话过期后,浏览器也可能继续发送旧 ID。
登录时先核对凭据,再把必要且尽量少的用户信息存进会话,并在身份变化时更新会话 ID。这样可以保留需要的状态,同时让旧标识失去当前关联,降低攻击者预先知道会话 ID 带来的风险。Cookie 不应保存改了名字的密码;确认用户已登录之后,还要继续检查他有没有权操作具体资源。
这篇继续用 loginUser 这个名字保存登录信息。登录时先把 User 中需要的字段整理成 UserDTO,去掉密码等秘密;后续请求取出时,也检查它是不是 UserDTO。属性叫 loginUser,不会把存入的 User 自动变成另一种 Java 类型。
只检查是否登录时调用 getSession(false):有会话就读取,没有就返回空,不顺手为匿名用户新建会话。会话不存在或类型不符时,应停止处理需要登录的请求。
文中这几个词,先认一认
- 会话标识
- 浏览器携带、让服务端找到对应会话的值,应难以猜测,且不能直接泄露内部身份秘密。
- 会话固定
- 攻击者让用户登录后仍使用攻击者已知的会话 ID;登录等身份变化时换 ID 有助于降低风险。
- 闲置过期
- 一段时间没有有效访问就过期,与从创建起固定计时的绝对过期不同。
- 会话失效
- 服务端不再通过这个会话认可登录状态;它和浏览器删除 Cookie 是两个不同操作。
02看看它怎么工作
请求到达后,Tomcat 等容器会拿请求带来的会话 ID,到当前应用管理的会话中查找,并检查找到的会话是否还有效。找到了,本次请求才能使用它;没找到有效会话,getSession(false) 就返回空,只有允许创建会话的调用才可能新建。浏览器带来名为 JSESSIONID 的 Cookie,只是提供了一个待检查的 ID。服务端还要找到有效会话,并确认里面有约定的登录信息,才能认可这次登录状态。
登录成功后先保证会话存在,再调用 changeSessionId,并写入统一 DTO。随后每个受保护请求读取会话、检查属性类型,再在具体业务层验证资源归属。退出登录应使服务端会话失效,并按应用约定处理客户端 Cookie。轮换和注销需要覆盖并发请求;正在执行的旧请求已经读取的上下文,不一定因注销瞬间自动被撤销。
Cookie 的三个设置管不同的事:Secure 约束发送时的连接安全,HttpOnly 限制脚本读取,SameSite 控制跨站时是否携带。即使脚本读不到 Cookie,恶意脚本仍可能借用户身份发送请求;SameSite 也不能在所有部署中单独防住跨站请求伪造(CSRF)。应用运行多个副本时,还要查清会话存在哪、下一次请求是否交给了能读到它的副本,以及把会话复制到其他副本失败后怎样处理。
正在加载流程图…
查看流程图源码(Mermaid)
flowchart TD
A["浏览器携带会话 ID"] --> B["容器查找有效会话"]
B --> C{"有效且有 UserDTO"}
C -->|"否"| D["未登录响应或进入登录"]
D --> E["凭据验证成功"]
E --> F["更新会话 ID 并写入 DTO"]
C -->|"是"| G["按当前身份检查资源授权"]
F --> G
G --> H["处理本次业务请求"]
I["退出或到期"] --> J["服务端会话失效"]按顺序看,每一步做了什么
- 区分 Cookie 中的标识与服务端会话内容。
- 登录成功后轮换标识,并统一 DTO 的写入与读取类型。
- 受保护请求只读取已有会话,再做资源级授权。
- 分别设计闲置、绝对寿命、注销和多实例状态策略。
03对照实现代码
Apache Tomcat 10.1.44 · org.apache.catalina.connector.Request#doGetSession(boolean)、getSession(boolean)
已核验真实方法和有效性判断。以下为原创会话查找局部模型,省略容器上下文、Cookie 生成、ID 管理、监听器和并发细节;它不是可用于生产认证的会话管理器。
import java.util.Map;
import java.util.function.Supplier;
class SourceModel {
record Session(String id,boolean valid) {}
static Session lookup(String requestedId,boolean create,
Map<String,Session> manager,Supplier<Session> factory) {
Session found=requestedId==null?null:manager.get(requestedId); // 标识只用于查找
if(found!=null && found.valid()) return found; // 必须确认服务端有效性
if(!create) return null; // 只读登录检查不能顺便创建新会话
Session fresh=factory.get(); // 真实 ID 生成由容器机制承担
manager.put(fresh.id(),fresh);
return fresh;
}
}Request 的无参 getSession 委托允许创建的重载,带 boolean 的重载再进入 doGetSession。真实方法先检查请求已关联的会话,再通过管理器查找请求中的标识,并确认会话有效。这使当前请求复用、外部 ID 查找与新建流程保持不同分支。
找到会话后会进入访问记录等容器逻辑,失效标识不会因为客户端继续发送就自动恢复有效。教学模型只保留一个 valid 布尔值,真实 StandardSession 还需要按闲置间隔、访问状态与配置处理有效性,不能把模型的简单字段等同于完整过期实现。
create=false 的分支让只读登录检查不必分配新状态。匿名爬取、健康检查和公共页面如果到处调用允许创建的方法,可能产生没有业务价值的会话。选择 API 应来自用途,而不是为了避免 null 就让容器默认创建。
会话属性由框架返回 Object,并不能保证里面一定是你想要的类型。写入和读取必须约定同一种对象,类型不符时明确处理。DTO 只是精简数据对象;用户权限改变后多久生效,还取决于缓存和会话更新方式,并不是换成 DTO 就自动解决。
代码先调用谁,再调用谁
HttpServletRequest.getSession(false)请求已有会话。Request.doGetSession查找当前或请求标识对应会话。Session.isValid检查有效性,成功后记录访问。- 仅在允许创建时进入新建及标识关联流程。
04动手写一个例子
会话轮换与滑动闲置过期
维护 ID 到 UserDTO 的会话映射,访问有效会话时延长固定闲置 TTL。轮换必须保留用户并删除旧 ID,目标 ID 冲突时原会话不变;到期时不可复活。时间由非递减的测试时钟提供,所有操作在一个进程内同步执行。
- 输入 1
创建 old,轮换为 fresh,再访问 old- 输出 1
旧 ID 不存在,新 ID 保留用户
- 输入 2
TTL=10,时刻 9 访问,再在 18 访问- 输出 2
仍有效,闲置期被延长
- 输入 3
TTL=10,首次在时刻 10 访问- 输出 3
已过期
- 输入 4
轮换目标已存在- 输出 4
拒绝且保留两个原会话
映射中的每项包含不可变 DTO 与截止时间。get 先检查过期,再计算新的截止时间;顺序相反会把已经过期的会话重新续活。以 now>=expiresAt 为失效边界,让恰好到期的行为可测试。
轮换先验证旧会话、目标不存在和新截止时间,再移除旧映射并写入新映射。检查与变更处于同一同步方法,避免两个线程同时占用目标 ID。失败前不能先删除旧 ID,否则一个冲突会意外让用户退出。
时钟非递减是题目约束,避免测试通过回拨时间延长旧会话。真实闲置计时和绝对时间需要按容器实现理解。本模型仅在访问时清除过期项,未访问的过期状态仍占内存,生产需要额外清理与容量限制。main 覆盖这些边界和错误路径。
为什么这样写是对的一次成功轮换后同一会话只有新 ID 可用;任何失败都不丢失仍有效的原映射;now 达到截止时间后该会话不能通过访问续活。
哈希表保存 s 个尚未清理条目,查找与轮换用常数次映射操作。惰性清理不能保证 s 等于当前有效会话数。
保存为 Main.java,运行 javac --release 17 -encoding UTF-8 Main.java,再运行 java Main。程序包含固定输入自测,断言失败时会抛出错误。
展开收起代码 · Main.java · 完整可运行程序
长行可在代码区域内左右滚动,按 Tab 可把焦点移入代码区。
import java.util.*;
public class Main {
record UserDTO(long id,String name) {}
record Entry(UserDTO user,long expires) {}
static class Sessions {
final Map<String,Entry> data=new HashMap<>();
final long ttl; long lastTime=0;
Sessions(long ttl) { if(ttl<=0) throw new IllegalArgumentException(); this.ttl=ttl; }
void time(long now) { if(now<lastTime) throw new IllegalArgumentException("时钟回退"); lastTime=now; }
long deadline(long now) { return Math.addExact(now,ttl); }
void id(String id) { if(id==null || id.isBlank()) throw new IllegalArgumentException(); }
Entry active(String id,long now) {
Entry e=data.get(id);
if(e!=null && now>=e.expires()) { data.remove(id); return null; } // 到期先失效
return e;
}
synchronized void create(String id,UserDTO user,long now) {
id(id); time(now); Objects.requireNonNull(user);
if(active(id,now)!=null) throw new IllegalArgumentException("ID 冲突");
data.put(id,new Entry(user,deadline(now)));
}
synchronized UserDTO get(String id,long now) {
id(id); time(now); Entry old=active(id,now);
if(old==null) return null;
data.put(id,new Entry(old.user(),deadline(now))); // 仅有效访问延长闲置期
return old.user();
}
synchronized boolean rotate(String oldId,String newId,long now) {
id(oldId); id(newId); time(now);
if(oldId.equals(newId)) throw new IllegalArgumentException("必须更换标识");
Entry old=active(oldId,now); if(old==null) return false;
if(active(newId,now)!=null) throw new IllegalArgumentException("目标冲突");
Entry fresh=new Entry(old.user(),deadline(now)); // 所有可能失败的检查在删除前
data.remove(oldId); data.put(newId,fresh);
return true;
}
}
static void check(boolean ok) { if(!ok) throw new AssertionError(); }
public static void main(String[] args) {
UserDTO user=new UserDTO(7,"Alice");
Sessions rotate=new Sessions(10); rotate.create("old",user,0);
check(rotate.rotate("old","fresh",1)); check(rotate.get("old",1)==null);
check(rotate.get("fresh",1).equals(user));
Sessions idle=new Sessions(10); idle.create("x",user,0);
check(idle.get("x",9)!=null); check(idle.get("x",18)!=null);
Sessions expired=new Sessions(10); expired.create("x",user,0); check(expired.get("x",10)==null);
check(!expired.rotate("x","y",11));
Sessions conflict=new Sessions(10); conflict.create("a",user,0); conflict.create("b",user,0);
try { conflict.rotate("a","b",1); throw new AssertionError("应拒绝冲突"); }
catch(IllegalArgumentException expected) { /* 原会话应仍保留 */ }
check(conflict.get("a",1)!=null && conflict.get("b",1)!=null);
try { idle.get("x",17); throw new AssertionError("应拒绝时间回退"); }
catch(IllegalArgumentException expected) { /* 非递减时钟约束 */ }
System.out.println("PASS 10 session assertions");
}
}这些特殊情况也要试一试
- 恰好到期时拒绝,不把首次超期访问当成正常续期。
- 目标 ID 冲突不能使原会话先被删除。
- 测试固定 ID 仅便于核对,生产必须生成不可预测标识并保护传输。
进阶选读 · 生产拓展:多实例注销与权限变化传播
场景多个节点缓存会话或用户权限,注销后其他节点仍可能持有旧状态。
做法定义共同会话存储或版本化撤销机制,明确缓存期限、失效传播与已经在途请求的处理边界。高风险动作可再次检查权威状态,普通读操作按可接受窗口处理。
复杂度本地读取可近似常数时间,但跨节点传播和存储访问包含网络及一致性成本;不能用单机映射复杂度替代。
好处与代价更及时的撤销需要更多共享查询或消息传播,降低缓存收益;允许短窗口则应符合实际业务风险。
05项目里怎么用
登录会话只保存业务确实需要的信息,不要为了读取方便,就把密码、密钥或含有敏感字段的完整用户对象放进 UserDTO。若会话要保存成字节或交给其他应用副本读取,就要约定对象类型和保存格式,并检查新版应用能否读懂旧版写下的数据。读取失败时应明确处理,不能悄悄把不认识的数据当成另一个用户的身份。
Cookie 属性依据传输与跨站场景配置,并配合实际 CSRF 与 XSS 防护。轮换 ID 与注销需要在响应尚可设置相关信息时执行;不能把客户端界面显示退出当作服务端状态已失效的证据。
会话规模和寿命影响容量。滑动闲置期方便活跃用户,但可能让会话长时间存在,某些场景还需绝对寿命与敏感操作重新认证。定期观察有效数、创建率、失效率和存储错误,区分正常过期与依赖故障导致的集中掉线。
容易踩的坑
- 存入实体后直接强转为 DTO,类型契约并未成立。
- 匿名读取总是创建会话,增加无意义状态。
- 先续期再检查是否过期,使失效状态被意外复活。
- 仅删除 Cookie 就声称服务端会话已注销。
几种做法,怎么选
表格有四列,窄屏可左右滑动查看代价和适用条件。
| 方案 | 优势 | 代价与限制 | 适用条件 |
|---|---|---|---|
| 节点本地会话 | 读取简单且低延迟。 | 扩容、故障和路由切换影响会话可见性。 | 明确单节点或有可接受路由约束的系统。 |
| 共享会话存储 | 多个应用实例能读取同一状态。 | 引入网络依赖、序列化和存储可用性成本。 | 多实例且需要集中注销的服务。 |
| 短期令牌加撤销机制 | 可减少部分共享读取。 | 撤销与权限实时变化需要额外设计。 | 能明确令牌寿命与风险窗口的系统。 |
06检验是否读懂了
先自己想、自己写,再展开答案核对。场景题是假设的练习情境;遇到还没学过的内容,可以做完基础后再回来。
先用自己的话解释
01Cookie 有会话 ID 就说明已经登录吗?
展开参考答案与推导
不说明。服务端还要找到有效会话并读取符合约定的登录属性,之后仍需进行资源授权。旧 ID、随机 ID 和没有登录属性的会话都不能直接代表认证成功。
02为什么 changeSessionId 与清空用户属性不是同一操作?
展开参考答案与推导
轮换改变关联标识,通常希望保留必要会话内容;清空属性改变业务状态。登录后轮换与退出失效分别服务不同目标。
03HttpOnly 能自动解决全部 CSRF 吗?
展开参考答案与推导
不能。它主要限制脚本读取 Cookie,浏览器仍可能自动携带凭据发送请求。跨站请求风险需要按实际场景另行防护。
换个情况,还会处理吗
01部署第二个实例后用户偶尔掉线,如何排查?
展开参考答案与推导
检查会话存储位置、请求路由、共享配置和序列化兼容性,核对同一 ID 在不同节点能否找到同一状态;不能直接断言是浏览器丢 Cookie。
02UserDTO 强转异常发生在请求读取处,应改哪里?
展开参考答案与推导
检查登录写入、存储序列化、读取及后续使用整个链路。统一写入 DTO 或明确转换,不只在报错行换一个强转类型掩盖契约不一致。
03注销后一个正在处理的请求仍完成,是否一定是漏洞?
展开参考答案与推导
需看定义的撤销边界。请求可能已在注销前取得身份并进入业务;高风险操作可要求提交前再验证权威状态,不能假定失效自动中断所有在途工作。
写代码试一试
01验证会话不会在到期时复活。
- 输入 / 条件
TTL=10,创建时刻0,访问时刻10- 输出 / 预期
null,映射失效
提示 先检查过期再计算新截止时间。
展开参考答案与推导
测试恰好边界,确认 get 不返回用户也不写入新的有效期;再访问仍为空。
02轮换目标冲突时如何保持原状态?
- 输入 / 条件
a 和 b 都有效,将 a 轮换为 b- 输出 / 预期
拒绝且 a、b 仍有效
提示 所有校验应在删除前完成。
展开参考答案与推导
先验证目标与计算新记录,再在同一原子边界移除旧项并写新项。异常路径不得留下半完成迁移。
03为惰性过期模型增加容量控制。
- 输入 / 条件
大量过期但不再访问的 ID- 输出 / 预期
能够回收过期条目并限制总量
提示 不能只依赖 get 清理。
展开参考答案与推导
增加受预算限制的周期扫描或到期索引,并为刷新版本处理过时到期记录;同时决定容量满时拒绝、淘汰还是扩容的业务语义。
读完之后,独立完成
课后作业:自己做一遍
本篇共 10 道作业,先完成 8 道基础与综合任务,再按前置知识挑战进阶题。其中包含 1 道思维拓展,试着换个条件,看看原来的办法是否还管用。不附参考答案。
能追踪登录写入和读取,并明确浏览器凭据与服务端用户态各自生命周期。
本篇重点加练
重点核验 Cookie 发送边界与退出语义,让用户身份生命周期具备可观察证据。
- Cookie 作用域
- invalidate 退出
重点内容会多练几次。先说清楚怎么做,再试试空数据、出错或条件改变时会发生什么。
先自己做,卡住时记下问题,再回来查。问答题写清“为什么、什么时候适用”;动手题留下输入、输出和运行步骤,方便下次重做。
基础必做
- 作业 01原理问答
一个浏览器访问同域
/app与/admin,分别收到不同 Path 的会话 Cookie。画出浏览器保存值、请求携带值和服务器会话对象的关系,说明仅观察 Cookie 能证明哪些事实。- 提交内容
- 作用域图和证据边界表。
- 怎样算完成
- 区分标识和服务器状态
- 明确 Path 与域的影响
练到的知识点Cookie 作用域 · 会话 ID 与对象
- 作业 02代码推演
登录步骤向 Session 写入
LoginUserDTO,资料接口和退出接口分别读取与移除。规定属性名、类型以及是否允许为空,再构造“存入实体而读取 DTO”的独立失败样例。- 提交内容
- 属性约定、调用链与最小类型实验。
- 怎样算完成
- 覆盖所有写入与读取位置
- 不以强制转换代替对存取类型的明确约定
练到的知识点Session 属性读写 · DTO 类型闭环
综合应用
- 作业 03排错实验
本地设置较短会话有效期并用两组客户端 Cookie 容器测试:主动退出、仅清除客户端 Cookie、等待闲置过期。用可控时间或明确等待上限记录服务端状态与后续访问。
- 提交内容
- 测试脚本、自写判定条件、时间线和运行证据。
- 怎样算完成
- 客户端与服务器生命周期分别观察
- 过期边界有重复检查
练到的知识点Cookie 与会话有效期 · invalidate 退出
- 作业 04设计评审
应用从单实例扩为两个实例,并嵌入第三方页面。评审 Secure、
HttpOnly、SameSite、会话共享和注销传播需求,注明部署条件和浏览器上下文。- 提交内容
- 配置评审表、威胁场景和多实例验收计划。
- 怎样算完成
- 不把单个属性当作全部安全措施
- 会话策略与真实访问路径相符
练到的知识点Secure/HttpOnly/SameSite · 多实例会话与安全边界
- 作业 05编程实践
实现内存会话练习模型,使用注入时钟,提供创建、读取、退出操作;会话只保存 DTO,用户标识非空,明确过期时刻与重复退出政策。
- 提交内容
- Java 实现、自写边界测试及运行证据。
- 怎样算完成
- 时间测试无需长时间等待
- 无效会话不泄露用户信息
- 退出后行为符合书面合同
练到的知识点会话 ID 与对象 · Session 属性读写 · Cookie 与会话有效期 · invalidate 退出
- 作业 06排错实验
使用两个独立本地 Cookie 容器登录同一用户,仅退出其中一个会话;观察两个容器之后的访问,先声明系统是单会话退出还是全设备退出。
- 提交内容
- 本地请求脚本、自写两容器检查和运行记录。
- 怎样算完成
- 会话标识与用户身份分别追踪
- 退出范围与合同一致
练到的知识点Cookie 作用域 · invalidate 退出
- 作业 07设计评审
同一主机的两个本地应用监听不同端口,开发者计划仅靠端口区分各自登录 Cookie。查阅本篇作用域定义,建立请求发送矩阵并审查该隔离方案。
- 提交内容
- 端点样本、作用域矩阵和隔离合同。
- 怎样算完成
- 判断依据是 Cookie 的实际作用域
- 方案有能区分两应用的验收
练到的知识点Cookie 作用域
- 作业 08面试场景
用户修改密码后要求“退出所有旧登录”,但当前手机会话可以继续使用。列出需要由产品定义的身份、时间点和在途请求行为,并给出验收矩阵。
- 提交内容
- 失效范围合同与时间线。
- 怎样算完成
- 会话与账号级状态区分
- 无模糊的立即生效承诺
练到的知识点invalidate 退出
进阶挑战 · 掌握前置后选做
- 作业 09面试场景
完成认证安全基础后:用户退出时已有下载请求在执行,另一实例仍保存旧身份缓存。定义“退出生效”的可承诺范围,提出业务敏感度不同的两档策略。
- 提交内容
- 事件时间线、承诺边界与故障验证方案。
- 怎样算完成
- 区分新请求与在途请求
- 考虑共享状态传播延迟
练到的知识点invalidate 退出 · Secure/HttpOnly/SameSite · 多实例会话与安全边界
思维拓展
先写出你的猜想,再用例子或实验验证。答案可以不同,但要说清依据、适用情况,以及还有什么没弄懂。
- 作业 10思维拓展 · 条件变化
登录页移动到单独子域,原退出按钮仍在学习站点。分析 Cookie 作用域与会话失效接口的新约束,提出至少两个可验证的部署方案。
- 提交内容
- 子域请求图与退出验收案例。
- 怎样算完成
- 浏览器发送条件与服务端状态都被检查
- 方案不依赖未声明的共享范围
练到的知识点Cookie 作用域 · invalidate 退出
完成后,再检验一次
给每题标记“独立完成 / 查阅后完成 / 尚未完成”。查过资料或没做出的题,记下卡在哪里。隔一天换一组输入再试,能解释结果和出错原因,再把这个知识点标为掌握。
继续阅读
先看能帮你完成当前练习的部分。例子跑通后,再读更深入的解释。
- 必读参考文档英文
Jakarta Servlet 6.0:HttpSession (新标签页)↗
阅读会话属性、访问时间、invalidate 和会话范围;与 Jakarta Servlet 6.0 教学边界对应。
- 实践规范英文
Jakarta Servlet 6.0 规范 (新标签页)↗
阅读生命周期、并发请求、分派类型和异步处理;用于 Jakarta 命名空间课程。
- 深入规范英文
IETF RFC 9110:HTTP 语义 (新标签页)↗
阅读方法、状态码、表示元数据和幂等性;结合真实请求记录理解。
- 实践实践指南中文
Cookie:是谁保存,又在什么时候发回去 (新标签页)↗
画三步:服务端返回
Set-Cookie、浏览器保存、后续请求带上 Cookie。再标注过期时间、Secure、HttpOnly和SameSite各管什么。关浏览器不一定立即清掉会话 Cookie;删浏览器记录也不等于服务器主动注销。 正文免费公开,不需账号。用文中的虚构值学习即可,不要复制真实登录凭据。浏览器的会话恢复和隐私策略会影响观察结果。 - 实践实践指南中文
跨域请求:允许浏览器读响应,不等于允许访问业务 (新标签页)↗
为虚构前端与接口写出协议、域名和端口,判断是否同源。然后对照一次带凭据请求:允许来源不能用星号替代具体来源。最后另列服务端身份与权限检查;CORS 不能代替它们,也不是完整的防跨站请求伪造方案。 中文文档免费公开。只读示例即可,不需要改线上跨域配置。浏览器的第三方 Cookie 策略和
SameSite限制仍独立生效。 - 实践实践指南英文
登录会话:换号、超时与退出各做什么 (新标签页)↗
写一条时间线:登录成功换号、长时间没活动、达到总时限、主动退出。每种情况分别说明旧编号是否还能访问服务端;浏览器清除 Cookie 与服务器让会话失效要分开检查。 官方速查表免费公开。建议较密,先读指定三部分;不要直接把示例时长当成所有产品通用值。练习只用虚构编号,不记录真实会话 ID。
- 深入参考文档英文
Jakarta Servlet 6.0 Specification (新标签页)↗
阅读 Session、会话追踪与生命周期契约。
- 深入参考文档英文
Tomcat 10.1.44 Request (新标签页)↗
定位
doGetSession的查找、有效性与创建分支。 - 深入参考文档英文
Tomcat 10.1.44 StandardSession (新标签页)↗
核对访问时间和闲置过期处理,区分完整容器行为与算法模型。