How do we Maintain Sessions?

 < Free Open Study > 



In the previous section, we saw that there are many situations in which we need to keep track of state. Essentially this meant that we must preserve the identity of the client and the data associated with the client across requests. Since HTTP is a stateless protocol, just how do web applications manage to keep track of users?

In the following sections, we will discuss various mechanisms that we can use to maintain state across requests. We will consider the following approaches:

  • URL rewriting

  • Cookies

  • Hidden-form fields

These techniques are used to maintain sessions in not only Java-based applications, but web applications written in many other languages too. Out of these three, the most common approach is to use cookies.

The Java Servlet API provides a way of maintaining sessions via two of these techniques: cookies and URL rewriting. We will examine the session-related interfaces and classes from the API later too.

Session handling is based on a simple idea that two entities can identify each other by exchanging some token (a unique identifier that both entities recognize) with each message. For instance, if your name is "Bob", and the name "Bob" is unique as far as the server is concerned, you may send the token "Bob" with each request to the server to uniquely identify you. Of course, this token need not be your name; it can be any piece of data that uniquely identifies you. The idea behind all session handling techniques is the same: they all rely on exchanging a server-generated unique ID with each request.

Let's begin by discussing the URL rewriting approach to session tracking.

Session Tracking Using URL Rewriting

URL rewriting is based on the idea of embedding a unique ID (generated by the server) in each URL of the response from the server. That is, while generating the response to the first request, the server embeds this ID in each URL. When the client submits a request to one such URL, the browser sends this ID back to the server. The server can therefore identify the ID with all requests. Let's examine this approach with the aid of a simple servlet. This servlet does the following:

  • Checks to see if the client sent any token with its request

  • If no token was sent, a new one is created

  • Provides two links back to the servlet - one including the token, and one not

Here's the source code for the TokenServlet:

     package sessions;     import java.io.*;     import java.util.Random;     import javax.servlet.http.*;     import javax.servlet.ServletException;     public class TokenServlet extends HttpServlet {       protected void doGet(HttpServletRequest request, HttpServletResponse response)           throws ServletException, IOException { 

First we get the token from the request:

         String tokenID = request.getParameter("tokenID"); 

Then we prepare the response:

         response.setContentType("text/html");         PrintWriter writer = response.getWriter();         writer.println("<html><head><title>Tokens</title></head><body ");         writer.println("style=\"font-family:verdana;font-size:10pt\">"); 

If the client did not send any token we create a new one:

         if(tokenID == null) {           Random rand = new Random();           tokenID = Long.toString(rand.nextLong());           writer.println("<p>Welcome. A new token " +                          tokenID + " is now established</p>");         } else { 

If the client sent a token then we acknowledge the client:

           writer.println("<p>Welcome back. Your token is " + tokenID + ".</p>");         } 

Then we prepare the links for sending requests back:

        String requestURLSame = request.getRequestURL().toString() +                                "?token=" + tokenID;        String requestURLNew = request.getRequestURL().toString(); 

Finally, we write the response and close the connection:

         writer.println("<p>Click <a href=" + requestURLSame +                        ">here</a> again to continue browsing with the " +                        "same identity.</p>");         writer.println("<p>Otherwise, click <a href=" + requestURLNew +                        ">here</a> again to start browsing with a new identity.</p>");         writer.close();       }     } 

Create a web application called token, compile the source code above, and add the class to the WEB-INF/class/sessions directory. Then create the following simple deployment descriptor for the web application:

     <?xml version="1.0"?>     <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"                              "http://java.sun.com/dtd/web-app_2_3.dtd">     <web-app>       <servlet>         <servlet-name>track</servlet-name>         <servlet-class>sessions.TokenServlet</servlet-class>       </servlet>       <servlet-mapping>         <servlet-name>track</servlet-name>         <url-pattern>/track/*</url-pattern>       </servlet-mapping>     </web-app> 

Deploy the web application, restart Tomcat, and navigate to http://localhost:8080/token/track. You should see something like:

click to expand

The initial request http://localhost:8080/token/track does not include the query parameter tokenID. The servlet creates a new token, and generates two links. The first link includes a query string while the second link doesn't. If you click on the first link, you will see the following page:

click to expand

Since there is a query parameter in the request, the servlet recognizes the user from this parameter, and displays the Welcome back message. If you click instead on the second link, the browser displays a page with a different token:

click to expand

Although this technique can solve the problem of session tracking, it has two important limitations:

  • Since the token is visible in the URL during a session, these sessions will not be very secure

  • Since links in static pages are hard-wired, they can't be dynamically changed for every user, so we can only use this system with servlets or other dynamic pages

An alternative is to use cookies. As you will see in the following section, cookies eliminate the above limitations.

Session Tracking Using Cookies

Cookies provide a better alternative to explicit URL rewriting, because cookies are not sent as query strings but are exchanged within the bodies of HTTP requests and responses. Since there is no need to rewrite URLs, session handling via cookies does not depend on whether the content is static or dynamic.

All modern browsers can recognize and receive cookies from web servers, and then send them back along with requests. However, there is a limitation with cookies. All browsers allow users to disable this functionality, which leads to browsers not recognizing cookies. This is because cookies have a bad press - they have sometimes been used to gather information about consumers without their knowledge. Given this, it is worth simultaneously supporting an alternative technique such as URL rewriting. We shall see how we can do this with the Java Servlet API.

A cookie is a string sent via the HTTP request and HTTP response headers. A cookie has the following parameters:

Parameter

Description

Name

Name of cookie

Value

A value of the cookie

Comment

A comment explaining the purpose of the cookie

Max-Age

Maximum age of the cookie (a time in seconds after which the client should not send the cookie back to the server)

Domain

Domain to which the cookie should be sent

Path

The path to which the cookie should be sent

Secure

Specifies if the cookie should be sent securely via HTTPS

Version

Version of the cookie protocol. Version 0 is meant for the original Netscape version of cookies. Version 1 is meant for cookies standardized via RFC 2109.

In order to send a cookie to a client, a server creates a cookie header and attaches it to the HTTP response. The client receives the request and extracts the cookie response header. This cookie data is usually stored in a file on the client's system.

When the client makes another request, to help the client decide whether to send the cookie back to the server or not, there are two parameters: Domain and Path. The path is given relative to the domain. For instance, in the URL http://www.myserver.com/shop/top100, myserver.com is the domain, and /shop/top100 is the path. If the Path parameter is not specified, the cookie applies to any path under the specified domain. Whenever the client makes a request to any URL matching the specified path within this domain, the client sends the cookie along with the request as a header. Let's say the response from the server contains links to http://www.myserver.com, http://sales.myserver.com and http://support.myserver.com and the Path parameter has not been set. When the user clicks of any of these links, the browser sends the cookie back, since all these links are under the same myserver.com domain, and any path in this domain is appropriate.

Working With Cookies in Servlets

The Java Servlet API includes a class javax.servlet.http.Cookie that abstracts the notion of a cookie. The javax.servlet.http.HttpServletRequest and javax.servlet.http.HttpServletResponse interfaces provide methods to add cookies to HTTP responses and to retrieve cookies from HTTP requests.

The Cookie class abstracts a cookie. A Cookie instance has the following constructor, which instantiates a cookie instance with the given name and value:

     public Cookie(String name, String value) 

This class has many methods that make working with cookies easier. It has getter and setter methods for all of the cookie parameters, for example:

     public String getName()     public void setName(String name) 

You can use these methods to access or change the name of a cookie. There are similar methods to access or change other parameters (such as path, header, and so on) of a cookie.

In order to set cookies, the javax.servlet.http.HttpServletResponse interface has the following method:

     public void addCookie(Cookie cookie) 

We can call this method as many times as we wish in order to set multiple cookies. To extract all cookies contained in the HTTP request, the javax.servlet.HttpServletRequest interface has this method:

     public Cookie[] getCookies() 

Now we can rewrite our TokenServlet servlet (now called CookieServlet) to track the user using cookies instead of URL rewriting. In this case, the servlet performs the following actions:

  • Checks if there is a cookie contained in the incoming request

  • If there is none, it creates a cookie, and sends it along with the response

  • If there is a cookie, it just displays the value of the cookie

