您的位置:寻梦网首页编程乐园JAVA 天地Servlet 专辑Servlet and JSP Tutorial
Handling Cookies

  1. Overview of Cookies
  2. The Servlet Cookie API
  3. Some Minor Cookie Utilities
  1. Example: A Customized Search Engine Interface
  2. Servlet and JSP Tutorial: Top
  3. Servlet and JSP Training Courses On-site at your company or at public venues.

Note: Chapter 8 of Core Servlets and JavaServer Pages contains much more detailed and up to date information on this topic. The full text is now available online in PDF for free access.

1. Overview of Cookies

Cookies are small bits of textual information that a Web server sends to a browser and that the browser returns unchanged when visiting the same Web site or domain later. By having the server read information it sent the client previously, the site can provide visitors with a number of conveniences:
  • Identifying a user during an e-commerce session. Many on-line stores use a "shopping cart" metaphor in which the user selects an item, adds it to his shopping cart, then continues shopping. Since the HTTP connection is closed after each page is sent, when the user selects a new item for his cart, how does the store know that he is the same user that put the previous item in his cart? Cookies are a good way of accomplishing this. In fact, this is so useful that servlets have an API specifically for this, and servlet authors don't need to manipulate cookies directly to make use of it. This is discussed in the tutorial section on Session Tracking.
  • Avoiding username and password. Many large sites require you to register in order to use their services, but it is inconvenient to remember the username and password. Cookies are a good alternative for low-security sites. When a user registers, a cookie is sent with a unique user ID. When the client reconnects at a later date, the user ID is returned, the server looks it up, determines it belongs to a registered user, and doesn't require an explicit username and password.
  • Customizing a site. Many "portal" sites let you customize the look of the main page. They use cookies to remember what you wanted, so that you get that result initially next time. I'll give an example like this later in this section of the tutorial.
  • Focusing advertising. The search engines charge their customers much more for displaying "directed" ads than "random" ads. That is, if you do a search on "Java Servlets", a search site can charge much more for an ad for a servlet development environment than an ad for an on-line travel agent. On the other hand, if the search had been "Bali Hotels", the situation would be reversed. The problem is that they have to show a random ad when you first arrive and haven't yet performed a search, as well as when you search on something that doesn't match any ad categories. Cookies let them remember "Oh, that's the person who was searching for such and such previously" and display an appropriate (read "high priced") ad instead of a random (read "cheap") one.
Now, providing convenience to the user and added value to the site owner is the purpose behind cookies. And despite much misinformation, cookies are not a serious security threat. Cookies are never interpreted or executed in any way, and thus can't be used to insert viruses or attack your system in any way. Furthermore, since browsers generally only accept 20 cookies per site and 300 cookies total, and each cookie is limited to 4KB, cookies cannot be used to fill up someone's disk or launch other denial of service attacks. However, even though they don't present a serious security threat, they can preseent a significant threat to privacy. First, some people don't like the fact that search engines can remember that they're the person that usually does searches on such and such a topic. For example, they might search for job openings or health data, and don't want some banner ad tipping off their coworkers next time they do a search. Even worse, two search engines could share data on a user by both loading small images off a third party site, where that third party uses cookies and shares the data with both search engines. (Netscape, however, provides a nice feature that lets you refuse cookies from sites other than that to which you connected, but without disabling cookies altogether.) This trick can even be exploited via email if you use an HTML-enabled email reader that "supports" cookies, as Outlook Express does. Thus, people could send you email that loads images, attach cookies to those images, then later identify you (email address and all) when you went to their site.

Or, a site that ought to have much higher security standards might let users skip user name and passwords via cookies. For example, some of the big on-line bookstores use cookies to remember users, and let you order without reentering much of your personal information. However, they don't actually display the full credit card number, and only let you send books to an address that was entered when you did enter the credit card in full or use the username and password. As a result, someone using the person's computer (or stealing their cookie file) could do no more harm than sending a big book order to the credit card owner's address, where it could be refused. However, smaller companies might not be so careful, and access to someone's computer or cookie file could result in loss of valuable personal information. Even worse, incompetent sites might embed credit card or other sensitive information directly in the cookies themselves, rather than using innocuous identifiers which are only linked to real users on the server.

The point of all this is twofold. First, due to real and perceived privacy problems, some users turn off cookies. So, even when you use cookies to give added value to a site, your site shouldn't depend on them. Second, as the author of servlets that use cookies, you should be careful not to trust cookies for particularly sensitive information, since this would open the user up to risks if somebody accessed their computer or cookie files.

2. The Servlet Cookie API

To send cookies to the client, a servlet would create one or more cookies with the appropriate names and values via new Cookie(name, value) (section 2.1), set any desired optional attributes via cookie.setXxx (section 2.2), and add the cookies to the response headers via response.addCookie(cookie) (section 2.3). To read incoming cookies, call request.getCookies(), which returns an array of Cookie objects. In most cases, you loop down this array until you find the one whose name (getName) matches the name you have in mind, then call getValue on that Cookie to see the value associated with that name. This is discussed in section 2.4.

2.1 Creating Cookies

