[Tomcat6.0源码]Servlet API之Session、Cookie
2012-10-22 23:24
776 查看
一、Session的使用
在servlet中取得Session:
1.doGetSession():
context.getManager();返回的是StandardManager对象,session就保存在其中。StandardManager对象是在StandardContext.start()时创建的:
(1).第一次调用getSession(),requestedSessionId为null,就会去创建一个Session:
createEmptySession(),调用getNewSession():
sessionId来自ManagerBasic.generateSessionId:
session.setId()会调用重构方法,即StandardSession.setId():
(2).设置session后,第二个请求报头中就会多出一行:
Cookie: JSESSIONID=*******
第二次进行request.getSession()就不用去创建,会根据这个JSESSIONID去查找:
headers是org.apache.coyote.Request的属性,在Http11Processer.process()中调用inputBuffer.parseHeaders()时往里边add值。
Cookies.processCookies(headers):
(3).将sessionId保存到cookie中:
2.回到org.apache.catalina.connector.Request.getSession():
那么request.getSession()返回的是StandardSessionFacade对象。
还有另外一个方法getSession(boolean create):
二、Session过期处理:
StandardContext父类ContainerBase.threadStart():
thisAccessedTime:最后一次访问session的时间,在org.apache.catalina.connector.Request.doGetSession()中调用StandardSession.access()时设置当前时间。
maxInactiveInterval:ManagerBase.createSession(String sessionId)中创建session的时候就设置有效期:
那么ManagerBase的maxInactiveInterval属性是什么时候设置的?回头看看在StandardContext.start()中创建StandardManager的时候有:
调用setMaxInactiveInterval()对ManagerBase.maxInactiveInterval变量设置,而这个参数是来自与之关联的StandardContext的sessionTimeout的属性值。
StandardContext.sessionTimeOut又是在解析web.xml时配置的
这个maxInactiveInterval的来源:web.xml配置session-timeout(单位是分钟)——StandardContext.setSessionTimeout()——StandardManager.setMaxInactiveInterval()(单位是秒)——StandardSession.setMaxInactiveInterval()——StandardSession.inValid()。
在servlet中取得Session:
HttpSession session = request.getSession();request.getSession()即org.apache.catalina.connector.Request.getSession():
public HttpSession getSession() { Session session = doGetSession(true); if (session != null) { return session.getSession(); } else { return null; } }
1.doGetSession():
protected Session doGetSession(boolean create) { // There cannot be a session if no context has been assigned yet 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 = null; if (context != null) 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.getCookies() && 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 if (connector.getEmptySessionPath() && isRequestedSessionIdFromCookie()) { session = manager.createSession(getRequestedSessionId()); } else { session = manager.createSession(null); } // Creating a new session cookie based on that session if ((session != null) && (getContext() != null) && getContext().getCookies()) { String scName = context.getSessionCookieName(); if (scName == null) { scName = Globals.SESSION_COOKIE_NAME; } Cookie cookie = new Cookie(scName, session.getIdInternal()); configureSessionCookie(cookie); response.addSessionCookieInternal(cookie, context.getUseHttpOnly()); } if (session != null) { session.access(); return (session); } else { return (null); } }如果有session就查出,没有的话就创建一个,并且将ID保存到cookie中。
context.getManager();返回的是StandardManager对象,session就保存在其中。StandardManager对象是在StandardContext.start()时创建的:
Manager contextManager = null; if (manager == null) { if ( (getCluster() != null) && distributable) { try { contextManager = getCluster().createManager(getName()); } catch (Exception ex) { log.error("standardContext.clusterFail", ex); ok = false; } } else { contextManager = new StandardManager(); } } // Configure default manager if none was specified if (contextManager != null) { setManager(contextManager); }StandardManager保存Session,还将处理过期的Session。
(1).第一次调用getSession(),requestedSessionId为null,就会去创建一个Session:
session = manager.createSession(null);StandardManager.createSession():
public Session createSession(String sessionId) { if ((maxActiveSessions >= 0) && (sessions.size() >= maxActiveSessions)) { rejectedSessions++; throw new TooManyActiveSessionsException( sm.getString("standardManager.createSession.ise"), maxActiveSessions); } return (super.createSession(sessionId)); }调用父类ManagerBase.createSession():
public Session createSession(String sessionId) { // 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); if (sessionId == null) { sessionId = generateSessionId(); } session.setId(sessionId); sessionCounter++; SessionTiming timing = new SessionTiming(session.getCreationTime(), 0); synchronized (sessionCreationTiming) { sessionCreationTiming.add(timing); sessionCreationTiming.poll(); } return (session); }
createEmptySession(),调用getNewSession():
protected StandardSession getNewSession() { return new StandardSession(this); }
sessionId来自ManagerBasic.generateSessionId:
protected synchronized String generateSessionId() { byte random[] = new byte[16]; String jvmRoute = getJvmRoute(); String result = null; // Render the result as a String of hexadecimal digits StringBuffer buffer = new StringBuffer(); do { int resultLenBytes = 0; if (result != null) { buffer = new StringBuffer(); duplicates++; } while (resultLenBytes < this.sessionIdLength) { getRandomBytes(random); random = getDigest().digest(random); for (int j = 0; j < random.length && resultLenBytes < this.sessionIdLength; j++) { byte b1 = (byte) ((random[j] & 0xf0) >> 4); byte b2 = (byte) (random[j] & 0x0f); if (b1 < 10) buffer.append((char) ('0' + b1)); else buffer.append((char) ('A' + (b1 - 10))); if (b2 < 10) buffer.append((char) ('0' + b2)); else buffer.append((char) ('A' + (b2 - 10))); resultLenBytes++; } } if (jvmRoute != null) { buffer.append('.').append(jvmRoute); } result = buffer.toString(); } while (sessions.containsKey(result)); return (result); }
session.setId()会调用重构方法,即StandardSession.setId():
public void setId(String id, boolean notify) { if ((this.id != null) && (manager != null)) manager.remove(this); this.id = id; if (manager != null) manager.add(this); if (notify) { tellNew(); } }manager.add()即StandardManager父类ManagerBase.add(),将session添加到session添加到ConcurrentHashMap<String, Session>对象sessions中。
(2).设置session后,第二个请求报头中就会多出一行:
Cookie: JSESSIONID=*******
第二次进行request.getSession()就不用去创建,会根据这个JSESSIONID去查找:
session = manager.findSession(requestedSessionId);requestedSessionId来自CoyoteAdapter.parseSessionCookiesId():
protected void parseSessionCookiesId(org.apache.coyote.Request req, Request request) { // If session tracking via cookies has been disabled for the current // context, don't go looking for a session ID in a cookie as a cookie // from a parent context with a session ID may be present which would // overwrite the valid session ID encoded in the URL Context context = (Context) request.getMappingData().context; if (context != null && !context.getCookies()) return; // Parse session id from cookies Cookies serverCookies = req.getCookies(); int count = serverCookies.getCookieCount(); if (count <= 0) return; String sessionCookieName = getSessionCookieName(context); for (int i = 0; i < count; i++) { ServerCookie scookie = serverCookies.getCookie(i); if (scookie.getName().equals(sessionCookieName)) { // Override anything requested in the URL if (!request.isRequestedSessionIdFromCookie()) { // Accept only the first session id cookie convertMB(scookie.getValue()); request.setRequestedSessionId (scookie.getValue().toString()); request.setRequestedSessionCookie(true); request.setRequestedSessionURL(false); if (log.isDebugEnabled()) log.debug(" Requested cookie session id is " + request.getRequestedSessionId()); } else { if (!request.isRequestedSessionIdValid()) { // Replace the session id until one is valid convertMB(scookie.getValue()); request.setRequestedSessionId (scookie.getValue().toString()); } } } } }Cookies serverCookies = req.getCookies();调用的是org.apache.coyote.Request.getCookies()返回的是Cookies属性:
private Cookies cookies = new Cookies(headers);int count = serverCookies.getCookieCount();调用的是Cookies.getCookieCount():
public int getCookieCount() { if( unprocessed ) { unprocessed=false; processCookies(headers); } return cookieCount; }
headers是org.apache.coyote.Request的属性,在Http11Processer.process()中调用inputBuffer.parseHeaders()时往里边add值。
Cookies.processCookies(headers):
public void processCookies( MimeHeaders headers ) { if( headers==null ) return;// nothing to process // process each "cookie" header int pos=0; while( pos>=0 ) { // Cookie2: version ? not needed pos=headers.findHeader( "Cookie", pos ); // no more cookie headers headers if( pos<0 ) break; MessageBytes cookieValue=headers.getValue( pos ); if( cookieValue==null || cookieValue.isNull() ) { pos++; continue; } // Uncomment to test the new parsing code if( cookieValue.getType() == MessageBytes.T_BYTES ) { if( dbg>0 ) log( "Parsing b[]: " + cookieValue.toString()); ByteChunk bc=cookieValue.getByteChunk(); processCookieHeader( bc.getBytes(), bc.getOffset(), bc.getLength()); } else { if( dbg>0 ) log( "Parsing S: " + cookieValue.toString()); processCookieHeader( cookieValue.toString() ); } pos++;// search from the next position } }pocessCookieHeader()才开始去解析请求报头中的cookie,用到时才解析的处理方法和parameter相似。
(3).将sessionId保存到cookie中:
Cookie cookie = new Cookie(scName, session.getIdInternal()); configureSessionCookie(cookie); response.addSessionCookieInternal(cookie, context.getUseHttpOnly())cookie保存的是键值对,保存在浏览器内存中,跨窗口无法访问,窗口关掉即消失。和正常所说的Cookie不同的是,后者保存在硬盘上。
2.回到org.apache.catalina.connector.Request.getSession():
return session.getSession();即StandardSession.getSession():
public HttpSession getSession() { if (facade == null){ if (SecurityUtil.isPackageProtectionEnabled()){ final StandardSession fsession = this; facade = (StandardSessionFacade)AccessController.doPrivileged(new PrivilegedAction(){ public Object run(){ return new StandardSessionFacade(fsession); } }); } else { facade = new StandardSessionFacade(this); } } return (facade); }facade = new StandardSessionFacade(this);StandardSessionFacade是的对StandardSession的封装,屏蔽了些方法。
那么request.getSession()返回的是StandardSessionFacade对象。
还有另外一个方法getSession(boolean create):
public HttpSession getSession(boolean create) { Session session = doGetSession(create); if (session != null) { return session.getSession(); } else { return null; } }也是调用doGetSession();那么getSession()=getSession(true);而getSession(false)调用doGetSession(false),回去查找session是否存
二、Session过期处理:
StandardContext父类ContainerBase.threadStart():
protected void threadStart() { if (thread != null) return; if (backgroundProcessorDelay <= 0) return; threadDone = false; String threadName = "ContainerBackgroundProcessor[" + toString() + "]"; thread = new Thread(new ContainerBackgroundProcessor(), threadName); thread.setDaemon(true); thread.start(); }启动内部类线程ContainerBackgroundProcessor.run():
public void run() { while (!threadDone) { try { Thread.sleep(backgroundProcessorDelay * 1000L); } catch (InterruptedException e) { ; } if (!threadDone) { Container parent = (Container) getMappingObject(); ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (parent.getLoader() != null) { cl = parent.getLoader().getClassLoader(); } processChildren(parent, cl); } } }getMappingObject()方法返回this,即StandardContext对象。ContainerBase$ContainerBackgroundProcessor.processChildren():
protected void processChildren(Container container, ClassLoader cl) { try { if (container.getLoader() != null) { Thread.currentThread().setContextClassLoader (container.getLoader().getClassLoader()); } container.backgroundProcess(); } catch (Throwable t) { log.error("Exception invoking periodic operation: ", t); } finally { Thread.currentThread().setContextClassLoader(cl); } Container[] children = container.findChildren(); for (int i = 0; i < children.length; i++) { if (children[i].getBackgroundProcessorDelay() <= 0) { processChildren(children[i], cl); } } }container.backgroundProcess()调用StandardContext父类ContainerBase.backgroundProcess():
public void backgroundProcess() { if (!started) return; if (cluster != null) { try { cluster.backgroundProcess(); } catch (Exception e) { log.warn(sm.getString("containerBase.backgroundProcess.cluster", cluster), e); } } if (loader != null) { try { loader.backgroundProcess(); } catch (Exception e) { log.warn(sm.getString("containerBase.backgroundProcess.loader", loader), e); } } if (manager != null) { try { manager.backgroundProcess(); } catch (Exception e) { log.warn(sm.getString("containerBase.backgroundProcess.manager", manager), e); } } if (realm != null) { try { realm.backgroundProcess(); } catch (Exception e) { log.warn(sm.getString("containerBase.backgroundProcess.realm", realm), e); } } Valve current = pipeline.getFirst(); while (current != null) { try { current.backgroundProcess(); } catch (Exception e) { log.warn(sm.getString("containerBase.backgroundProcess.valve", current), e); } current = current.getNext(); } lifecycle.fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null); }manager即StandardManager,在StandardContext.start()时创建的。manager.backgroundProcess()调用StandardManager父类ManagerBase.backgroundProcess():
public void backgroundProcess() { count = (count + 1) % processExpiresFrequency; if (count == 0) processExpires(); }ManagerBase.processExpires():
public void processExpires() { long timeNow = System.currentTimeMillis(); Session sessions[] = findSessions(); int expireHere = 0 ; if(log.isDebugEnabled()) log.debug("Start expire sessions " + getName() + " at " + timeNow + " sessioncount " + sessions.length); for (int i = 0; i < sessions.length; i++) { if (sessions[i]!=null && !sessions[i].isValid()) { expireHere++; } } long timeEnd = System.currentTimeMillis(); if(log.isDebugEnabled()) log.debug("End expire sessions " + getName() + " processingTime " + (timeEnd - timeNow) + " expired sessions: " + expireHere); processingTime += ( timeEnd - timeNow ); }StandardSession.isValid()判断session最后操作时间到现在的时间差是否超出最大时间:
public boolean isValid() { if (this.expiring) { return true; } if (!this.isValid) { return false; } if (ACTIVITY_CHECK && accessCount.get() > 0) { return true; } if (maxInactiveInterval >= 0) { long timeNow = System.currentTimeMillis(); int timeIdle = (int) ((timeNow - thisAccessedTime) / 1000L); if (timeIdle >= maxInactiveInterval) { expire(true); } } return (this.isValid); }
thisAccessedTime:最后一次访问session的时间,在org.apache.catalina.connector.Request.doGetSession()中调用StandardSession.access()时设置当前时间。
maxInactiveInterval:ManagerBase.createSession(String sessionId)中创建session的时候就设置有效期:
session.setMaxInactiveInterval(this.maxInactiveInterval);StandardSession.setMaxInactiveInterval():
public void setMaxInactiveInterval(int interval) { this.maxInactiveInterval = interval; if (isValid && interval == 0) { expire(); } }
那么ManagerBase的maxInactiveInterval属性是什么时候设置的?回头看看在StandardContext.start()中创建StandardManager的时候有:
if (contextManager != null) { setManager(contextManager); }StandardContext的父类ContainerBase.setManager():
public synchronized void setManager(Manager manager) { // Change components if necessary Manager oldManager = this.manager; if (oldManager == manager) return; this.manager = manager; // Stop the old component if necessary if (started && (oldManager != null) && (oldManager instanceof Lifecycle)) { try { ((Lifecycle) oldManager).stop(); } catch (LifecycleException e) { log.error("ContainerBase.setManager: stop: ", e); } } // Start the new component if necessary if (manager != null) manager.setContainer(this); if (started && (manager != null) && (manager instanceof Lifecycle)) { try { ((Lifecycle) manager).start(); } catch (LifecycleException e) { log.error("ContainerBase.setManager: start: ", e); } } // Report this property change to interested listeners support.firePropertyChange("manager", oldManager, this.manager); }manager.setContainer(this);就是将StandardManager和StandardContext关联起来,StandardManager.setContainer():
public void setContainer(Container container) { // De-register from the old Container (if any) if ((this.container != null) && (this.container instanceof Context)) ((Context) this.container).removePropertyChangeListener(this); // Default processing provided by our superclass super.setContainer(container); // Register with the new Container (if any) if ((this.container != null) && (this.container instanceof Context)) { setMaxInactiveInterval ( ((Context) this.container).getSessionTimeout()*60 ); ((Context) this.container).addPropertyChangeListener(this); } }
调用setMaxInactiveInterval()对ManagerBase.maxInactiveInterval变量设置,而这个参数是来自与之关联的StandardContext的sessionTimeout的属性值。
StandardContext.sessionTimeOut又是在解析web.xml时配置的
digester.addCallMethod(prefix + "web-app/session-config/session-timeout", "setSessionTimeout", 1, new Class[] { Integer.TYPE }); digester.addCallParam(prefix + "web-app/session-config/session-timeout", 0);StandardContext.setSessionTimeout():
public void setSessionTimeout(int timeout) { int oldSessionTimeout = this.sessionTimeout; /* * SRV.13.4 ("Deployment Descriptor"): * If the timeout is 0 or less, the container ensures the default * behaviour of sessions is never to time out. */ this.sessionTimeout = (timeout == 0) ? -1 : timeout; support.firePropertyChange("sessionTimeout", oldSessionTimeout, this.sessionTimeout); }
这个maxInactiveInterval的来源:web.xml配置session-timeout(单位是分钟)——StandardContext.setSessionTimeout()——StandardManager.setMaxInactiveInterval()(单位是秒)——StandardSession.setMaxInactiveInterval()——StandardSession.inValid()。
相关文章推荐
- [Tomcat6.0源码]Servlet API之Request
- [Tomcat6.0源码]Servlet API之ServletConfig、ServletContext
- Tomcat源码阅读之Cookie和Session
- tomcat7 启动项目报错 java.lang.NoSuchMethodError: javax.servlet.ServletContext.getSessionCookieConfig()
- Tomcat7.0 使用以前的jre的时候报错:javax.servlet.ServletContext.getSessionCookieConfig
- 【原】tomcat 7 启动报错:java.lang.NoSuchMethodError: javax.servlet.ServletContext.getSessionCookieConfig()Ljavax/servlet/SessionCookieConfig的解决
- Servlet第六篇【Session介绍、API、生命周期、应用、与Cookie区别】
- spring MVC 对servletAPI支持(request,response,cookie,session)
- Servlet-Cookie源码分析 源码环境:Tomcat8
- ServletAPI --- session cookie接口 会话
- tomcat 7.x 报错javax.servlet.ServletContext.getSessionCookieConfig
- Tomcat7.0 使用以前的jre的时候报错:javax.servlet.ServletContext.getSessionCookieConfig
- Servlet Session 与 Cookie
- tomcat源码解读六 tomcat中的session生命历程
- Servlet容器Tomcat中web.xml中url-pattern的配置详解[附带源码分析]
- IDEA添加项目依赖(将Tomcat中的servlet-api.jar添加到项目中去)
- jsp ,servlet中session ,Cookie 相关知识
- java之辨析Session Cookie Servletcontext
- Servlet源码解析:Session、Request以及Response
- jdk6.0 Tomcat6.0下jsp、servlet和javabean的 手动配置