This is the 23rd day of my participation in the August More Text Challenge

This paper introduces the principle and common usage of Cookie technology in Java Web Servlet in detail.

1 Session tracing technology

A session can be simply defined as a meeting between a client and a server that may contain multiple requests and responses in a single meeting. For example, when a user accesses a Web application in a browser, as long as the browser is not closed, no matter how many hyperlinks and resources the user clicks on the Web application, the whole access process is called a session until the browser is closed.

In the Java Web, by default, a session starts when a client makes the first request to a server (Web application) until the client closes the browser and the session ends.

Session tracing is the technique of sharing data across multiple requests for a session. Session tracking technology can solve many, many of our problems. Such as one of the most common is a user on a web site, request the website pages and other resources from the login function, such as common in shopping website showed once again read the function of the goods, or some shopping website allow not logged in user can add goods to function in a temporary shopping cart.

Common Session tracking techniques are Cookie and Session. Cookie comes first.

2 Overview of Cookies

HTTP is a stateless protocol that does not save state. The HTTP protocol itself does not store the state of communication between requests and responses. That is, at the HTTP level, the protocol does not persist requests or responses that have been sent.

With HTTP, a new response is generated whenever a new request is sent. The protocol itself does not retain information about all previous request or response messages. The HTTP protocol is designed to be so simple in order to process a large number of transactions more quickly and ensure protocol scalability.

However, as the Web continues to evolve, the number of cases where business processing becomes tricky due to statelessness increases. For example, a user logging into a shopping site needs to be able to stay logged in even after he jumps to other pages on the site. For this example, the site needs to keep track of the user’s state, known as session tracking, in order to know who sent the request.

HTTP/1.1 is also a stateless protocol, but in order to achieve the desired state retention, Cookie technology was introduced. Cookie technology controls client status by writing Cookie information in request and response packets.

To put it simply: A Cookie is a key-value pair created by the server and sent to the client through the response (the header field of set-cookie). The key-value pair contains the state information that the session wants to remember. The client saves the Cookie and marks the source of the Cookie (which website is the Cookie). When the client sends a request to the website again, it will send all the cookies of the website contained in the request header to the server, so that the server can identify the client according to the Cookie information carried by the request, and get the previous state information!

With cookies and HTTP communication, you can manage session state to some extent.

First request without Cookie information:

Second start, request with Cookie information state:

3 Cookie and HTTP request header

Cookies are passed on both the client and server side via HTTP request and response header fields:

  1. In the request header, a field named “Cookie” is used, and the client sends the stored Cookie to the server.
  2. The format is “Cookie: a= a; b=B; C = c “. Multiple cookies can share a request header separated by a semicolon, and there is a Cookie field in each request header.
  3. In the response header, a field named “set-cookie” is used, and the server sends the Cookie to the client.
  4. The format is set-cookie: a= a; b=B; C = c;” . Multiple cookies can share a response header separated by a semicolon, or there can be multiple set-cookie fields in a response header.

4 Simple use of cookies

In the Java EE Servlet specification, it is very simple to set and retrieve cookies, which are abstracted as a Cookie class.

When setting cookies, you need to create Cookie objects and set key and value, then add the Cookie through addCookie method of response response object, and the Web server will automatically send cookies in response to the client.

When the client accesses other resources of the Web application, the Cookie sent by the server will be automatically carried by the client (other Web applications on the same server will not send cookies).

In the following example, a Web application with two servlets in “cookie” tries to send an “ID” cookie to the browser with a random UUID string value when accessing the “/cookie-servlet” resource:

@WebServlet("/cookie-servlet")
public class CookieServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=utf-8");
        // Generate a random string
        String id = UUID.randomUUID().toString();
        // Create a Cookie object with a name and value. The Cookie class has only one constructor
        Cookie cookie = new Cookie("id", id);
        // Add a Cookie object to the response
        resp.addCookie(cookie);
        resp.getWriter().print("I have sent you my ID."); }}Copy the code

When accessing the “/hello-servlet” resource, this servlet is used to test the Cookie, which is obtained through the getCookies method of the Request object, and attempts to output the previously issued ID (if present) :

