深入Tomcat源码分析Session到底是个啥!
2016-10-19 14:48
471 查看
原帖地址:深入Tomcat源码分析Session到底是个啥!
Session到底是个啥?
我们都知道,HTTP协议本身是无状态的(Stateless),这对于一些简单的页面展示来说,功能足够,不受影响。而对于日渐复杂的动态页面、应用,各种需要登录认证等场景,就力不从心了。试想对于一个已经登录的用户仍然一次次的提示登录,会是什么感受呢?
因此,多数需要保持状态的应用,就要保证客户端和服务端交互状态的一致。对于浏览器发起的多次请求,仅基于HTTP协议,是无法识别出是否为同一用户请求的。而为了保持客户端和服务端交互状态,可以采取一系列的策略,例如:
Cookie
隐藏form 表单域
Session
URL
SSL
本文将通过深入Tomcat源码,分析这一最常用的Servlet容器内部是如何使用Session来保持客户端与服务器状态一致的。
做为一名Web应用开发者,我们一定都听说过,甚至了解过Session这个词,以及其背后代表的一些概念。
Session,中文称之为会话,用于两个设备之间交互时状态的保持。因此,会话中至少要有一方需要保存会话的状态。
在Servlet规范中,session对象由HttpSession这一接口来表示,接口描述简明的概括了其主要作用
而Tomcat内部则是通过StandardSession实现了HttpSession这个接口,内部统一使用StandardSession来处理。
在初次请求应用时,如果需要用到Session,则会创建之。一般的Servlet中并不会直接使用。而如果是请求JSP文件,由于JSP默认的隐式对象中是包含
session的,其在生成Servlet文件时,内部相当于包含了
HttpServletRequest.getSession(true)
因此,请求时会直接创建session对象。
创建session的过程,大致是下面的样子:
整体流程基本是先判断是否已经存在session,如果没有则创建。如果有,则直接使用。
此时,需要注意两个问题:
初次请求时,session如何生成并传递给客户端的
后续的其它请求中,如果将客户端的请求与服务端已有的session建立关联的
上面代码中,判断session不存在并创建的过程,是直接调用createSession这个方法,并会根据sessionId是否为空,来确定是完全新创建session,还是恢复已有session。
这里有个Session超时时间,即最大空闲时间
注意此处maxInactiveInterval的值,即为我们默认的web.xml中提供的session超时时间(后台回复关键字004,了解更多),为30分钟。
在创建完Session之后,Tomcat通过在响应头中设置Set-Cookie这个MimeHeader来返回给客户端session数据。返回的数据是这样的:
JSESSIONID=CC4D83F3A61823AA8F980C89890A19D7; Path=/manager/; HttpOnly
设置Header的过程如下:
我们看到,初次请求时,响应头中包含了高亮的数据。
那再次请求呢,我们看到这次响应头中没有sessionId的数据,而是转移到请求头中,并且是以Cookie的形式提供:
此时,传到服务端,服务端解析Cookie对应的JSESSIOONID,并提取对应的sessionId值,与服务端对应的session数据做关联。
我们看代码中的实现
再次请求时,从Request中获取SessionCookie的地方在这里:
CoyoteAdapter.postParseRequset()
其内部调用 parseSessionCookiesId(request), 解析请求头中的cookie数据。
此处需要注意SessionCookie的名称是允许配置的,因此这一名称不一定一直都是JSESSIONID。
在解析Cookie获取SessionId之后,我们拿到的仅仅是一个字符串,还不能马上和Session关联起来,此时request会将此值赋值给其内部的一个名为
requestSessionId的属性。
当后面再次请求session时,就和我们最上面代码看到的一样,会有一个findSession的过程,
到此,我们基本了解了客户端浏览器和服务端Tomcat之间,如果保持交互状态的一致中的一种实现方式,即SessionCookie。
而本质上,这一过程就是传统上Socket交互的一个过程,我们完全可以自已写一段代码模拟返回响应的数据,只是需要注意响应头数据在HTTP规范中有特定的格式要求,如下,即数据之间要以CRLF分隔
总结下,客户端初次请求服务端会创建Session,此时通过在响应头中设置Set-Cookie将sessionId传递给客户端。后续客户端的请求会在请求头中设置Cookie项带上sessionId,从而保证客户端与服务端交互状态的一致。
Session到底是个啥?
我们都知道,HTTP协议本身是无状态的(Stateless),这对于一些简单的页面展示来说,功能足够,不受影响。而对于日渐复杂的动态页面、应用,各种需要登录认证等场景,就力不从心了。试想对于一个已经登录的用户仍然一次次的提示登录,会是什么感受呢?
因此,多数需要保持状态的应用,就要保证客户端和服务端交互状态的一致。对于浏览器发起的多次请求,仅基于HTTP协议,是无法识别出是否为同一用户请求的。而为了保持客户端和服务端交互状态,可以采取一系列的策略,例如:
Cookie
隐藏form 表单域
Session
URL
SSL
本文将通过深入Tomcat源码,分析这一最常用的Servlet容器内部是如何使用Session来保持客户端与服务器状态一致的。
做为一名Web应用开发者,我们一定都听说过,甚至了解过Session这个词,以及其背后代表的一些概念。
Session,中文称之为会话,用于两个设备之间交互时状态的保持。因此,会话中至少要有一方需要保存会话的状态。
在Servlet规范中,session对象由HttpSession这一接口来表示,接口描述简明的概括了其主要作用
Provides a way to identify a user across more than one page request or visit to a Web site and to store information about that user. The servlet container uses this interface to create a session between an HTTP client and an HTTP server. The session persists for a specified time period, across more than one connection or page request from the user. A session usually corresponds to one user.
而Tomcat内部则是通过StandardSession实现了HttpSession这个接口,内部统一使用StandardSession来处理。
在初次请求应用时,如果需要用到Session,则会创建之。一般的Servlet中并不会直接使用。而如果是请求JSP文件,由于JSP默认的隐式对象中是包含
session的,其在生成Servlet文件时,内部相当于包含了
HttpServletRequest.getSession(true)
因此,请求时会直接创建session对象。
创建session的过程,大致是下面的样子:
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 (response != null && context.getServletContext() .getEffectiveSessionTrackingModes() .contains(SessionTrackingMode.COOKIE) && response.getResponse().isCommitted()) { throw new IllegalStateException( sm.getString("coyoteRequest.sessionCreateCommitted")); } // Attempt to reuse session id if one was submitted in a cookie // Do not reuse the session id if it is from a URL, to prevent possible // phishing attacks // Use the SSL session ID if one is present. if (("/".equals(context.getSessionCookiePath()) && isRequestedSessionIdFromCookie()) || requestedSessionSSL ) { session = manager.createSession(getRequestedSessionId()); } else { session = manager.createSession(null); } // Creating a new session cookie based on that session if (session != null && context.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; }
整体流程基本是先判断是否已经存在session,如果没有则创建。如果有,则直接使用。
此时,需要注意两个问题:
初次请求时,session如何生成并传递给客户端的
后续的其它请求中,如果将客户端的请求与服务端已有的session建立关联的
上面代码中,判断session不存在并创建的过程,是直接调用createSession这个方法,并会根据sessionId是否为空,来确定是完全新创建session,还是恢复已有session。
public Session createSession(String sessionId) { if ((maxActiveSessions >= 0) && (getActiveSessions() >= maxActiveSessions)) { rejectedSessions++; throw new TooManyActiveSessionsException( sm.getString("managerBase.createSession.ise"), maxActiveSessions); //注意这里有个策略 } // Recycle or create a Session instance Session session = createEmptySession(); // Initialize the properties of the new session and return it session.setNew(true); session.setValid(true); session.setCreationTime(System.currentTimeMillis()); session.setMaxInactiveInterval(this.maxInactiveInterval); String id = sessionId; if (id == null) { id = generateSessionId(); } session.setId(id); sessionCounter++; SessionTiming timing = new SessionTiming(session.getCreationTime(), 0); synchronized (sessionCreationTiming) { sessionCreationTiming.add(timing); sessionCreationTiming.poll(); } return (session); }
这里有个Session超时时间,即最大空闲时间
注意此处maxInactiveInterval的值,即为我们默认的web.xml中提供的session超时时间(后台回复关键字004,了解更多),为30分钟。
在创建完Session之后,Tomcat通过在响应头中设置Set-Cookie这个MimeHeader来返回给客户端session数据。返回的数据是这样的:
JSESSIONID=CC4D83F3A61823AA8F980C89890A19D7; Path=/manager/; HttpOnly
设置Header的过程如下:
public void addSessionCookieInternal(final Cookie cookie) { if (isCommitted()) { //此处判断,如果response已经提交,则不能再设置 return; } String name = cookie.getName(); final String headername = "Set-Cookie"; final String startsWith = name + "="; String header = generateCookieString(cookie); //此处根据具体cookie的内容生成对应的串,内部会判断cookie的版本,过期时间等 if (!set) { addHeader(headername, header); } }
我们看到,初次请求时,响应头中包含了高亮的数据。
那再次请求呢,我们看到这次响应头中没有sessionId的数据,而是转移到请求头中,并且是以Cookie的形式提供:
此时,传到服务端,服务端解析Cookie对应的JSESSIOONID,并提取对应的sessionId值,与服务端对应的session数据做关联。
我们看代码中的实现
再次请求时,从Request中获取SessionCookie的地方在这里:
CoyoteAdapter.postParseRequset()
其内部调用 parseSessionCookiesId(request), 解析请求头中的cookie数据。
public void parseCookieHeader(MimeHeaders headers, ServerCookies serverCookies) { // process each "cookie" header int pos = headers.findHeader("Cookie", 0); } }
此处需要注意SessionCookie的名称是允许配置的,因此这一名称不一定一直都是JSESSIONID。
在解析Cookie获取SessionId之后,我们拿到的仅仅是一个字符串,还不能马上和Session关联起来,此时request会将此值赋值给其内部的一个名为
requestSessionId的属性。
当后面再次请求session时,就和我们最上面代码看到的一样,会有一个findSession的过程,
到此,我们基本了解了客户端浏览器和服务端Tomcat之间,如果保持交互状态的一致中的一种实现方式,即SessionCookie。
而本质上,这一过程就是传统上Socket交互的一个过程,我们完全可以自已写一段代码模拟返回响应的数据,只是需要注意响应头数据在HTTP规范中有特定的格式要求,如下,即数据之间要以CRLF分隔
总结下,客户端初次请求服务端会创建Session,此时通过在响应头中设置Set-Cookie将sessionId传递给客户端。后续客户端的请求会在请求头中设置Cookie项带上sessionId,从而保证客户端与服务端交互状态的一致。
相关文章推荐
- tomcat源码分析3—>代理模式深入分析
- Tomcat7.0源码分析——Session管理分析(下)
- Tomcat 服务器Session管理深入剖析-附带源代码分析
- Tomcat 之 Session的活化和钝化 源码分析
- tomcat源码分析(4)—>servlet深入分析(部分关键类的描述)
- tomcat源码分析(4)—>servlet深入分析
- [置顶] Spring Boot系列十二 通过redis实现Tomcat集群的Session同步及从源码分析其原理
- Java源码分析:深入探讨Iterator模式
- Java源码分析:深入探讨Iterator模式 (转载)
- Java源码分析:深入探讨Iterator模式
- Java源码分析:深入探讨Iterator模式
- Java源码分析:深入探讨Iterator模式
- Java源码分析:深入探讨Iterator模式
- Java源码分析:深入探讨Iterator模式
- [源码分析]Community Server的MemberRole之Membership深入篇
- Java源码分析:深入探讨Iterator模式 (转)
- Java源码分析:深入探讨Iterator模式及Java 5.0中改进的for循环
- Java源码分析:深入探讨Iterator模式
- tomcat 4.1.30启动过程的源码分析
- TOMCAT源码分析(消息处理)