Overview
- HTTP is a stateless protocol
- To keep JAVA object available longer than the duration of a HTTP request each Object is stored within the HTTP session object. This is called Session tracking feature.
- Typical technical solutions for Session Tracking are
- Cookies
- URL rewriting
Hidden form fields.
Why and how should you protect your HTTP session object
- HttpSession object is not thread safe
- Browser Tabbing, Fast Page Reloading and async. AJAX request may give you concurrent access to the same HttpSession object
- There is no guarantee that multiple calls to HttpServletRequest.getSession() will return the same HttpSession object .
- Don’t synchronize on HttpSession object as this object may be recreated by certain containers
- Synchronize on immutable object returned by : session.getId().intern()
Reference
Creating a HTTP session using cookies
JSESSIONID cookie is created/sent when session is created. Session is created when your code calls request.getSession()
or request.getSession(true) for the first time. If you just want get session, but not create it if it doesn't exists,
use request.getSession(false) -- this will return you a session or null. In this case, new session is not created,
and JSESSIONID cookie is not sent. (This also means that session isn't necessarily created on first request... you
and your code is in control when the session is created)
Understanding Browser Session and HTTP Session Object
Using Browser tabbing with firefox Start a first Browser session and run their initial HTTP POST to create the HTTP session by running timeoutTest() : Session 1: $ firefox http://localhost:8180/WFJPA2EL-1.0/ 12:56:42.530 timeoutTest() - NEW Session - ID zjiDVDLbFkQYhJ4bARWTmP6P - Access Count: 0- i JSESSIONID cookie: zjiDVDLbFkQYhJ4bARWTmP6P.wls1 - MaxIncativeInterval: 10 - Last AccessedTime: 12:56:34.636 - Cookie MaxAge: -1 Now start as new Browser Tab using the same URL and run the HTTP Post request again Session 2: $ firefox http://localhost:8180/WFJPA2EL-1.0/ 12:56:47.856 timeoutTest() - WELCOME Back Session - ID zjiDVDLbFkQYhJ4bARWTmP6P - Access Count: 1- JSESSIONID cookie: zjiDVDLbFkQYhJ4bARWTmP6P.wls1 - MaxIncativeInterval: 10 - Last AccessedTime: 12:56:42.547 - Cookie MaxAge: -1 - The second request doesn't create a new HTTP session object - Instead browser session are uing the HTTP Session object - Thats is why you should serialize access to the HTTP session object as multiple threads may use this JAVA object ! Using a different Firefox profile or different Host name will create different HTTP Objects: Session 1: $ firefox http://localhost:8180/WFJPA2EL-1.0/ 10:06:59.096 timeoutTest() - NEW Session - ID O3tl4IqIDCAGWB16O52HO6h6 - Access Count: 0- JSESSIONID cookie: O3tl4IqIDCAGWB16O52HO6h6.wls1 - MaxIncativeInterval: 10 - Last AccessedTime: 10:06:53.374 - Cookie MaxAge: -1 Session 2: $ firefox http://wls1:8180/WFJPA2EL-1.0/ 10:07:42.817 timeoutTest() - NEW Session - ID 6kqRcL-DDpKCywOqrq3wo8yB - Access Count: 0- JSESSIONID cookie: 6kqRcL-DDpKCywOqrq3wo8yB.wls1 - MaxIncativeInterval: 10 - Last AccessedTime: 10:07:37.164 - Cookie MaxAge: -1 - Using a different hostname [ or Firefox Profiles ] will create a new HTTP session object !
HTTP session object details
- A HTTP Session objects stores data about Cookies and Attributes
- The JSESSIONID is the cookie for implementing the Session Feature
- The accessCount attribute tracks how often this session is reused by an HTTP request
10:19:27.673 timeoutTest() - WELCOME Back Session - ID fwdaf1VInpfsA-20853SiT_F - Access Count: 1- JSESSIONID cookie: fwdaf1VInpfsA-20853SiT_F.wls1 - MaxIncativeInterval: 10 - Last AccessedTime: 10:19:23.205 - Cookie MaxAge: -1 10:19:27.674 attr = accessCount value = 1 10:19:27.675 Cookie name = JSESSIONID value = fwdaf1VInpfsA-20853SiT_F.wls1 Cookie MaxAge = -1 Details: - Cookie MaxAge = -1 -> No Cookie Timeout - MaxIncativeInterval: 10 -> HTTP session timeout [ 10 seconds ] - accsessCount -> HTTP object stored within HTTP session object
Java Code to display HTTP session details and set and read attributes
public void trackSession(HttpSession session, String methodName) { final Object lock = session.getId().intern(); synchronized(lock) { String heading = null; accessCount = (Integer)session.getAttribute("accessCount"); if (accessCount == null) { accessCount = new Integer(0); heading = methodName + " - NEW Session"; } else { heading = methodName + " - WELCOME Back Session "; accessCount = new Integer(accessCount.intValue() + 1); } String jSessionId = "JSESSIONID not found"; int jSessionMaxAge = 0; HttpServletRequest req = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest(); // There no API to return JESSIONID cookie - we need to loop throught the Cookie Arrary Cookie[] cookies = req.getCookies(); for(Cookie cookie : cookies) { if("JSESSIONID".equals(cookie.getName())) { jSessionId = cookie.getValue(); jSessionMaxAge = cookie.getMaxAge(); } } session.setAttribute("accessCount", accessCount); setSessionInfo(heading + " - ID " + session.getId() + " - Access Count: " + accessCount.intValue() + "- JSESSIONID cookie: " + jSessionId + " - MaxIncativeInterval: " + session.getMaxInactiveInterval() + " - Last AccessedTime: " + Tools.getTime2(session.getLastAccessedTime()) + " - Cookie MaxAge: " + jSessionMaxAge); // display Session details : Cookies and if (displaySessionDetails ) { Enumeration es = session.getAttributeNames(); while (es.hasMoreElements()) { String attr = (String)es.nextElement(); Object value = session.getValue(attr); setSessionInfo(" attr = "+ attr +" value = "+ value); } for (Cookie cookie : cookies) { String cookieName = cookie.getName(); String cookieValue = cookie.getValue(); setSessionInfo(" Cookie name = "+ cookieName+" value = "+ cookieValue + " Cookie MaxAge = " + cookie.getMaxAge()); } } } }
How to deal with HTTP Session Timeout – a JSF sample
- For security and memory management, sessions need to be invalidated at a certain time
There are two related methods in HttpSession. - HttpSession.invalidate() By invoking invalidate(), the session will be invalidated immediately. This is useful for the case such as logout. - HttpSession.setMaxInactiveInterval(int interval) The method setMaxInactiveInterval(int interval) allows us to configure the time (in seconds) between client requests before the servlet container will invalidate the session. That is, an idle session will be invalidated after the specified time. See https://weblogs.java.net/blog/swchan2/archive/2013/08/29/when-httpsession-invalidated HTTP session timemout can be configured via web.xml web.xml sample <session-config> <session-timeout> 30 </session-timeout> </session-config> setMaxInactiveInterval() sample HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(true); if ( session == null) { throw new IllegalArgumentException(methodName+ ": Could not get HTTP session : "); } session.setMaxInactiveInterval(5); Note setMaxInactiveInterval configures the time (in seconds) between client requests and before the servlet container will invalidate the session. After your initial HTTP GET request wait 5 seconds and send a HTTP POST request. This HTTP POST request will fail with: javax.faces.application.ViewExpiredException: viewId:/index.xhtml - View /index.xhtml could not be restored. at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:210) at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101) at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:121) at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198) at javax.faces.webapp.FacesServlet.service(FacesServlet.java:646) at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85) Fix : You may Add an error page to your web.xml file <error-page> <exception-type>javax.faces.application.ViewExpiredException</exception-type> <location>/faces/index.xhtml</location> </error-page>
Further details on Thread Safety
Never assign any request or session scoped data as an instance variable of a servlet or filter. It will be shared among all other requests in other sessions. That's threadunsafe! The below example illustrates that: public class ExampleServlet extends HttpServlet { private Object thisIsNOTThreadSafe; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Object thisIsThreadSafe; thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests! thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe. } }
Reference
- Synchronizing the HttpSession
- Java theory and practice: Are all stateful Web applications broken?
- HTTP Session Tracking
- Httpsession is applicable for per user or per browser?
- HTTP Session
- Dealing Gracefully with ViewExpiredException in JSF2