什么是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 yetContext context = getContext();if (context == null) {return (null);}// Return the current session if it exists and is validif ((session != null) && !session.isValid()) {session = null;}if (session != null) {return (session);}// Return the requested session if it exists and is validManager 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 committedif (!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 sessionif ((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;}

View Code

先看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 sessionif ((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小结相关推荐

  1. tomcat中的session管理

    Session的管理 当一个sesson开始时,Servlet容器会创建一个HttpSession对象,在某些情况下把这些Httpsession对象从内存中转移到文件系统中或数据库中,需要访问的时候在 ...

  2. Tomcat 集群中 实现session 共享的三种方法

    前两种均需要使用 memcached 或 redis 存储 session ,最后一种使用 terracotta 服务器共享.  建议使用 redis ,不仅仅因为它可以将缓存的内容持久化,还因为它支 ...

  3. MemCache在tomcat中的负载均衡和session交叉存放

    一.安装jdk 1.下载软件,并解压到指定目录/usr/local/ [root@server1 ~]# tar zxf jdk-7u79-linux-x64.tar.gz -C /usr/local ...

  4. 多台tomcat服务的session共享 memcached与redis

    由于tomcat的并发数瓶颈问题,可以说使用tomcat的web应用,几乎都存在session不同步问题. 借鉴网上的资料,我也找时间实验一把. 文中涉及的软件下载和安装,一一略过,想必大家也没必要看 ...

  5. nginx反向代理(proxy_pass)tomcat的过程中,session失效的问题解决

    nginx反向代理(proxy_pass)tomcat的过程中,session失效的问题解决 参考文章: (1)nginx反向代理(proxy_pass)tomcat的过程中,session失效的问题 ...

  6. Tomcat中两个不同项目共享Session

    本文研究的是同一个Tomcat目录下的两个不同的应用共享同一个session.由于每个WEB应用程序都有一个唯一的一个ServletContext实例对象,本应用中的所有的servlet共享此Serv ...

  7. 使用Tomcat-redis-session-manager来实现Tomcat集群部署中的Session共享

    2019独角兽企业重金招聘Python工程师标准>>> 一.工作中因为要使用到Tomcat集群部署,此时就涉及到了Session共享问题,主要有三种解决方案: 1.使用数据库来存储S ...

  8. 使用 CAS 在 Tomcat 中实现单点登录

    CAS 介绍 CAS 是 Yale 大学发起的一个开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法,CAS 在 2004 年 12 月正式成为 JA-SIG 的一个项目.CAS 具有以下特 ...

  9. Tomcat5集群中的SESSION复制详解

    Tomcat 5服务器为集群和SESSION复制提供了集成的支持.本系列的第一篇文章将为大家提供SESSION持久性以及TOMCAT集群中SESSION复制的 内在工作机制一个概要认识.我将会讨论SE ...

最新文章

  1. 网页编辑html中怎么写搜索代码,如何在html页面中实现查找功能
  2. android studio 抓log,Android studio保存logcat日志到本地的操作
  3. Centos6.8下搭建SVN服务器
  4. 联想小新air14笔记本黑屏_联想小新air14锐龙版测评,谈谈它的好和坏
  5. 小米11 Pro概念图曝光:曲面挖孔屏+后置五摄相机模组
  6. 封装JDBC—非框架开发必备的封装类
  7. Python_Tika
  8. python实现第三方验证码获取_Python 原生爬虫教程
  9. js判断IE内核,IE浏览器版本
  10. 移动端 html 模板,移动端web页面代码模板
  11. 东芝移动硬盘无法弹出密保,无法执行该软件,因为未启动需要的服务程序
  12. openwrt开机启动设置
  13. php微信零钱明细,微信钱包的收支记录明细在哪里查看,看完就明白了
  14. CSCD(2015-2016年)来源期刊目录中国科学引文数据库
  15. swfobject.js 的用法
  16. Geek 青年说北京沙龙分享
  17. fprintf与fscanf
  18. ThreadLocal 简单了解
  19. [luogu] P2345 MooFest G 树状数组
  20. C# 淘宝商品微信返利助手开发-(八)微信号对接

热门文章

  1. java 为什么有匿名类_全面了解Java中的内部类和匿名类
  2. android 获取网卡mac_在Android机顶盒上 怎么样获取有线网卡MAC地址?
  3. 2018全国计算机考试报名入口,北京2018年3月全国计算机等级考试报名入口
  4. Oracle10g补丁怎么安装,在CentOS6.4上安装oracle10g需要的补丁
  5. mysql 行锁 超时_技术分享 | MySQL 行锁超时排查方法优化
  6. 将多个图片转换成PDF文件-img2pdf
  7. 2020年智能车竞赛由预赛赛道拼接成决赛赛道的方案
  8. 今晚有直播 | 全国大学生智能汽车竞赛人工智能创意赛来啦!
  9. python多线程没用_python的多线程到底有没有用?
  10. php对象和数组的效率,PHP对象数组和一般数组区别