Handling Cookies |
---|
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.
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.
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:
[ ] ( ) = , " / ? @ : ;
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
.
LongLivedCookie
class below, which defines a subclass of
Cookie
with a maximum age automatically set one year in the future.
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.
someCookie.setPath("/")
specifies that all pages on the server
should receive the cookie. Note that the path specified must include
the current directory.
boolean
value indicating whether the cookie should
only be sent over encrypted (i.e. SSL) connections.
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);
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 Coo
kie 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.
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); }
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); } }
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.
You can also
download the source or
try it on-line. 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(""); } }
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); } }