session详解
什么是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 {
Session().getAttribute("count") == null){
}else{
int a = Integer.Session().getAttribute("count").toString());
}
Cookie[] cookies = Cookies();
StringBuffer sb = new StringBuffer();
if(cookies!=null){
for(Cookie cookie : cookies){
sb.Name()+":"+Value()+",");
}
sb.deleteCharAt(sb.length()-1);
}
System.out.println("[第"+(++index)+"次访问]from client request, cookies:" + sb);
System.out.println("[第"+(index)+"次访问]from server response, header-Set-Cookie:" + Header("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 ⽅法源码,如下源码:
View Code
先看doGetSession⽅法中的如下代码,这个⼀般是第⼀次访问的情况,即创建session对象,session的创建是调⽤了ManagerBase的createSession⽅法来实现的; 另外,注意response.addSessionCookieInternal⽅法,该⽅法的功能就是上⾯提到的往响应头写⼊“Set-Cookie”信息;最后,还要调⽤session.access⽅法记录下该session的最后访问时间,因为session是可以设置过期时间的;
session = ateSession(sessionId);
// Creating a new session cookie based on that session
if ((session != null) && (getContext() != null)
&& getContext().getServletContext().
getEffectiveSessionTrackingModes().contains(
SessionTrackingMode.COOKIE)) {
Cookie cookie =
context, IdInternal(), 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⽅法,记录下该s
ession的最后访问时间;
if (requestedSessionId != null) {
try {
session = manager.findSession(requestedSessionId);
} catch (IOException e) {
session = null;
}
session如何设置和读取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也是可以的;
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论