@WebServlet("/hello-servlet")
public class HelloServlet extends HttpServlet {

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        Cookie[] cookies = request.getCookies();
        // If a Cookie exists in the request
        if(cookies ! =null) {
            // Iterate over all cookies
            for (Cookie c : cookies) {
                // Get the Cookie name, if the Cookie name is id
                if ("id".equals(c.getName())) {
                    // Prints the Cookie value
                    response.getWriter().print("Your ID is:" + c.getValue());
                }
            }
        }
    }
}
Copy the code

We first accessed /hello-servlet, which yielded no data ID information and no cookie-related fields (the Google browser we used here) :

When we revisit the /cookie-servlet, the server returns a set-cookie response header:

The /hello-servlet is then accessed again, and the result is the id and Cookie fields in the request header, which are the key and value sent by the server:

5 Other details of Cookie

5.1 Cookie coverage

If the server sends the Cookie with the duplicate key for many times, the Cookie sent later will overwrite the original Cookie. For example, the Cookie sent by the server for the first request of the client is Set-Cookie: a= a; In the second request, the server sends set-cookie: A =AA, so the client leaves only one Cookie, that is, A =AA.

If the server sends multiple cookies with duplicate keys at once, the client will only carry the value of the last set cookie when it requests again.

If we request /cookie-servlet again, we can see that different ids are issued and different values are set for the same key:

We ask again/hello-servletThe Cookie in the request header also changes. This is the override of the Cookie:

5.2 Validity period of Cookie

The so-called Cookie validity period is the maximum validity period of the Cookie on the client. We can set the validity period of the Cookie by setMaxAge(int) method, in seconds.

  1. cookie.setMaxAge(-1)The default value of the maxAge attribute of the cookie is -1, indicating that the cookie only lives in browser memory. Once the browser window is closed, the cookie disappears. Cookies with negative health are also called memory cookies.
  2. cookie.setMaxAge(60*60): indicates that the cookie object can survive for 1 hour. When the life value is greater than 0, the browser saves the Cookie to the hard disk. Even if the browser is closed, even if the client computer is restarted, the Cookie will survive for 1 hour, unless the Cookie is manually cleaned from the hard disk. Cookies with positive health are also known as hard drive cookies.
  3. cookie.setMaxAge(0)Cookie Life equals 0 is a special value that indicates that the cookie is invalidated! That is, if the browser has already saved the Cookie, the Cookie setMaxAge(0) can be used to ask the browser to delete the Cookie. The Cookie is deleted either in the browser’s memory or on the client’s hard drive.

5.3 Cookie modification and deletion

Cookies do not provide direct modification and deletion functions, but can be modified and deleted indirectly according to the previous Cookie coverage and validity period attributes.

When you want to modify a Cookie, you can send a Cookie with the same name (key) so that the old Cookie is overwritten. When xiaoyao deletes cookies, maxAge = 0 of cookies with the same name (key) is also set.

5.4 Path of Cookie

5.4.1 What is the Cookie path

Now you have Web application A sending 10 cookies to the client, and the client will include these 10 cookies in the request no matter which resource application A accesses! But perhaps only the AServlet needs to read the Cookie in the request, and the other servlets don’t get the Cookie in the request at all. This shows that the client browser sometimes sends these cookies unnecessarily!

You can specify what cookies the browser includes when accessing what path by setting the Cookie’s path.

5.4.2 Relationship between Cookie Path and request Path

Here are the three cookies saved by the client browser and their paths:

  1. A/cookietest;
  2. B/cookietest/servlet;
  3. C/cookietest/JSP;

Here are the three requests and their urls:

  1. A http://localhost:8080/cookietest/AServlet
  2. B http://localhost:8080/cookietest/servlet/BServlet
  3. C http://localhost:8080/cookietest/jsp/CServlet

So:

  1. When A is requested, Cookie A is included in the request;
  2. Cookies A and B are included in the request for B.
  3. Cookies A and C are included in the request when C is requested.