A Cookie is created by calling the Cookie constructor, which takes two strings: the cookie name and the cookie value. Neither the name nor the value should contain whitespace or any of:
  [ ] ( ) = , " / ? @ : ;

2.2 Reading and Specifying Cookie Attributes

Before adding the cookie to the outgoing headers, you can look up or set attributes of the cookie. Here's a summary:
getComment/setComment
Gets/sets a comment associated with this cookie.
getDomain/setDomain
Gets/sets the domain to which cookie applies. Normally, cookies are returned only to the exact hostname that sent them. You can use this method to instruct the browser to return them to other hosts within the same domain. Note that the domain should start with a dot (e.g. .prenhall.com), and must contain two dots for non-country domains like .com, .edu, and .gov, and three dots for country domains like .co.uk and .edu.es.
getMaxAge/setMaxAge
Gets/sets how much time (in seconds) should elapse before the cookie expires. If you don't set this, the cookie will last only for the current session (i.e. until the user quits the browser), and will not be stored on disk. See the LongLivedCookie class below, which defines a subclass of Cookie with a maximum age automatically set one year in the future.
getName/setName
Gets/sets the name of the cookie. The name and the value are the two pieces you virtually always care about. Since the getCookies method of HttpServletRequest returns an array of Cookie objects, it is common to loop down this array until you have a particular name, then check the value with getValue. See the getCookieValue method shown below.
getPath/setPath
Gets/sets the path to which this cookie applies. If you don't specify a path, the cookie is returned for all URLs in the same directory as the current page as well as all subdirectories. This method can be used to specify something more general. For example, someCookie.setPath("/") specifies that all pages on the server should receive the cookie. Note that the path specified must include the current directory.
getSecure/setSecure
Gets/sets the boolean value indicating whether the cookie should only be sent over encrypted (i.e. SSL) connections.
getValue/setValue
Gets/sets the value associated with the cookie. Again, the name and the value are the two parts of a cookie that you almost always care about, although in a few cases a name is used as a boolean flag, and its value is ignored (i.e the existence of the name means true).
getVersion/setVersion
Gets/sets the cookie protocol version this cookie complies with. Version 0, the default, adheres to the original Netscape specification. Version 1, not yet widely supported, adheres to RFC 2109.

2.3 Placing Cookies in the Response Headers

The cookie is added to the Set-Cookie response header by means of the addCookie method of HttpServletResponse. Here's an example:
  Cookie userCookie = new Cookie("user", "uid1234");
  response.addCookie(userCookie);

2.4 Reading Cookies from the Client

To send cookies to the client, you created a Cookie then used addCookie to send a Set-Cookie HTTP response header. This was discussed above in section 2.1. To read the cookies that come back from the client, you call getCookies on the HttpServletRequest. This returns an array of Cookie objects corresponding to the values that came in on the Cookie HTTP request header. Once you have this array, you typically loop down it, calling getName on each Cookie until you find one matching the name you have in mind. You then call getValue on the matching Cookie, doing some processing specific to the resultant value. This is such a common process that the following section presents a simple getCookieValue method that, given the array of cookies, a name, and a default value, returns the value of the cookie matching the name, or, if there is no such cookie, the designated default value.

3. Some Minor Cookie Utilities

Here are some simple but useful utilities for dealing with cookies.

3.1 Getting the Value of a Cookie with a Specified Name

Here's a section of ServletUtilities.java that slightly simplifies the retrieval of a cookie value given a cookie name by looping through the array of available Cookie objects, returning the value of any Cookie whose name matches the input. If there is no match, the designated default value is returned.
  public static String getCookieValue(Cookie[] cookies,
                                      String cookieName,
                                      String defaultValue) {
    for(int i=0; i<cookies.length; i++) {
      Cookie cookie = cookies[i];
      if (cookieName.equals(cookie.getName()))
        return(cookie.getValue());
    }
    return(defaultValue);
  }

3.2 LongLivedCookie.java (Download source code)

Here's a small class that you can use instead of Cookie if you want your cookie to automatically persist when the client quits the browser.
package hall;

import javax.servlet.http.*;

public class LongLivedCookie extends Cookie {
  public static final int SECONDS_PER_YEAR = 60*60*24*365;
  
  public LongLivedCookie(String name, String value) {
    super(name, value);
    setMaxAge(SECONDS_PER_YEAR);
  }
}

4. Example: A Customized Search Engine Interface

Here's a variation of the SearchEngines example shown in the section on the HTTP status line. In this version, the front end is dynamically generated instead of coming from a static HTML file. Then, the servlet that reads the parameters and forwards them to the appropriate search engine also returns cookies to the client that list these values. Next time the client visits the front end, the cookie values are used to preload the form fields with the most recently used entries.

4.1 SearchEnginesFrontEnd.java

This servlet builds the form-based front end to the search engine servlet. At first blush, the output looks just like the page given by the static HTML page presented in the section on HTTP status codes. Here, however, selected values are remembered in cookies (set by the CustomizedSearchEngines servlet that this page sends data to), so if the user comes back to the same page at a later time (even after quitting the browser and restarting), the page is initialized with the values from the previous search.