Let's walk through the source code for CookieServlet:

     package sessions;     import ava.io.*;     import java.util.Random;     import javax.servlet.http.*;     import javax.servlet.http.*;     import javax.servlet.ServletException;     public class CookieServlet extends HttpServlet {       protected void doGet(HttpServletRequest request, HttpServletResponse response)                                                throws ServletException, IOException {         Cookie[] cookies = request.getCookies();         Cookie token = null; 

The servlet first retrieves all of the cookies contained in the request object. Provided there are cookies present, the servlet then checks each cookie to see if the name of the cookie is "token":

         if(cookies != null) {           for(int i = 0; i < cookies.length; i++) {             if(cookies[i].getName().equals("token")) {               token = cookies[i];               break;             }           }         }         response.setContentType("text/html");         PrintWriter writer = response.getWriter();         writer.println("<html><head><title>Tokens</title></head><body ");         writer.println("style=\"font-family:verdana;font-size:10pt\">");         String reset = request.getParameter("reset"); 

If a cookie with name "token" is not found, the servlet creates a cookie called "token" and adds it to the response. The servlet creates a random number. The servlet then created a cookie with the following parameters:

  • Name: "token"

  • Value: A random number (converted into a string)

  • Comment: "Token to identify user"

  • Max-Age: -1, indicating that the cookie should be discarded when the browser exits

  • Path: "/cookie/track", so that the browser sends the cookie to only requests under http://localhost:8080/cookie/track.

Note that the following code snippet does not set Domain. As a matter of convention, it defaults to "localhost". If you are deploying this application on a remote machine (server) and accessing it from another machine (client), you need to set the domain name to be that of the server.

         if(token == null || (reset != null && reset.equals("yes"))) {           Random rand = new Random();           long id = rand.nextLong();           writer.println("<p>Welcome. A new token " +                          id + " is now established</p>");           token = new Cookie("token", Long.toString(id));           token.setComment("Token to identify user");           token.setMaxAge(-1);           token.setPath("/cookie/track"); 

The servlet then adds the cookie to the Response object:

           response.addCookie(token);         else { 

In order to facilitate recreating the identity, the servlet also expects a request parameter "reset". If this parameter is sent with a value of "yes", the servlet recreates the cookie as above, so that the client gets a new token. Otherwise, the servlet does not set the cookie, and just prints a message as shown below:

           writer.println("Welcome back. Your token is " + token.getValue() + ".</p>");         }         String requestURLSame = request.getRequestURL().toString();         String requestURLNew = request.getRequestURL() + "?reset=yes";         writer.println("<p>Click <a href=" + requestURLSame +                        ">here</a> again to continue browsing with the " +                        "same identity.</p>");         writer.println("<p>Otherwise, click <a href=" + requestURLNew +                        ">here</a> again to start browsing with a new identity.</p>");         writer.println("</body></html>");         writer.close();       }     } 

Now create a new web application as you did for the URL rewriting example, and call it cookie. Compile the class above and place the class in the cookie/WEB-INF/classes/sessions/ directory.

Create the following simple deployment descriptor for our web application:

     <?xml version="1.0"?>     <!DOCTYPE web-app         PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"         "http://java.sun.com/dtd/web-app_2_3.dtd">     <web-app>       <servlet>         <servlet-name>track</servlet-name>         <servlet-class>CookieServlet</servlet-class>       </servlet>       <servlet-mapping>         <servlet-name>track</servlet-name>         <url-pattern>/track/*</url-pattern>       </servlet-mapping>     </web-app> 

Deploy the web application, restart Tomcat, and navigate to http://localhost:8080/cookies/track. You'll see a page just like the one in the previous example:

click to expand

Click on the first link to get the following page:

click to expand

The token number is the same. The browser sent the cookie back to the server along with the request, and the server recognized it. Notice that the first link (in the second line) is the same as the one you entered in the address bar in the browser.

From the client perspective, there are two differences here from URL rewriting:

  • The token was not included in the query string

  • While displaying the page, the browser received a cookie from the server

If you instead click on the link on the third line, a fresh token will be created, and a new cookie will be set. In order for the server to recreate the token, you should note that there is an additional query parameter passed called "reset", with value "yes".

In order to better understand the role of the domain and path names, in the above example change the path to "/token" as shown below:

             token.setPath("/token"); 

Recompile the servlet, and restart both Tomcat and the browser. This time, the browser does not send the cookie back when you click on the links. This is because, while the cookie is set for path "/token", the links are pointing to "/cookie". The servlet therefore cannot track the user.

Try changing the maximum age to 86400 seconds (1 day). With this setting, even if you restart the browser and Tomcat, the servlet still recognizes the cookie for one day. In this case, the browser stores the cookie locally on the disk.

Session Tracking Using Hidden Form Fields

The third alternative to session handling is via hidden form fields. HTML forms allow fields to be hidden, which means that such fields are not displayed when the form is rendered on the browser. While preparing a page with a form, the server can add one or more hidden fields within the form. When the client submits the form, the browser transfers the values in the hidden fields along with the other visible fields (if any) to the server. We can use this mechanism to track a user. The following HiddenFieldServlet illustrates this point:

     package sessions;     import java.io.*;     import java.util.Random;     import javax.servlet.http.*;     import javax.servlet.ServletException;     public class HiddenFieldServlet extends HttpServlet {       protected void doGet(HttpServletRequest request, HttpServletResponse response)                                               throws ServletException, IOException { 

First we get the token from the request:

         String token = request.getParameter("token"); 

Then we prepare the response:

         response.setContentType("text/html");         PrintWriter writer = response.getWriter();         writer.println("<html><head><title>Tokens</title></head><body ");         writer.println("style=\"font-family:verdana;font-size:10pt\">"); 

If the client did not sent any token we create a new one:

         if(token == null) {           Random rand = new Random();           token = Long.toString(rand.nextLong());           writer.println("<p>Welcome. A new token " +                          token + " is now established</p>");         } else { 

If the client sent a token we acknowledge the client:

           writer.println("<p>Welcome back. Your token is " + token + ".</p>");         } 

Then we prepare a URL for the client to send requests back:

         String requestURL = request.getRequestURL().toString(); 

We finish by writing two forms. First we write a form with the token as a hidden field:

         writer.println("<p>");         writer.println("<form method='GET' action='" + requestURL + "'>");         writer.println("<input type='hidden' name='token' value='" + token + "'/>");         writer.println("<input type='submit' value='Click Here'/>");         writer.println("</form>");         writer.println(" to continue browsing with the same identity.</p>"); 

Then we write another form without the hidden field:

         writer.println("<form method='GET' action='" + requestURL + "'>");         writer.println("<input type='submit' value='Click Here'/>");         writer.println("</form>");         writer.println(" to start browsing with a new identity.</p>");         writer.close();       }     } 

In this servlet, instead of using the usual <href> tags, we used buttons (within forms) for the user to respond to. In the first form, the only parameter is a hidden field, while the second form has no parameters at all.

Again, to run the example, you need to create a new web application as you did for the other examples, and call this one hidden. Compile the above class and place the class file in the hidden/WEB-INF/classes/sessions/ directory.

Then create the following simple deployment descriptor for the web application:

     <?xml version="1.0"?>     <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"                              "http://java.sun.com/dtd/web-app_2_3.dtd">     <web-app>       <servlet>         <servlet-name>track</servlet-name>         <servlet-class>sessions.HiddenFieldServlet</servlet-class>       </servlet>       <servlet-mapping>         <servlet-name>track</servlet-name>         <url-pattern>/track/*</url-pattern>       </servlet-mapping>     </web-app> 

Deploy the web application, restart Tomcat, and go to http://localhost:8080/hidden/track. You should see the following:

click to expand

Instead of hyperlinks, the response contains two buttons. This is because the forms we created above do not have any visible fields.

Click on the first button to see a page similar to the following:

click to expand

If you now click on the second button, a new token is allocated.

As you can see from our example, a significant disadvantage to this form of maintaining session is obviously that we need to create forms, which is not particularly convenient when your content has hyper links and not forms.



 < Free Open Study > 



Professional Java Servlets 2.3
Professional Java Servlets 2.3
ISBN: 186100561X
EAN: 2147483647
Year: 2006
Pages: 130

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net