什么是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也是可以的;

(原文地址:http://www.cnblogs.com/chenpi/p/5434537.html)

[转]Tomcat中的Session小结的更多相关文章

  1. Tomcat中的Session小结

    什么是Session 对Tomcat而言,Session是一块在服务器开辟的内存空间,其存储结构为ConcurrentHashMap: Session的目的 Http协议是一种无状态协议,即每次服务端 ...

  2. 【Web】Tomcat中利用Session识别用户的基本原理

    HTTP无状态的特性与Session.Cookie的存在 HTTP有一个特性:无状态的,就是前后两个HTTP事务它们并不知道对方的信息. 而为了维护会话信息或用户信息,一般可用Cookie或Sessi ...

  3. How Tomcat works — 八、tomcat中的session管理

    在使用shiro的session的时候感觉对于tomcat中session的管理还不是特别清楚,而且session管理作为tomcat中比较重要的一部分还是很有必要学习的. 目录 概述 session ...

  4. Tomcat 中的 Session 和 Cookie

    HTTP 是一种无状态通信协议,每个请求之间相互独立,服务器不能识别曾经来过的请求.而对于 Web 应用,它的活动都是依赖某个状态的,比如用户登录,此时使用 HTTP 就需要它在一次登录请求后,有为后 ...

  5. [转]tomcat中的session管理

    转载地址:http://blog.csdn.net/iloveqing/article/details/1544958 当一个sesson开始时,Servlet容器会创建一个HttpSession对象 ...

  6. Tomcat中的session实现

    Tomcat中一个会话对应一个session,其实现类是StandardSession,查看源码,可以找到一个attributes成员属性,即存储session的数据结构,为ConcurrentHas ...

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

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

  8. Nginx+tomcat集群中,session的共享

    nginx,tomcat集群后多个session分配到同一个应用 单节点低负荷的情况下,我们通常把一个WEB应用打成WAR包放WEB应用服务器,如TOMCAT下运行就行了(如图1).但随着用户量的增加 ...

  9. Tomcat使用Memcached Session Manager管理Session

    Tomcat使用Memcached Session Manager管理Session 废话不多说,直接进入主题.项目使用阿里云负载均衡+ECS服务器集群进行部署,Tomcat使用8.5版本.阿里云负载 ...

随机推荐

  1. PAT 1065 1066 1067 1068

    pat 1065 A+B and C                                          主要是注意一下加法溢出的情况,不要试图使用double,因为它的精度是15~16 ...

  2. C#中使用多款LevelDB.Net封装测试性能

    一.使用http://www.nuget.org/packages/LevelDB.NET 测试 1.新建项目,并Nuget引入库: 2.写代码 using LevelDB; using System ...

  3. js 开源k线图开发库

    https://github.com/andredumas/techan.js/wiki http://techanjs.org/ A visual, stock charting (Candlest ...

  4. http 三次握手

    1.三次握手 目的:主要规避由于网络阻塞,传输不畅等原因导致服务器端端口一直被占用,无法释放端口资源.(想想假设只要一次就建立连接,服务器响应后浏览器若是没收到数据,等待一段时间后,网络超时,浏览器重 ...

  5. Mysql查询优化之 触发器加中间表 方法优化count()统计大数据量总数问题

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6138288.html 在上一篇博文我们提到,分页有三种方法.其中,第三种是我们最常用的.然而,在实际应用过程中 ...

  6. java 怎么打印变量

    //Test.java public class Test16{ public static void main(String args[]){ int age=28; System.out.prin ...

  7. 转:高层游戏引擎——基于OGRE所实现的高层游戏引擎框架

    高层游戏引擎——基于OGRE所实现的高层游戏引擎框架 这是意念自己的毕业论文,在一个具体的实践之中,意念主要负责的是物件和GUI之外的其他游戏系统.意念才学疏陋,望众位前辈不吝赐教.由于代码质量不高. ...

  8. 【mysql】关于ICP、MRR、BKA等特性

    一.Index Condition Pushdown(ICP) Index Condition Pushdown (ICP)是mysql使用索引从表中检索行数据的一种优化方式,从mysql5.6开始支 ...

  9. 键盘录入, if语句

    1.运算符    (1).算数运算符        A:+ - * / % ++ --           注意:%运算符号取决于被摸数.           ++ --的两种用法:1.单独使用 ,都 ...

  10. 基于 CoreText 实现高性能 UITableView

    引起UITableView卡顿比较常见的原因有cell的层级过多.cell中有触发离屏渲染的代码(譬如:cornerRadius.maskToBounds 同时使用).像素是否对齐.是否使用UITab ...