Note that code uses ServletUtilities.java, for the getCookieValue method (shown above) and for headWithTitle for generating part of the HTML. It also uses the LongLivedCookie class, shown above, for creating a Cookie that automatically has a long-term expiration date.

package hall;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.net.*;

public class SearchEnginesFrontEnd extends HttpServlet {
  public void doGet(HttpServletRequest request,
                    HttpServletResponse response)
      throws ServletException, IOException {
    Cookie[] cookies = request.getCookies();
    String searchString =
      ServletUtilities.getCookieValue(cookies,
                                      "searchString",
                                      "Java Programming");
    String numResults =
      ServletUtilities.getCookieValue(cookies,
                                      "numResults",
                                      "10");
    String searchEngine =
      ServletUtilities.getCookieValue(cookies,
                                      "searchEngine",
                                      "google");
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    String title = "Searching the Web";
    out.println(ServletUtilities.headWithTitle(title) +
                "<BODY BGCOLOR=\"#FDF5E6\">\n" +
                "<H1 ALIGN=\"CENTER\">Searching the Web</H1>\n" +
                "\n" +
                "<FORM ACTION=\"/servlet/hall.CustomizedSearchEngines\">\n" +
                "<CENTER>\n" +
                "Search String:\n" +
                "<INPUT TYPE=\"TEXT\" NAME=\"searchString\"\n" +
                "       VALUE=\"" + searchString + "\"><BR>\n" +
                "Results to Show Per Page:\n" +
                "<INPUT TYPE=\"TEXT\" NAME=\"numResults\"\n" + 
                "       VALUE=" + numResults + " SIZE=3><BR>\n" +
                "<INPUT TYPE=\"RADIO\" NAME=\"searchEngine\"\n" +
                "       VALUE=\"google\"" +
                checked("google", searchEngine) + ">\n" +
                "Google |\n" +
                "<INPUT TYPE=\"RADIO\" NAME=\"searchEngine\"\n" +
                "       VALUE=\"infoseek\"" +
                checked("infoseek", searchEngine) + ">\n" +
                "Infoseek |\n" +
                "<INPUT TYPE=\"RADIO\" NAME=\"searchEngine\"\n" +
                "       VALUE=\"lycos\"" +
                checked("lycos", searchEngine) + ">\n" +
                "Lycos |\n" +
                "<INPUT TYPE=\"RADIO\" NAME=\"searchEngine\"\n" +
                "       VALUE=\"hotbot\"" +
                checked("hotbot", searchEngine) + ">\n" +
                "HotBot\n" +
                "<BR>\n" +
                "<INPUT TYPE=\"SUBMIT\" VALUE=\"Search\">\n" +
                "</CENTER>\n" +
                "</FORM>\n" +
                "\n" +
                "</BODY>\n" +
                "</HTML>\n");
  }

  private String checked(String name1, String name2) {
    if (name1.equals(name2))
      return(" CHECKED");
    else
      return("");
  }
}

4.2 CustomizedSearchEngines.java

The SearchEnginesFrontEnd servlet shown above sends its data to the CustomizedSearchEngines servlet. In many respects, it is just like the SearchEngines servlet shown in the section on HTTP status codes. However, in addition to constructing a URL for a search engine and sending a redirection response to the client, the servlet also sends cookies recording the user data. These cookies will, in turn, be used by the servlet building the front end to initialize the entries in the HTML forms.
package hall;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.net.*;

/** A varition of the SearchEngine servlet that uses
 *  cookies to remember users choices. These values
 *  are then used by the SearchEngineFrontEnd servlet
 *  to create the form-based front end with these
 *  choices preset.
 *  

* Part of tutorial on servlets and JSP that appears at * http://www.apl.jhu.edu/~hall/java/Servlet-Tutorial/ * 1999 Marty Hall; may be freely used or adapted. */ public class CustomizedSearchEngines extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String searchString = request.getParameter("searchString"); Cookie searchStringCookie = new LongLivedCookie("searchString", searchString); response.addCookie(searchStringCookie); searchString = URLEncoder.encode(searchString); String numResults = request.getParameter("numResults"); Cookie numResultsCookie = new LongLivedCookie("numResults", numResults); response.addCookie(numResultsCookie); String searchEngine = request.getParameter("searchEngine"); Cookie searchEngineCookie = new LongLivedCookie("searchEngine", searchEngine); response.addCookie(searchEngineCookie); SearchSpec[] commonSpecs = SearchSpec.getCommonSpecs(); for(int i=0; i<commonSpecs.length; i++) { SearchSpec searchSpec = commonSpecs[i]; if (searchSpec.getName().equals(searchEngine)) { String url = searchSpec.makeURL(searchString, numResults); response.sendRedirect(url); return; } } response.sendError(response.SC_NOT_FOUND, "No recognized search engine specified."); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }

4.3 SearchEnginesFrontEnd Output

Here's the front end as it looks after the user types in some values or if the user comes back to the page in the same or a later session, after having typed in the values in the previous visit.
SearchEngines FrontEnd

4.4 CustomizedSearchEngines Output

CustomizedSearchEngines