That is, if the request path contains a Cookie path, the Cookie will be included in the request, otherwise the Cookie will not be included in the request.

  1. A request URL contains “/cookietest”, so the request can contain A Cookie whose path is “/cookietest”;
  2. B request URL contains “/cookietest” and “/cookietest/servlet”, so the request can contain two cookies “/cookietest” and “/cookietest/servlet”.
  3. C request URL contains “/cookietest” and “/cookietest/ JSP”, so the request can contain two cookies “/cookietest” and “/cookietest/ JSP”.

5.4.3 Setting a Path

The setPath() method is used to set the path to the Cookie, for example cookie.setPath(“/cookietest/servlet”). If setPath(“/”), then all urls are carried.

If the Cookie path is not actively set, then the default value of the Cookie path is the path where the resource is currently accessed, for example:

  1. To add the default path of cookies to visit http://localhost:8080/cookietest/AServlet for/cookietest;
  2. To add the default path of cookies to visit http://localhost:8080/cookietest/servlet/BServlet for/cookietest/servlet;
  3. To add the default path of cookies to visit http://localhost:8080/cookietest/jsp/CServlet for/cookietest/JSP;

We set the cookie-servlet path to /a/cookie-servlet in the case above. Request /a/cookie-servlet and see that cookies are issued:

According to the path rules above, we know that the Cookie path set here is “/ Cookie /a”, which is compatible with the request URL that also contains this path.

/hello-servlet: /hello-servlet: /hello-servlet: /hello-servlet: /hello-servlet: /hello-servlet: /hello-servlet

5.5 Domain (Domain name) of cookies

By default, cookies set in different domain names cannot be shared, i.e. cross domain problem!

Cookies have a setDomain method that sets the domain property (domain name) of the cookie, indicating the scope of the cookie at the domain name level. If not set, the default description is the current domain name.

How to make multiple subdomains with the same domain suffix share the Cookie of the master domain? For example, the following domain names are www.baidu.com, http://zhidao.bai…

Before Tomcat 8.x, we could set the first character of the domain name in the setDomain method to be “. To set up multi-level domain name sharing.

It’s very simple and requires the following two steps:

  1. Set the path of Cookie to “/”, that is, setPath(“/”), indicating that all URL paths can be accessed.
  2. Set the domain of the Cookie to.baidu.com, that is, setDomain(“.baidu.com”), indicating that all level 2, level 3, and Level N subdomain names with the baidu.com top-level domain name share the Cookie.

In Tomcat 8.x and later, if the domain method parameter is preceded by “. , an exception will be thrown:

Since tomcat8.x uses Rfc6265CookieProcessor as the default Cookie processor, it has the following domain attributes:

  1. The value is 1-9, a-z, A-z, period (.), and -.
  2. Must start with a number or letter (all domain Settings begin with “. An exception is thrown at the beginning);
  3. Must end in a number or letter.

How do you solve it? It’s easy too! Modify the configuration file context. XML, specify CookieProcessor is org.. Apache tomcat. Util. HTTP. LegacyCookieProcessor, this is the old version of tomcat default Cookie processor:

<CookieProcessor className="org.apache.tomcat.util.http.LegacyCookieProcessor" />
Copy the code

That is:

5.6 Chinese characters are saved in cookies

Cookie name and value cannot be in Chinese, because Chinese is a Unicode character, English data ASCII character, Chinese contains 4 characters or 3 characters, English contains 2 characters.

If you want to use Chinese in a Cookie, you need to urL-encode Chinese first, and then put the encoded string into the Cookie. When obtaining cookies, the encoded data is first URL decoded.

Add cookies to client responses:

String name = URLEncoder.encode("Name"."UTF-8");
String value = URLEncoder.encode("Zhang"."UTF-8");
Cookie c = new Cookie(name, value);
c.setMaxAge(3600);
response.addCookie(c);
Copy the code

Retrieves cookies from client requests

response.setContentType("text/html; charset=utf-8");
Cookie[] cs = request.getCookies();
if(cs ! =null) {
	for(Cookie c : cs) {
		String name = URLDecoder.decode(c.getName(), "UTF-8");
		String value = URLDecoder.decode(c.getValue(), "UTF-8");
		String s = name + ":" + value + "<br/>"; response.getWriter().print(s); }}Copy the code

