Tomcat中实现Session小结
发布时间 - 2026-01-10 22:20:48 点击率:次什么是Session

对Tomcat而言,Session是一块在服务器开辟的内存空间,其存储结构为ConcurrentHashMap;
Session的目的
Http协议是一种无状态协议,即每次服务端接收到客户端的请求时,都是一个全新的请求,服务器并不知道客户端的历史请求记录;
Session的主要目的就是为了弥补Http的无状态特性。简单的说,就是服务器可以利用session存储客户端在同一个会话期间的一些操作记录;
实现机制
先看两个问题,如下:
1、服务器如何判断客户端发送过来的请求是属于同一个会话?
答:用Session id区分,Session id相同的即认为是同一个会话,在Tomcat中Session id用JSESSIONID表示;
2、服务器、客户端如何获取Session id?Session id在其之间是如何传输的呢?
答:服务器第一次接收到请求时,开辟了一块Session空间(创建了Session对象),同时生成一个Session id,并通过响应头的Set-Cookie:“JSESSIONID=XXXXXXX”命令,向客户端发送要求设置cookie的响应;
客户端收到响应后,在本机客户端设置了一个JSESSIONID=XXXXXXX的cookie信息,该cookie的过期时间为浏览器会话结束;
接下来客户端每次向同一个网站发送请求时,请求头都会带上该cookie信息(包含Session id);
然后,服务器通过读取请求头中的Cookie信息,获取名称为JSESSIONID的值,得到此次请求的Session id;
ps:服务器只会在客户端第一次请求响应的时候,在响应头上添加Set-Cookie:“JSESSIONID=XXXXXXX”信息,接下来在同一个会话的第二第三次响应头里,是不会添加Set-Cookie:“JSESSIONID=XXXXXXX”信息的;
而客户端是会在每次请求头的cookie中带上JSESSIONID信息;
举个例子:
以chrome浏览器为例,访问一个基于tomcat服务器的网站的时候,
浏览器第一次访问服务器,服务器会在响应头添加Set-Cookie:“JSESSIONID=XXXXXXX”信息,要求客户端设置cookie,如下图:
同时我们也可以在浏览器中找到其存储的sessionid信息,如下图
接下来,浏览器第二次、第三次...访问服务器,观察其请求头的cookie信息,可以看到JSESSIONID信息存储在cookie里,发送给服务器;且响应头里没有Set-Cookie信息,如下图:
只要浏览器未关闭,在访问同一个站点的时候,其请求头Cookie中的JSESSIONID都是同一个值,被服务器认为是同一个会话。
再举个简单的例子加深印象,新建个Web工程,并写一个Servlet,在doGet中添加如下代码,主要做如下工作
首先,从session中获取key为count的值,累加,存入session,并打印;
然后,每次从请求中获取打印cookie信息,从响应中获取打印Header的Set-Cookie信息:
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if(request.getSession().getAttribute("count") == null){
request.getSession().setAttribute("count", 0);
response.getWriter().write(0+"");
}else{
int a = Integer.parseInt(request.getSession().getAttribute("count").toString());
request.getSession().setAttribute("count", ++a);
response.getWriter().write(a+"");
}
Cookie[] cookies = request.getCookies();
StringBuffer sb = new StringBuffer();
if(cookies!=null){
for(Cookie cookie : cookies){
sb.append(cookie.getName()+":"+cookie.getValue()+",");
}
sb.deleteCharAt(sb.length()-1);
}
System.out.println("[第"+(++index)+"次访问]from client request, cookies:" + sb);
System.out.println("[第"+(index)+"次访问]from server response, header-Set-Cookie:" + response.getHeader("Set-Cookie"));;
}
部署到tomcat后,连续访问该servlet,观察控制台输出,如下,客户端第一次访问服务器的时候,在服务端的响应头里添加了JSESSIONID信息,且接下来客户端的每次访问都会带上该JSESSIONID:
其实这里有一个问题,session劫持
只要用户知道JSESSIONID,该用户就可以获取到JSESSIONID对应的session内容,还是以上面这个例子为例,
我先用IE浏览器访问该站点,比如连续访问了5次,此时,session中的count值为:
查看该会话的Session id,为6A541281A79B24BC290ED3270CF15E32
接下来打开chrome控制台,将IE浏览器获取过来的JSESSIONID信息(“6A541281A79B24BC290ED3270CF15E32”)写入到cookie中,如下
接着删除其中的一个,只留下JSESSIONID为“6A541281A79B24BC290ED3270CF15E32”的cookie;
刷新页面,发现我们从session获取的count值已经变成6了,说明此次chrome浏览器的请求劫持了IE浏览器会话中的session,
Tomcat中的session实现
Tomcat中一个会话对应一个session,其实现类是StandardSession,查看源码,可以找到一个attributes成员属性,即存储session的数据结构,为ConcurrentHashMap,支持高并发的HashMap实现;
/** * The collection of user data attributes associated with this Session. */ protected Map<String, Object> attributes = new ConcurrentHashMap<String, Object>();
那么,tomcat中多个会话对应的session是由谁来维护的呢?ManagerBase类,查看其代码,可以发现其有一个sessions成员属性,存储着各个会话的session信息:
/** * The set of currently active Sessions for this Manager, keyed by * session identifier. */ protected Map<String, Session> sessions = new ConcurrentHashMap<String, Session>();
接下来,看一下几个重要的方法,
服务器查找Session对象的方法
客户端每次的请求,tomcat都会在HashMap中查找对应的key为JSESSIONID的Session对象是否存在,可以查看Request的doGetSession方法源码,如下源码:
protected Session doGetSession(boolean create) {
// There cannot be a session if no context has been assigned yet
Context context = getContext();
if (context == null) {
return (null);
}
// Return the current session if it exists and is valid
if ((session != null) && !session.isValid()) {
session = null;
}
if (session != null) {
return (session);
}
// Return the requested session if it exists and is valid
Manager manager = context.getManager();
if (manager == null) {
return null; // Sessions are not supported
}
if (requestedSessionId != null) {
try {
session = manager.findSession(requestedSessionId);
} catch (IOException e) {
session = null;
}
if ((session != null) && !session.isValid()) {
session = null;
}
if (session != null) {
session.access();
return (session);
}
}
// Create a new session if requested and the response is not committed
if (!create) {
return (null);
}
if ((context != null) && (response != null) &&
context.getServletContext().getEffectiveSessionTrackingModes().
contains(SessionTrackingMode.COOKIE) &&
response.getResponse().isCommitted()) {
throw new IllegalStateException
(sm.getString("coyoteRequest.sessionCreateCommitted"));
}
// Re-use session IDs provided by the client in very limited
// circumstances.
String sessionId = getRequestedSessionId();
if (requestedSessionSSL) {
// If the session ID has been obtained from the SSL handshake then
// use it.
} else if (("/".equals(context.getSessionCookiePath())
&& isRequestedSessionIdFromCookie())) {
/* This is the common(ish) use case: using the same session ID with
* multiple web applications on the same host. Typically this is
* used by Portlet implementations. It only works if sessions are
* tracked via cookies. The cookie must have a path of "/" else it
* won't be provided to for requests to all web applications.
*
* Any session ID provided by the client should be for a session
* that already exists somewhere on the host. Check if the context
* is configured for this to be confirmed.
*/
if (context.getValidateClientProvidedNewSessionId()) {
boolean found = false;
for (Container container : getHost().findChildren()) {
Manager m = ((Context) container).getManager();
if (m != null) {
try {
if (m.findSession(sessionId) != null) {
found = true;
break;
}
} catch (IOException e) {
// Ignore. Problems with this manager will be
// handled elsewhere.
}
}
}
if (!found) {
sessionId = null;
}
sessionId = getRequestedSessionId();
}
} else {
sessionId = null;
}
session = manager.createSession(sessionId);
// Creating a new session cookie based on that session
if ((session != null) && (getContext() != null)
&& getContext().getServletContext().
getEffectiveSessionTrackingModes().contains(
SessionTrackingMode.COOKIE)) {
Cookie cookie =
ApplicationSessionCookieConfig.createSessionCookie(
context, session.getIdInternal(), isSecure());
response.addSessionCookieInternal(cookie);
}
if (session == null) {
return null;
}
session.access();
return session;
}
先看doGetSession方法中的如下代码,这个一般是第一次访问的情况,即创建session对象,session的创建是调用了ManagerBase的createSession方法来实现的; 另外,注意response.addSessionCookieInternal方法,该方法的功能就是上面提到的往响应头写入“Set-Cookie”信息;最后,还要调用session.access方法记录下该session的最后访问时间,因为session是可以设置过期时间的;
session = manager.createSession(sessionId);
// Creating a new session cookie based on that session
if ((session != null) && (getContext() != null)
&& getContext().getServletContext().
getEffectiveSessionTrackingModes().contains(
SessionTrackingMode.COOKIE)) {
Cookie cookie =
ApplicationSessionCookieConfig.createSessionCookie(
context, session.getIdInternal(), isSecure());
response.addSessionCookieInternal(cookie);
}
if (session == null) {
return null;
}
session.access();
return session;
再看doGetSession方法中的如下代码,这个一般是第二次以后访问的情况,通过ManagerBase的findSession方法查找session,其实就是利用map的key从ConcurrentHashMap中拿取对应的value,这里的key即requestedSessionId,也即JSESSIONID,同时还要调用session.access方法,记录下该session的最后访问时间;
if (requestedSessionId != null) {
try {
session = manager.findSession(requestedSessionId);
} catch (IOException e) {
session = null;
}
if ((session != null) && !session.isValid()) {
session = null;
}
if (session != null) {
session.access();
return (session);
}
}
在session对象中查找和设置key-value的方法
这个我们一般调用getAttribute/setAttribute方法:
getAttribute方法很简单,就是根据key从map中获取value;
setAttribute方法稍微复杂点,除了设置key-value外,如果添加了一些事件监听(HttpSessionAttributeListener)的话,还要通知执行,如beforeSessionAttributeReplaced, afterSessionAttributeReplaced, beforeSessionAttributeAdded、 afterSessionAttributeAdded。。。
session存在的问题
- 安全性,session劫持,这个前面已经举过例子了;
- 增加服务器压力,因为session是直接存储在服务器的内存中的;
- 如果存在多台服务器的话,还存在session同步问题,当然如果只有一台tomcat服务器的话,也就没有session同步的事情了,然而现在一般的应用都会用到多台tomcat服务器,通过负载均衡,同一个会话有可能会被分配到不同的tomcat服务器,因此很可能出现session不一致问题;解决session同步问题,实际上主要是保证能够抽离出一块共享空间存放session信息,且这块空间不同的tomcat服务器都可以访问到;一般这块共享的空间可以是数据库,或者某台服务器的内存空间,甚至硬盘空间,或者客户端的cookie也是可以的;
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# tomcat
# session
# 实现
# Tomcat实现session共享(session 会话复制)
# Tomcat集群和Session复制应用介绍
# 深入浅析TomCat Session管理分析
# nginx+tomcat实现负载均衡
# 使用redis session共享
# Tomcat中session的管理机制
# Java中tomcat memecached session 共享同步问题的解决办法
# 浅谈Tomcat Session管理分析
# Nginx+Tomcat关于Session的管理的实现
# Tomcat如何监控并删除超时Session详解
# Tomcat中的Session与Cookie深入讲解
# 客户端
# 会在
# 都是
# 如下图
# 为例
# 这块
# 先看
# 多台
# 有一个
# 几个
# 是一种
# 也就
# 多个
# 的说
# 是由
# 一台
# 数据结构
# 头上
# 在同一个
# 很简单
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Windows11怎样设置电源计划_Windows11电源计划调整攻略【指南】
香港服务器部署网站为何提示未备案?
详解免费开源的.NET多类型文件解压缩组件SharpZipLib(.NET组件介绍之七)
焦点电影公司作品,电影焦点结局是什么?
微博html5版本怎么弄发语音微博_语音录制入口及时长限制操作【教程】
如何用y主机助手快速搭建网站?
Laravel如何处理异常和错误?(Handler示例)
邀请函制作网站有哪些,有没有做年会邀请函的网站啊?在线制作,模板很多的那种?
宙斯浏览器文件分类查看教程 快速筛选视频文档与图片方法
企业在线网站设计制作流程,想建设一个属于自己的企业网站,该如何去做?
LinuxCD持续部署教程_自动发布与回滚机制
laravel怎么用DB facade执行原生SQL查询_laravel DB facade原生SQL执行方法
昵图网官方站入口 昵图网素材图库官网入口
Win11摄像头无法使用怎么办_Win11相机隐私权限开启教程【详解】
Laravel如何从数据库删除数据_Laravel destroy和delete方法区别
Laravel如何使用Contracts(契约)进行编程_Laravel契约接口与依赖反转
如何在腾讯云服务器快速搭建个人网站?
JavaScript中的标签模板是什么_它如何扩展字符串功能
微信小程序 require机制详解及实例代码
如何快速搭建安全的FTP站点?
laravel怎么在请求结束后执行任务(Terminable Middleware)_laravel Terminable Middleware请求结束任务执行方法
Laravel广播系统如何实现实时通信_Laravel Reverb与WebSockets实战教程
Laravel路由Route怎么设置_Laravel基础路由定义与参数传递规则【详解】
Laravel怎么判断请求类型_Laravel Request isMethod用法
Laravel如何处理文件上传_Laravel Storage门面实现文件存储与管理
JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)
图片制作网站免费软件,有没有免费的网站或软件可以将图片批量转为A4大小的pdf?
Laravel如何实现登录错误次数限制_Laravel自带LoginThrottles限流配置【方法】
网站建设整体流程解析,建站其实很容易!
详解jQuery中的事件
Laravel Artisan命令怎么自定义_创建自己的Laravel命令行工具完全指南
什么是JavaScript解构赋值_解构赋值有哪些实用技巧
佛山网站制作系统,佛山企业变更地址网上办理步骤?
高端企业智能建站程序:SEO优化与响应式模板定制开发
如何用VPS主机快速搭建个人网站?
品牌网站制作公司有哪些,买正品品牌一般去哪个网站买?
Laravel如何将应用部署到生产服务器_Laravel生产环境部署流程
Win11怎么查看显卡温度 Win11任务管理器查看GPU温度【技巧】
如何在阿里云购买域名并搭建网站?
Laravel如何使用Laravel Vite编译前端_Laravel10以上版本前端静态资源管理【教程】
郑州企业网站制作公司,郑州招聘网站有哪些?
Laravel怎么使用Intervention Image库处理图片上传和缩放
Win11怎么关闭资讯和兴趣_Windows11任务栏设置隐藏小组件
logo在线制作免费网站在线制作好吗,DW网页制作时,如何在网页标题前加上logo?
如何在HTML表单中获取用户输入并用JavaScript动态控制复利计算循环
如何在阿里云香港服务器快速搭建网站?
Laravel如何使用API Resources格式化JSON响应_Laravel数据资源封装与格式化输出
Laravel中Service Container是做什么的_Laravel服务容器与依赖注入核心概念解析
网站优化排名时,需要考虑哪些问题呢?
nginx修改上传文件大小限制的方法

