servlets chapter 7 session tracking. http is stateless but web applications are not. we need to know...
Post on 21-Dec-2015
220 views
TRANSCRIPT
Servlets Chapter 7
Session Tracking
http is stateless
• But web applications are not.• We need to know information about order items, eg., in
order to continue processing this client’s session.• Consider the shopping cart: you order something,
browse elsewhere, then return to pay. Or it is an online chat situation where you are a featured guest: Someone asks you a question and if you ask a question in return, you don’t know who/when the answer is returned.
• (Ajax and other technologies have mitigated this issue somewhat.)
Sessions may be tracked with
1. User authentication
2. Hidden form fields
3. url rewriting
4. Persistent cookies
getRemoteUser()
• We can get the user’s name and even pass it between servlet’s.
• We can use it to implement user authentication (discussed in chapter 8)
Client introductions• The cliet needs to introduce himself each time he interacts with the
server. A unique identifier is needed.• Servlets can do traditional CGI session tracking. And there is built in
session tracking support as well.• User authentication (log in) allows servlet to call getRemoteUser()
method to see who this is. The browser sends login (name/pw) info each time it requests a page. This way, even different servlets could handle the shopping cart:
• For example: addItemsToCart(name,items(i))• And later: String items[]=getItemsFromCart(name);• Chapter 8 (Security) explains how to protect pages with user
authentication and this, along with getRemoteUser() is one solution to the problem.
User authentication
• If the user has submitted login information then the name/pw is passed each time the viewer views a new page.
• We can add items to the user’s cart with code like
addItems(name,items);• And another servlet can retrieve them with code
like
String []items=getItems(name);
Hidden html form fields: Another way to track session
• Hidden form fields enable us to implement anonymous session tracking.
• Hidden fields are not displayed to the client on the browser but are returned to the server.
• Hidden form fields provide constant form data but to the servlet retrieving the parameters, there is no difference.
A form with hidden fields
<FORM Method=GET Action="http://localhost:8080/servlet/ShoppingCart">
item1 <input type=text name="item"><p>item2<input type=text name="item"><p>item3<input type=text name="item"><p>item4<input type=text name="item"><p>
<input type=hidden name="secret" value ="1234"><p>
other user info <input type=text name="userinfo"><p><input type =submit VALUE=" Place Order "></form>
Form in IE
Hunter’s shopping cart viewer hidden
• Uses hidden form fields to allow anonymous shopping until checkout time.
• User can add more or check out at any time.
• The currently selected cart items are (re-) written to the form as hidden fields each time it is presented to the client.
servlet public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setContentType("text/html"); PrintWriter out = res.getWriter();
out.println("<HEAD><TITLE>Current Shopping Cart Items</TITLE></HEAD>"); out.println("<BODY>");
// Cart items are passed in as the item parameter. String[] items = req.getParameterValues("item"); // Print the current cart items. out.println("You currently have the following items in your cart:<BR>"); if (items == null) { out.println("<B>None</B>"); } else { out.println("<UL>"); for (int i = 0; i < items.length; i++) { if(items[i].length()>0)out.println("<LI>" + items[i]); } out.println("</UL>");
Servlet 2nd slide// Ask if the user wants to add more items or check out. // Include the current items as hidden fields so they'll be passed on. out.println("<FORM ACTION=\"/servlet/ShoppingCart\" METHOD=POST>"); if (items != null) { for (int i = 0; i < items.length; i++) { out.println("<INPUT TYPE=HIDDEN NAME=\"item\" VALUE=\"" + items[i] + "\">"); } } out.println("Would you like to<BR>"); out.println("<INPUT TYPE=SUBMIT VALUE=\" Add More Items \">"); out.println("<INPUT TYPE=SUBMIT VALUE=\" Check Out \">"); out.println("</FORM>");
out.println("</BODY></HTML>"); }
The servlets to go to for submits don’t exist, as yet
About this example• The items are hidden in the form returned
to the user.• The links to a “checkout” servlet are not
valid.• As more and more stuff is in the cart, it is
tedious to write it all as hidden form data. A unique session ID is a better way to associate a client with the data. Session ids must be protected and generated carefully.
Hidden fields
• Hidden fields are simple
• Supported by all browsers.
• A session id can be a hidden form field.
• Session id can only be maintained through dynamic form generation, not static pages.
Saving session information by URL rewriting
• In this method, local URLs (passing between pages on the same server) have some information glued to the end, retrievable as path info.
• The extra info is usually limited to a session id or some parameters.
• Servlets will have to be customized to look for and process this.
• All servers can handle extra path information but it may interfere with servlets that need true path information.
• Added parameters works on all servers, but parameter collision may occur.
• Several ways to add the information to the path.
• Passing session id 123 might look like #2-#4 below:
1. http://someurl:someport/ShoppingCart2. http://someurl:someport/ShoppingCart/1233. http://someurl:someport/ShoppingCart/sessionid=1234. http://someurl:someport/ShoppingCartjsessionid=123
URL rewriting
Saving session information by URL rewriting
URL rewriting (select add more)
URL rewriting (select help)
Remarks• I didn’t really create an interface between the
two servlets (ShoppingCart and ShoppingCartRewrite) to pass the items parameters or the mechanism to look up the items given the sessionid (like a Hashtable visible to both servlets)
• I stubbed in a minimal Help servlet… Note that the Help servlet could use session info to provide help to a specific situation.
• Mappings will need to be /theServelt/* so that session Id can be passed as pathInfo for ShoppingCartRewrite and Help
ShoppingCartRewritepublic void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setContentType("text/html"); PrintWriter out = res.getWriter(); out.println("<HEAD><TITLE>Current Shopping Cart Items</TITLE></HEAD>"); out.println("<BODY>"); // Get the current session ID, or generate one if necessary String sessionid = req.getPathInfo(); if (sessionid == null) { sessionid = generateSessionId(); } // Cart items are associated with the session ID String[] items = getItemsFromCart(sessionid); // Print the current cart items. out.println("You currently have the following items in your cart:<BR>"); if (items == null) { out.println("<B>None</B>"); } else { out.println("<UL>"); for (int i = 0; i < items.length; i++) { out.println("<LI>" + items[i]); } out.println("</UL>"); }
ShoppingCartRewrite// Ask if the user wants to add more items or check out. // Include the session ID in the action URL. out.println("<FORM
ACTION=\"http://csci345.oneonta.edu:8080/myexamples/ShoppingCart/" + sessionid +
"\" METHOD=POST>"); out.println("Would you like to<BR>"); out.println("<INPUT TYPE=SUBMIT VALUE=\" Add More Items \">"); out.println("<INPUT TYPE=SUBMIT VALUE=\" Check Out \">"); out.println("</FORM>"); // Offer a help page. Include the session ID in the URL. out.println("For help, click <A
HREF=\"http://csci345.oneonta.edu:8080/myexamples/Help/" + sessionid + "?topic=ShoppingCartRewrite\">here</A>"); out.println("</BODY></HTML>"); }
private static String generateSessionId() { String uid = new java.rmi.server.UID().toString(); // guaranteed unique return java.net.URLEncoder.encode(uid); // encode any special chars }
private static String[] getItemsFromCart(String sessionid) {//stubbed this in too String[]cart={"mo","larry","curly","shemp"}; return cart; }}
Persistent Cookies
• A cookie is information sent by a server to a browser which can later be read back from the browser.
• A browser returns the cookie each time it returns to a given server subject to certain rules.
• The cookie can uniquely identify a client and so can be used for session tracking.
• Cookies are not part of the http standard but may be included in the next rewrite. They are part of the de facto toolkit.
Persistent Cookies• The servlet cookie class is provided to
manipulate cookies.• The constructor: public Cookie(String
name,String value) builds a cookie.• Legal name/values for cookies appear in the
Netscape cookie specification RFC 2109• Cookie c=new Cookie(“id”,123456);//set
cookie• Passing a cookie object to the addCookie
method (of the response) sends a cookie to the client.
Persistent Cookies
• A servlet retrieves an array of cookies with request.getCookies() method.
• Name/value pairs can be accessed from the entries in this array
• but there is no method to get a value of a cookie given its name.
• Other attributes like version, domain, max age and path can be set.
Cookie methods• If you wish to set an attribute on a cookie from the client you will have to add
the cookie back to the response to effect the change.• Most attributes (except name, value and version) on cookies will need to be
reset each time.• There are two versions, 0 and 1.• setDomain specifies servers that should see the cookie. Default is to return
a cookie only to the host that set them. The domain pattern begins with a dot and contains at least two dots. The pattern is matched only to domains that match one entry beyond the initial dot. So foo.com matches www.foo.com and upload.foo.com but not www.upload.foo.com
• setMaxAge specifies how long to keep the cookie (in seconds) before expiration. A negative value is the default, so cookies expire when the browser exits. A zero value is also deleted immediately.
• setPath sets URIs to which cookies can be sent. The default is the page that set the cookie and all pages in the same directory or under that directory. Path of / would mean any page on the server would get the cookie.
• setSecure (t/f) indicates if the cookie should only be sent over a secure channel.
• setComment sets a comment field.• setValue sets a new cookie value
Cookie methods
• setMaxAge() specifies how long to keep the cookie (in seconds) before expiration. A negative value is the default, so cookies expire when the browser exits. A zero value is also deleted immediately.
• setPath() sets URIs to which cookies can be sent. The default is the page that set the cookie and all pages in the same directory or under that directory. Path of / would mean any page on the server would get the cookie.
• setSecure (t/f) indicates if the cookie should only be sent over a secure channel.
• setComment() sets a comment field.• setValue() sets a new cookie value
Shopping revisited via persistent cookies
Shopping with persistent cookies: after reload
ShoppingCartWithCookiepublic void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setContentType("text/html"); PrintWriter out = res.getWriter();boolean idfromcookie=false;int num=0; // Get the current session ID by searching the received cookies. String sessionid = null; String comment=null; Cookie[] cookies = req.getCookies(); if (cookies != null) {
num=cookies.length; for (int i = 0; i < cookies.length; i++) { if (cookies[i].getName().equals("sessionid")) { sessionid = cookies[i].getValue(); idfromcookie=true;cookies[i].setMaxAge(30);//30 second cookie res.addCookie(cookies[i]); break; } } } // If the session ID wasn't sent, generate one. // Then be sure to send it to the client with the response. if (!idfromcookie) { sessionid = generateSessionId(); Cookie c = new Cookie("sessionid", sessionid);c.setMaxAge(30); res.addCookie(c); }//rest is the same a ShoppingCartSessionIdRewrite
SessionTracking API
• Servlet API has several classes and methods to handle short term session tracking.
• Some servers support session tracking via cookies, URL rewriting, or a database or file that holds session info after shutdown. (These classes must be serializable.)
• Text chapter 12 discusses distributed session tracking.
SessionTracking API
• Every site user is associated with a javax.servlet.http.HttpSession object that a servlet can use to store arbitrary information.
• This object can hold an array of items from a shopping cart or a database connection.
• The request.getSession(boolean create) retrieves the HttpSession object. It will create one if the parameter is set to true, if none exits. It will return null if there is none and create is set to false. (There is a no-argument version that has a flag set to true to create the session).
SessionTracking API• getSession() must be called at least once
before commiting the response.• HttpSession.setAttribute(String name,
Object value) allows you to add data to the session. A getAttribute method allows retrieval of objects associated with the session.
• HttpSession.getAttribut(String name) retrieves the attribute for a particular name.
SessionTracking API
• getAttributeNames() returns an enumeration of all objects bound to this session as Strings, or an empty enumeration.
• Void method removeAttribute(name) allows you to remove an attribute with given name from association with the session. Servlet API before 2.2 used “value” instead of “attribute” and those methods are still supported but are deprecated.
SessionInfo servlet (counts hits)
Compiling the SessionTracker
• 'enum' is a keyword in jdk1.5+, and may not be used as an identifier
• C:\Program Files\Java\jdk1.5.0_07\bin\SessionTracker.java:35: as of release 1.5, 'enum' is a keyword, and may not be used as an identifier
• (try -source 1.4 or lower to use 'enum' as an identifier)• Enumeration enum = session.getAttributeNames();• ^
the SessionTracker servletpublic void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setContentType("text/html"); PrintWriter out = res.getWriter(); // Get the current session object, create one if necessary HttpSession session = req.getSession(); // Increment the hit count for this page. The value is saved // in this client's session under the name "tracker.count". Integer count = (Integer)session.getAttribute("tracker.count"); if (count == null) count = new Integer(1); else count = new Integer(count.intValue() + 1); session.setAttribute("tracker.count", count); out.println("<HTML><HEAD><TITLE>SessionTracker</TITLE></HEAD>"); out.println("<BODY><H1>Session Tracking Demo</H1>"); // Display the hit count for this page out.println("You've visited this page " + count + ((count.intValue() == 1) ? " time." : " times.")); out.println("<P>"); out.println("<H2>Here is your session data:</H2>"); Enumeration list = session.getAttributeNames(); while (list.hasMoreElements()) { String name = (String) list.nextElement(); out.println(name + ": " + session.getAttribute(name) + "<BR>"); } out.println("</BODY></HTML>"); }
More
• I redid this servlet to keep adding stuff to the session, willy-nilly, but somewhat like a shopping cart might do
A sample form
After several items and several credit cards and addresses
Here is internal info about the different session values
The form uses post public void doGet(HttpServletRequest request, HttpServletResponse res)
throws ServletException, IOException { res.setContentType("text/html"); PrintWriter out = res.getWriter(); // Get the current session object, create one if necessary HttpSession session = request.getSession(true); Enumeration paramNames = request.getParameterNames(); while(paramNames.hasMoreElements()) { String paramName = (String)paramNames.nextElement(); out.print("<TR><TD>" + paramName + "\n<TD>"); String[] paramValues = request.getParameterValues(paramName); if (paramValues.length == 1) { String paramValue = paramValues[0];//assumed there was just one of each name out.println(paramValue); String x=(String)session.getAttribute(paramName);//get any attribute with this name session.setAttribute(paramName,x+", "+paramValue);//glue more stuff to this attribute…array would be better }//if }//for out.println("<HTML><HEAD><TITLE>session info</TITLE></HEAD>"); out.println("<BODY><H1>Session Tracking Demo</H1>"); out.println("<P>"); out.println("<H2>Here is your session data:</H2>"); Enumeration list = session.getAttributeNames();//dump everything to view each time while (list.hasMoreElements()) { String name = (String) list.nextElement(); out.println(name + ": " + session.getAttribute(name) + "<BR>"); }//while out.println("</BODY></HTML>");
}//doGet public void doPost(HttpServletRequest request, HttpServletResponse res)
throws ServletException, IOException { doGet(request, res);}
Session lifecycle• Sessions expire automatically, or – on Tomcat, by
default - after 30 minutes.• The servlet can invalidate a session at any time.• Any information saved in the user’s session object is lost
when the session is invalidated.• Information needed beyond the timeout period should be
saved in a database.• Methods for saving client information include providing a
cookie that can last for years, requiring a login, or rewriting the URL to provide a “bookmark” as covered in the first part of this ppt.
• Yahoo handles this via a login/pw and a persistent cookie. Even though the user is known via the cookie, Yahoo still requires a pw to read mail.
Session lifecycle: timeout
• In web.xml you can set session timeout:<web-app>…<session-config><session-timeout>60 <!– in minutes--></session-timeout></session-config></web-app>
Timeout
• If timeout is set in session-config then the user is timed out after 60 minutes pass without any request being made.
• The session object has a method setMaxInactiveInterval(int secs) to set session timeout.
• Timeout value can be retrieved form the session object via getMaxInactiveInterval()
• This method can also tell you the default timeout on a server if session-config hasn’t set a timeout.
Original view of sessiontimer
After reloading
A compromise between security, convenience, and scalability
• Server can destroy session-related objects when a session times out.
• Security decreases as timeout period is extended.
• Convenience increases with timeout length.
Lifecycle methods
• HttpSession.isNew()- server creates session
• HttpSession.invalidate()- session immediately invalidated
• HttpSession.getCreationTime()- a long int (from Jan 1, 1970) time at which session was created.
A servlet to invalidate a stale session: I didn’t implement this
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setContentType("text/html"); PrintWriter out = res.getWriter(); // Get the current session object, create one if necessary HttpSession session = req.getSession(); // Invalidate the session if it's more than a day old or has been // inactive for more than an hour. if (!session.isNew()) { // skip new sessions Date dayAgo = new Date(System.currentTimeMillis() - 24*60*60*1000); Date hourAgo = new Date(System.currentTimeMillis() - 60*60*1000); Date created = new Date(session.getCreationTime()); Date accessed = new Date(session.getLastAccessedTime());
if (created.before(dayAgo) || accessed.before(hourAgo)) { session.invalidate(); session = req.getSession(); // get a new session } } // Continue processing... }
Handling session data in applets
• Applets do not participate in the server session data although it may be passed to the client browser as a a session id cookie (JSESSIONID)
• The server can pass this to the applet as a normal parameter on the html page that generates the applet and then the applet can pass it back to the servlet as an artificially created session id cookie. This is discussed further in servlet-applet communication (Chapter 10).
Session tracking without cookies
• The servlet specification requires web servers to support session tracking even for browsers that don’t support cookies.
• Two methods help in URL rewriting to implement this tracking:
• Response.encodeURL(String URL) – attaches the session id to the URL and returns it or leaves it unchanged if encoding is not supported.
• Response.encodeRedirectURL(String URL)-similar.• URLs passed to the sendRedirect() method should be
passed through this method.
SessionSnoop servlet combines many methods discussed
SessionSnoop (as previously, change enum)public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setContentType("text/html"); PrintWriter out = res.getWriter(); // Get the current session object, create one if necessary HttpSession session = req.getSession(); // Increment the hit count for this page. The value is saved // in this client's session under the name "snoop.count". Integer count = (Integer)session.getAttribute("snoop.count"); if (count == null) count = new Integer(1); else count = new Integer(count.intValue() + 1); session.setAttribute("snoop.count", count); out.println("<HTML><HEAD><TITLE>SessionSnoop</TITLE></HEAD>"); out.println("<BODY><H1>Session Snoop</H1>"); // Display the hit count for this page out.println("You've visited this page " + count + ((count.intValue() == 1) ? " time." : " times.")); out.println("<P>"); out.println("<H3>Here is your saved session data:</H3>"); Enumeration list = session.getAttributeNames(); while (list.hasMoreElements()) { String name = (String) list.nextElement(); out.println(name + ": " + session.getAttribute(name) + "<BR>"); }
SessionSnoop: continuedout.println("<H3>Here are some vital stats on your session:</H3>"); out.println("Session id: " + session.getId() + " <I>(keep it secret)</I><BR>"); out.println("New session: " + session.isNew() + "<BR>"); out.println("Timeout: " + session.getMaxInactiveInterval()); out.println("<I>(" + session.getMaxInactiveInterval() / 60 + " minutes)</I><BR>"); out.println("Creation time: " + session.getCreationTime()); out.println("<I>(" + new Date(session.getCreationTime()) + ")</I><BR>"); out.println("Last access time: " + session.getLastAccessedTime()); out.println("<I>(" + new Date(session.getLastAccessedTime()) + ")</I><BR>");
out.println("Requested session ID from cookie: " + req.isRequestedSessionIdFromCookie() + "<BR>"); out.println("Requested session ID from URL: " + req.isRequestedSessionIdFromURL() + "<BR>"); out.println("Requested session ID valid: " + req.isRequestedSessionIdValid() + "<BR>");
out.println("<H3>Test URL Rewriting</H3>"); out.println("Click <A HREF=\"" + res.encodeURL(req.getRequestURI()) + "\">here</A>"); out.println("to test that session tracking works via URL"); out.println("rewriting even when cookies aren't supported.");
out.println("</BODY></HTML>"); }
SessionBinding• Some objects may need to perform an action when they are bound
or unbound from a session. Example would be a database which might start a transaction when bound and end the transaction when unbound.
• Any object implementing the interface HttpSessionBindingListener is notified when it is bound/unbound from a session.
• The interface has two methods, valueBound(HttpSessionBindingEvent) and valueUnbound(HttpSessionBindingEvent)
• The HttpSessionBindingEvent argument provides access to the name under which the object is bound via public String HttpSessionBindingEvent.getName() method.
• A getSession() method provides access to the session under which the object is beingh bound or unbound.
• The servlet example uses this to put an entry in a log file when it is bound or unbound from a session
SessionBindingimport java.io.*;import java.util.*;import javax.servlet.*;import javax.servlet.http.*;public class SessionBindings extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setContentType("text/plain"); PrintWriter out = res.getWriter(); // Get the current session object, create one if necessary HttpSession session = req.getSession(); // Add a CustomBindingListener session.setAttribute("bindings.listener", new CustomBindingListener(getServletContext())); out.println("This page intentionally left blank"); }}class CustomBindingListener implements HttpSessionBindingListener { // Save a ServletContext to be used for its log() method ServletContext context; public CustomBindingListener(ServletContext context) { this.context = context; } public void valueBound(HttpSessionBindingEvent event) { context.log("[" + new Date() + "] BOUND as " + event.getName() + " to " + event.getSession().getId()); } public void valueUnbound(HttpSessionBindingEvent event) { context.log("[" + new Date() + "] UNBOUND as " + event.getName() + " from " + event.getSession().getId()); }}
SessionBinding
SessionBinding (look at log file)
• INFO: [Wed Oct 19 14:37:00 EDT 2005] BOUND as bindings.listener to 7987D28F15FD07E0994D82F5A7A619C1
• INFO: [Wed Oct 19 14:40:00 EDT 2005] UNBOUND as bindings.listener from 7987D28F15FD07E0994D82F5A7A619C1
Revisiting shopping with session trackingpublic class ShoppingCartWithSession extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setContentType("text/html"); PrintWriter out = res.getWriter(); // Get the current session object, create one if necessary. HttpSession session = req.getSession(); // Cart items are maintained in the session object. String[] items = (String[])session.getAttribute("cart.items"); out.println("<HTML><HEAD><TITLE>SessionTracker</TITLE></HEAD>"); out.println("<BODY><Session Tracking Demo</H1>"); // Print the current cart items. out.println("You currently have the following items in your cart:<BR>"); if (items == null) { out.println("<B>None</B>"); } else { out.println("<UL>"); for (int i = 0; i < items.length; i++) { out.println("<LI>" + items[i]); } out.println("</UL>"); }
continued// Ask if they want to add more items or check out.
out.println("<FORM ACTION=\"" + res.encodeURL("http://csci345.oneonta.edu:8080/myexamples/ShoppingCart") + "\" METHOD=POST>"); out.println("Would you like to<BR>"); out.println("<INPUT TYPE=SUBMIT VALUE=\" Add More Items \">"); out.println("<INPUT TYPE=SUBMIT VALUE=\" Check Out \">"); out.println("</FORM>");
// Offer a help page. Encode it as necessary. out.println("For help, click <A HREF=\"" + res.encodeURL("http://csci345.oneonta.edu:8080/myexamples/Help?topic=ShoppingCartWithSession") + "\">here</A>");
out.println("</BODY></HTML>"); }}
Revisiting shopping with session tracking
Click add button
Help button
• My help servlet doesn’t do anything with the parameter (servlet name) being passed in.