6 Simple use of cookies

6.1 Displaying the last access Time

Simple use of cookies in memory to display the last access time function, if you need to save for a long time, and need to be displayed in all places, then you still need to use the database to save.

@WebServlet("/cookie-time")
public class CookieTime extends HttpServlet {

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html; charset=utf-8");


        // Create a Cookie named lastTime with the value of the current time and add it to response
        Cookie cookie = new Cookie("lastTime", Long.toString(System.currentTimeMillis()));
        cookie.setMaxAge(60 * 60);
        response.addCookie(cookie);
        /* * Get lastTime Cookie * if it does not exist, print "you are the first time to visit this site", if it exists, print "you last visited this site is XXX"; * /
        Cookie[] cs = request.getCookies();
        String s = "This is your first visit to this site!";
        if(cs ! =null) {
            for (Cookie c : cs) {
                if ("lastTime".equals(c.getName())) {
                    // Format the timestamp in milliseconds
                    s = "Your last visit was:" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss SSS") .format(Instant.ofEpochMilli(Long.parseLong(c.getValue())).atZone(ZoneId.systemDefault())); } } } response.getWriter().print(s); }}Copy the code

First run:

Subsequent runs:

6.2 Display items that have been browsed

Simply use cookies to realize the function of displaying products that have been browsed in memory. If it needs to be stored for a long time and can be displayed in all places, it still needs to use the database to save.

@WebServlet("/products-servlet")
public class ProductsServlet extends HttpServlet {

    /** * simple mapping of product names and links */
    static HashMap<String, String> productsMap;
    static String productsUrl;

    static {
        productsMap = new HashMap<>();
        productsMap.put("ThinkPad"."ThinkPad

"
); productsMap.put("Lenovo"."Lenovo

"
); productsMap.put("Apple"."Apple

"
); productsMap.put("HP"."HP

"
); productsMap.put("SONY"."SONY

"
); productsMap.put("ACER"."ACER

"
); productsMap.put("DELL"."DELL

"
); } @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setContentType("text/html; charset=utf-8"); String productName = request.getParameter("name"); String products = getCookValue(request, "products"); String[] oldProducts = null; if(products ! =null) { oldProducts = products.split("_"); } if(productName ! =null) { if(products ! =null) { LinkedList<String> strings = new LinkedList<>(Arrays.asList(oldProducts)); / / update the cookies if(! strings.contains(productName)) { strings.addFirst(productName);// Display up to three recently browsed items if (strings.size() == 4) { strings.removeLast(); }}else { / / update the cookies strings.remove(productName); strings.addFirst(productName); } products = String.join("_", strings); } else{ products = productName; }}/ / update the cookies if(products ! =null) { Cookie cookie = new Cookie("products", products); cookie.setMaxAge(60 * 60 * 24); response.addCookie(cookie); } /* * Output information */ PrintWriter writer = response.getWriter(); writer.println("

List of goods:

"
); // Outputs a list of items writer.println(getproductsUrl()); writer.println("

Items you have browsed before:

"
); if(oldProducts ! =null) { for (String oldProduct : oldProducts) { // Outputs the most recently browsed item in Cookewriter.println(productsMap.get(oldProduct)); }}}/** * merchandise list */ private String getproductsUrl(a) { if(productsUrl ! =null) { return productsUrl; } StringBuilder stringBuilder = new StringBuilder(); for (String value : productsMap.values()) { stringBuilder.append(value); } productsUrl = stringBuilder.toString(); return productsUrl; } /** * gets the value of the key specified in the Cookie */ private String getCookValue(HttpServletRequest request, String name) { Cookie[] cs = request.getCookies(); if (cs == null) { return null; } for (Cookie c : cs) { if (name.equals(c.getName())) { returnc.getValue(); }}return null; }}Copy the code

References:

  1. Graphical HTTP Protocol

If you need to communicate, or the article is wrong, please leave a message directly. In addition, I hope to like, collect, pay attention to, I will continue to update a variety of Java learning blog!