Session对象的创建一般是源于这样的一条语句:
Session session = request.getSession(false);或者Session session = request.getSession();如果不在乎服务器压力可能多那么一点点的话。
在Tomcat的实现中,这个request是org.apache.catalina.connector.Request类的包装类 org.apache.catalina.connector.RequestFacade的对象,它的两个#getSession()方法如下:
- public HttpSession getSession(boolean create) {
- if (request == null) {
- throw new IllegalStateException(
- sm.getString("requestFacade.nullRequest"));
- }
- if (SecurityUtil.isPackageProtectionEnabled()){
- return (HttpSession)AccessController.
- doPrivileged(new GetSessionPrivilegedAction(create));
- } else {
- return request.getSession(create);
- }
- }
- public HttpSession getSession() {
- if (request == null) {
- throw new IllegalStateException(
- sm.getString("requestFacade.nullRequest"));
- }
- return getSession(true);
- }
其实差不太多,最后都会进入org.apache.catalina.connector.Request的#getSession()方法。这个方法的源代码如下:
- public HttpSession getSession(boolean create) {
- Session session = doGetSession(create);
- if (session != null) {
- return session.getSession();
- } else {
- return null;
- }
- }
然后调用就到了#doGetSession()这个方法了。源代码如下
- protected Session doGetSession(boolean create) {
- // 没有Context的话直接返回null
- if (context == null)
- return (null);
- // 判断Session是否有效
- if ((session != null) && !session.isValid())
- session = null;
- if (session != null)
- return (session);
- // 返回Manager对象,这里是StandardManager类的对象
- Manager manager = null;
- if (context != null)
- manager = context.getManager();
- if (manager == null)
- return (null); // Sessions are not supported
- // 判断是否有SessionID
- if (requestedSessionId != null) {
- try {
- // 在Manager中根据SessionID查找Session
- session = manager.findSession(requestedSessionId);
- } catch (IOException e) {
- session = null;
- }
- if ((session != null) && !session.isValid())
- session = null;
- if (session != null) {
- // 更新访问时间
- session.access();
- return (session);
- }
- }
- // 创建新的Session
- if (!create)
- return (null);
- if ((context != null) && (response != null) && context.getCookies()
- && response.getResponse().isCommitted()) {
- throw new IllegalStateException(sm.getString("coyoteRequest.sessionCreateCommitted"));
- }
- // 判断是否使用 "/" 作为Session Cookie的存储路径 并且 是否SessionID来自Cookie
- if (connector.getEmptySessionPath() && isRequestedSessionIdFromCookie()) {
- // 创建Session
- session = manager.createSession(getRequestedSessionId());
- } else {
- session = manager.createSession(null);
- }
- // 创建一个新的Session Cookies
- if ((session != null) && (getContext() != null) && getContext().getCookies()) {
- Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME, session.getIdInternal());
- // 配置Session Cookie
- configureSessionCookie(cookie);
- // 在响应中加入Session Cookie
- response.addCookieInternal(cookie);
- }
- if (session != null) {
- // 更新访问时间
- session.access();
- return (session);
- } else {
- return (null);
- }
- }
这个方法说明了Session创建的大致过程,首先判断requestedSessionId是否存在,如果存在,那么根据这个ID去查找 Session对象。如果requestedSessionId不存在或者没有取到Session,并且传递给#getSession(boolean) 的参数为真,那么要创建一个新的Session,并且给客户端写回去一个Session Cookie。
首先,我感兴趣的是requestedSessionId的赋值,它到底是什么时候被赋值的呢?
还要向回看Tomcat的请求处理过程,请求曾到过这一步,org.apache.catalina.connector.CoyoteAdapter的#service()方法。里边有这样一句方法调用:postParseRequest(req, request, res, response)。就是这一步处理了SessionID的获取,这个方法调用了#parseSessionId()和 parseSessionCookiesId()这两个方法,就是它对Session ID进行了提取,源代码分别如下:
- protected void parseSessionId(org.apache.coyote.Request req, Request request) {
- ByteChunk uriBC = req.requestURI().getByteChunk();
- // 判断URL中是不是有";jsessionid="这个字符串
- int semicolon = uriBC.indexOf(match, 0, match.length(), 0);
- if (semicolon > 0) {
- // Parse session ID, and extract it from the decoded request URI
- // 在URL中提取Session ID
- int start = uriBC.getStart();
- int end = uriBC.getEnd();
- int sessionIdStart = semicolon + match.length();
- int semicolon2 = uriBC.indexOf(';', sessionIdStart);
- if (semicolon2 >= 0) {
- request.setRequestedSessionId(new String(uriBC.getBuffer(), start + sessionIdStart,
- semicolon2 - sessionIdStart));
- byte[] buf = uriBC.getBuffer();
- for (int i = 0; i < end - start - semicolon2; i++) {
- buf[start + semicolon + i] = buf[start + i + semicolon2];
- }
- uriBC.setBytes(buf, start, end - start - semicolon2 + semicolon);
- } else {
- request.setRequestedSessionId(new String(uriBC.getBuffer(), start + sessionIdStart,
- (end - start) - sessionIdStart));
- uriBC.setEnd(start + semicolon);
- }
- // 设定Session ID来自于URL
- request.setRequestedSessionURL(true);
- } else {
- request.setRequestedSessionId(null);
- request.setRequestedSessionURL(false);
- }
- }
- protected void parseSessionCookiesId(org.apache.coyote.Request req, Request request) {
- Context context = (Context) request.getMappingData().context;
- if (context != null && !context.getCookies())
- return;
- // 返回Cookie
- Cookies serverCookies = req.getCookies();
- int count = serverCookies.getCookieCount();
- if (count <= 0)
- return;
- for (int i = 0; i < count; i++) {
- ServerCookie scookie = serverCookies.getCookie(i);
- // 判断是否有JSESSIONID这个名字的Cookie
- if (scookie.getName().equals(Globals.SESSION_COOKIE_NAME)) {
- // Override anything requested in the URL
- if (!request.isRequestedSessionIdFromCookie()) {
- // 设定Session ID
- convertMB(scookie.getValue());
- request.setRequestedSessionId(scookie.getValue().toString());
- // 如果之前在URL中读到了SessionID,那么会覆盖它
- request.setRequestedSessionCookie(true);
- request.setRequestedSessionURL(false);
- if (log.isDebugEnabled())
- log.debug(" Requested cookie session id is " + request.getRequestedSessionId());
- } else {
- if (!request.isRequestedSessionIdValid()) {
- convertMB(scookie.getValue());
- request.setRequestedSessionId(scookie.getValue().toString());
- }
- }
- }
- }
- }
Tomcat就是通过上边的两个方法读到URL或者Cookie中存放的Session ID的。
了解了Session ID的获取,下面要看一下Session的查找过程,就是org.apache.catalina.session.StandardManager的#findSession()方法。这个方法是在它的基类中定义的,源代码如下:
- public Session findSession(String id) throws IOException {
- if (id == null)
- return (null);
- return (Session) sessions.get(id);
- }
代码很短,其中sessions是一个ConcurrentHashMap<String, Session>对象。那么这个sessions的对象是什么时候载入的Session呢?
启动的时候!可以看一下StandardManager#start()方法。最后调用了#load()方法,这个就是载入Session的方法了:
- public void load() throws ClassNotFoundException, IOException {
- if (SecurityUtil.isPackageProtectionEnabled()) {
- try {
- AccessController.doPrivileged(new PrivilegedDoLoad());
- } catch (PrivilegedActionException ex) {
- Exception exception = ex.getException();
- if (exception instanceof ClassNotFoundException) {
- throw (ClassNotFoundException) exception;
- } else if (exception instanceof IOException) {
- throw (IOException) exception;
- }
- if (log.isDebugEnabled())
- log.debug("Unreported exception in load() " + exception);
- }
- } else {
- doLoad();
- }
- }
最后调用了#doLoad()方法来具体的载入Session,源代码如下:
- protected void doLoad() throws ClassNotFoundException, IOException {
- if (log.isDebugEnabled())
- log.debug("Start: Loading persisted sessions");
- // 清空Map
- sessions.clear();
- // 对应work/Catalina/localhost/%app name%/SESSIONS.ser文件
- File file = file();
- if (file == null)
- return;
- if (log.isDebugEnabled())
- log.debug(sm.getString("standardManager.loading", pathname));
- FileInputStream fis = null;
- ObjectInputStream ois = null;
- Loader loader = null;
- ClassLoader classLoader = null;
- try {
- // 载入Session缓存文件
- fis = new FileInputStream(file.getAbsolutePath());
- BufferedInputStream bis = new BufferedInputStream(fis);
- if (container != null)
- loader = container.getLoader();
- if (loader != null)
- classLoader = loader.getClassLoader();
- if (classLoader != null) {
- if (log.isDebugEnabled())
- log.debug("Creating custom object input stream for class loader ");
- ois = new CustomObjectInputStream(bis, classLoader);
- } else {
- if (log.isDebugEnabled())
- log.debug("Creating standard object input stream");
- ois = new ObjectInputStream(bis);
- }
- } catch (FileNotFoundException e) {
- if (log.isDebugEnabled())
- log.debug("No persisted data file found");
- return;
- } catch (IOException e) {
- log.error(sm.getString("standardManager.loading.ioe", e), e);
- if (ois != null) {
- try {
- ois.close();
- } catch (IOException f) {
- ;
- }
- ois = null;
- }
- throw e;
- }
- synchronized (sessions) {
- try {
- // 读出Session个数
- Integer count = (Integer) ois.readObject();
- int n = count.intValue();
- if (log.isDebugEnabled())
- log.debug("Loading " + n + " persisted sessions");
- // 读入Session
- for (int i = 0; i < n; i++) {
- StandardSession session = getNewSession();
- session.readObjectData(ois);
- session.setManager(this);
- sessions.put(session.getIdInternal(), session);
- session.activate();
- sessionCounter++;
- }
- } catch (ClassNotFoundException e) {
- log.error(sm.getString("standardManager.loading.cnfe", e), e);
- if (ois != null) {
- try {
- ois.close();
- } catch (IOException f) {
- ;
- }
- ois = null;
- }
- throw e;
- } catch (IOException e) {
- log.error(sm.getString("standardManager.loading.ioe", e), e);
- if (ois != null) {
- try {
- ois.close();
- } catch (IOException f) {
- ;
- }
- ois = null;
- }
- throw e;
- } finally {
- try {
- if (ois != null)
- ois.close();
- } catch (IOException f) {
- }
- // 删除Session缓存文件
- if (file != null && file.exists())
- file.delete();
- }
- }
- if (log.isDebugEnabled())
- log.debug("Finish: Loading persisted sessions");
- }
大致知道了Session的读取过程,后面就是Session没找到时创建Session的过程了。具体就是org.apache.catalina.session.StandardManager的#createSession()方法:
- public Session createSession(String sessionId) {
- if ((maxActiveSessions >= 0) && (sessions.size() >= maxActiveSessions)) {
- rejectedSessions++;
- throw new IllegalStateException(sm.getString("standardManager.createSession.ise"));
- }
- return (super.createSession(sessionId));
- }
最后调用到了它的基类的#createSession()方法了。
- public Session createSession(String sessionId) {
- // 创建一个新的Session
- Session session = createEmptySession();
- // 初始化Session的属性
- session.setNew(true);
- session.setValid(true);
- session.setCreationTime(System.currentTimeMillis());
- session.setMaxInactiveInterval(this.maxInactiveInterval);
- // 如果Session ID为null,那么就生成一个
- if (sessionId == null) {
- sessionId = generateSessionId();
- }
- session.setId(sessionId);
- sessionCounter++;
- return (session);
- }
通过上述过程,一个新的Session就创建出来了。
相关推荐
Tomcat8亲测可用 tomcat-redis-session-manager的jar包 修改了tomcat-redis-session-manager源码进行的编译生成的jar包
自己通过源码编译后的jar包,已实验可以正常使用
基于tomcat-redis-session-manager源码进行的编译生成的jar包,压缩包中包含Tomcat7和Tomcat8打好的jar包。
tomcat8下 tomcat-redis-session-manager , github上有源码,其他版本都有打好的jar包,tomcat 8 下没有,下载源码生成了一个。
基于tomcat-redis-session-manager源码进行的编译生成的jar包,压缩包中包含Tomcat7和Tomcat8打好的jar包。
基于tomcat-redis-session-manager源码进行的编译生成的jar包,压缩包中包含Tomcat7和Tomcat8打好的jar包。
这是一个tomcat使用了自带会话同步功能时,不安全的配置(没有使用EncryptInterceptor)导致存在的反序列化漏洞,通过精心构造的数据包,可以对使用了tomcat自带会话同步功能的服务器进行攻击。 使用 先编译出jar包...
redis+tomcat实现session共享需要的包,源码编译打包生成,亲测可用. 编译环境:tomcat7+jdk7。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
由于工作原因,用户环境必须使用tomcat6,且想做集群环境,没办法只得找资源,可惜网上tomcat6的资源比较少,而且本人找到的基本都有错误,不能保存session进redis,导致每次测试页面都生成新的sessionid,所以最后...
真正实现一键自动化部署,Nginx+Tomcat+Redis 实现负载均衡集群服务搭建,所有文件自动生成,包括自动生成配置文件参数、解决依赖问题、Session共享同步问题等等,直接把 Web 应用的 war 包,放进Tomcat的 webapps ...
第9章 session管理 62 9.1 概述 62 9.2 Sessions 62 9.2.1 Session接口 62 9.2.2 StandardSession类 63 9.2.3 StandardSessionFacade类 65 9.3 Manager 65 9.3.1 Manager接口 66 9.3.2 ManagerBase类 66 9.3.3 ...
根据源码这个Valid只做一件事,就是在控制台打印一下当前的sessionID。如果程序已经部署到tomcat下面,这个输出信息,要到 Tomcat 6.0\logs目录下,查看最新的stdout_20120111.log文件,其中那个20120111表示文件...
登陆jsp采用jquery.cooki.js脚本在项目根目录下生成cookie,用来控制一个浏览器只能登陆一个用户,用户退出时清空cookie,关闭浏览器cookie自动清空,用户非正常退出下,需等待20分的session失效时间后方可正常登陆...
在Tomcat中怎样指连接一次,建立一个JavaBean封装连接,写一个Servlet连接,并把包含连接的Bean保存在session中,或直接生成静态变量。在其他Servlet中从Session中得到Bean,并可以直接使用连接。
6.10.4 利用session制作一个购物车 6.10.5 JavaBeans的作用域 6.10.6 利用JavaBeans制作的购物车 6.11 使用application对象 6.11.1 应用的概念 6.11.2 application对象可用的方法和属性 6.11.3 application...
解压后打开cas-server-3.3.1-release\cas-server-3.3.1\modules ,将cas-server-webapp-3.3.1.war 重命名为cas.war,并将war包拷贝到tomcat5.5以上版本的webapps目录下(在此对server jdbc支持不做详细解读,测试...
在用nginx的反向代理tomcat的路径中,可能会出现session丢失问题。每发送一次请求 JESSIONID 都会发生改变,说明上一次形成的session丢失,从而创建新的session。 第一种情况: server{ listen 80; server_name...
Spring session | 分布式Session管理 | [http://projects.spring.io/spring-session/](http://projects.spring.io/spring-session/) MyBatis | ORM框架 | [http://www.mybatis.org/mybatis-3/zh/index.html]...
- Spring Boot 缓存,包括redis、ehcache、spring-cache、memcached、使用redis实现session共享 等。 - springboot-templates - Spring Boot 模板,包括thymeleaf、freemarker、jsp、表单校验 等。 - ...