This is the 24th day of my participation in the August More Text Challenge

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

Java Web(9) – The principle and usage of Cookie technology, this time we will learn about Session!

Session and cookie have similar functions. They both store user status information and are Session tracking technologies. The difference is that cookies are stored in the local browser, while sessions are stored on the server. Data stored on the server is more secure and less susceptible to theft, and sessions are easier to use than cookies. Sessions can store objects, while cookies can only store strings, and later versions of Tomcat8.x have limitations on the types of characters cookies can store.

We often use the Session is javax.mail. Servlet. HTTP. HttpSession!

1 Obtain the HttpSession object

  1. HttpSession request.getSesssion()
    1. On the first request, the server creates a Session object and returns it. Each Session object has a unique JSESSIONID. The JSESSIONID is then returned to the client as a set-cookie response header. The client sends the JSESSIONID to the server when it visits again. If the current Session already has a Session object, the getSesssion() method returns the current Session object.
  2. HttpSession request.getSession(boolean)
    1. When true, same as requeset.getSession(). If false, return the Session object if it exists in the current Session, but null if it does not, instead of creating it.

2 HttpSession is a domain object

We’ve learned about HttpServletRequest, ServletContext, which are domain objects, and now we’ve learned about an HttpSession, which is also a domain object, whose scope is the current session. Three of these are domain objects that can be used in servlets, and one more domain object, pageContext, can be used in JSPS.

  1. It: A request creates a request object, so a request can be shared within the same request. For example, if a request is forwarded from AServlet to BServlet, AServlet and BServlet can share data in the request domain.
  2. ServletContext: An application creates only one ServletContext object, so the data in the ServletContext can be shared across the application. As long as the server is not shut down, the data in the ServletContext can be shared across the application.
  3. HttpSession: An HttpSession object is created for each session. Data in the session can be shared between multiple requests in the same session. The scope is the current Session. The Session closes and the Session disappears.

All domain objects have the following four methods for manipulating data:

methods describe
void setAttribute(String name, Object object) Binds the object to the given property name in this domain object. If the name specified is already used for an attribute, this method replaces the old attribute with the value of the new attribute.
Object getAttribute(String name) Returns a value object containing the attribute value with the given name key, or null if no attribute matching the given name exists.
void removeAttribute(String name) Deletes the property with the given name from this domain object. After deletion, subsequent calls to getAttrit (java.lang.string) to retrieve the value of the property will return NULL. If the domain property specified by the parameter name does not exist, this method does nothing.
Enumeration getAttributeNames(a) Returns an enumeration containing the names of properties available in this domain object. If not, an empty enumeration is returned.

3 Implementation principle of Session

Session implementation is Cookie dependent.

When a client first calls getSesssion() or getSession(True), the server creates a Session object, A unique Session ID associated with the Session is returned to the client (the Tomcat server is a pseudo-random number generated by the SessionIdGenerator), that is, the JSESSIONID, which is sent to the client in the form of set-cookie. Therefore, the client only stores the SessionId in the form of cookies, while the Session data is stored in the Session object of the server.

How do I know if a client is calling getSesssion() or getSession(True) for the first time? This is actually based on whether there is a Cookie of the JSESSIONID in the request header of the client and whether the Session corresponding to the JSESSIONID exists (possibly expired).

When the client accesses the server again, the Cookie with the SessionId (JSESSIONID) will be displayed in the request, and the server will find the corresponding Session by the SessionId of the key named JSESSIONID in the Cookie in the request header and return, You don’t have to create a new Session.

Therefore, Session implementation is Cookie dependent and client data (state information) is stored on the server side.

In addition, a Session is automatically created when a JSP dynamic resource is accessed, because the JSP has the getSesssion() method in the code after it is compiled into a Java file.

4 Session timeout duration

By default, the Tomcat server does not limit the number of sessions that can be created. To prevent memory overflow, the Tomcat server deletes a Session that has not been active for a long time from the memory. The timeout period is 30 minutes by default. If you open a page on the site and start sitting for longer than 30 minutes, then click on a link or submit a form and you will find that your identity has expired because your Session has been lost!

However, if you continue to access (read/write) the Session within 30 minutes, the server will update the last access time of the Session, and the timeout will be pushed back.

The default Session timeout period of the server can be set in the ${CATALANA}/conf/web. XML configuration file, in minutes. If the value is zero or negative, the Session never times out:

The default Session timeout period of a single Web application can be set in the session-config tag of the application’s web. XML. If this parameter is not set, the server time is used, in minutes.

For a single Session object, you can call setMaxInactiveInterval in the code to set the timeout period, in seconds. If the value is zero or negative, the Session never times out.

5 Session Other common apis

methods describe
String getId() Get sessionId
int getMaxInactiveInterval() Gets the maximum inactivity time (seconds) of the session. The default is 30 minutes. If a session has not been used for 30 minutes, Tomcat removes the session from the session pool.
void setMaxInactiveInterval(int interval) Set this parameter to the maximum inactivity time (s) allowed for a single session. If this parameter is set to 1 second, the session will be removed if the session is not used within 1 second. A value of zero or negative indicates that the Session never times out.
long getCreationTime() Returns the time when the session was created, in milliseconds.
long getLastAccessedTime() Returns the last active time of the session in milliseconds.
void invalidate() When the Session expires, the server creates a new Session for the client and gives the client the sessionId of the new Session in the response
boolean isNew() Check whether the session is new. When the client requests the session for the first time, the server creates a session for the client, but the server has not responded to the client, that is, has not responded to the client with the sessionId, then the session state is new.

6 SessionId life

The Session is stored on the server, and the SessionId is sent to the client via a Cookie, but we know that the Cookie has a default life of -1, that is, it only exists in browser memory, which means that if the user closes the browser, the Cookie is lost.

When the client opens the browser next time to access, although the Session on the server may not have expired, the previous Session cannot be found due to the absence of the SessionId, so the client’s status may be lost, and it may need to log in again.

To do this, we can manually set the Cookie expiration time of “JSESSIONID”, for example:

@WebServlet("/session-servlet")
public class SessionServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        HttpSession session = req.getSession();
        Cookie jsessionid = new Cookie("JSESSIONID", session.getId());
        jsessionid.setMaxAge(30 * 60);
        jsessionid.setPath("/"); resp.addCookie(jsessionid); }}Copy the code

Therefore, cookies and sessions are usually used together!

7 the URL rewrite

If the client browser disables cookies, the browser does not bring cookies to the server. To do this, you can use URL rewriting to append the JSESSIONID directly to the request parameter, so that the server can get the client’s SessionId by retrieving the request parameter JSESSIONID and find the sessoin object.

URL rewriting can be implemented using a Response object calling the encodeURL() or encodeRedirectURL() method. When using redirection, use the encodeRedirectURL() method.

The two methods first determine whether the current Servlet has executed the httpsession.invalidate () method (whether the current session is invalid and a new session is re-established), and if it has executed the URL parameter, then determine whether the client has disabled cookies. If the URL parameter is disabled, append the JSESSIONID to the URL parameter and return the encoded URL.

Redirected servlets:

@WebServlet("/url-rewrite")
public class UrlRewrite extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        HttpSession session = req.getSession();
        System.out.println("url-rewrite: "+session.getId());

        // Plain redirection
        resp.sendRedirect("UrlRewrite-servlet");
        // Redirects the URL rewrite
       // resp.sendRedirect(resp.encodeRedirectURL("UrlRewrite-servlet"));}}Copy the code

Objective the Servlet:

@WebServlet("/UrlRewrite-servlet")
public class UrlRewriteServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        HttpSession session = req.getSession();
        System.out.println("UrlRewrite-servlet: "+ session.getId()); System.out.println(); }}Copy the code

We tried to disable cookies for our browser (I’m using Firefox) :

Accessing “/url-rewrite”, using plain redirects first, console two servlets will output different ids:

Next, we redirected UrlRewrite using resp.sendreDirect (resp.encodereDirecturl (” urlrewrite-servlet “)) with URL rewrite:

We can see that the same JSESSIONID is used for each redirection, and we can find the JSESSIONID in the browser after the request URL parameter:

If the URL is accessed from another browser that has not disabled cookies, the URL will not carry the JSESSIONID. This is why THE URL rewriting will dynamically determine whether the client has disabled cookies!

Note: since the sessionId attached to the URL is generated dynamically and is different for each user, the URL rewriting mechanism is helpless for static page jumps to each other (such as HTML). You can, of course, solve this problem by converting static pages into dynamic pages, such as JSPS.

8 Differences between Session and Cookie

  1. Compare from storage mode
    1. Cookies can only store strings, and if you want to store non-ASCII strings, you have to encode them.
    2. A Session can store any type of data. You can think of a Session as a container
  2. Compare from privacy security
    1. Cookies are stored in the browser and are visible to the client. Information is easy to leak out. If you use cookies, it is best to encrypt them
    2. Sessions are stored on the server and are transparent to clients. There is no sensitive information leakage problem.
  3. From the period of validity
    1. Cookies are stored on the hard disk. You only need to set the maxAge property to a large positive integer. Cookies will still exist even if the browser is closed
    2. Sessions are saved on the server. Set the maxInactiveInterval value to determine the Session validity period. And the Session relies on a Cookie named JSESSIONID, whose default maxAge attribute is -1. If the browser is closed, the Session is invalidated, though not extinguished from the server.
  4. Compare the load from the server
    1. Sessions are stored on the server. Each user generates a Session. If a large number of concurrent users access the server, sessions cannot be used because they consume a large amount of memory.
    2. Cookies are stored on the client. Does not occupy server resources. Large websites such as Baidu and Sina generally use cookies for session tracking.
  5. Compare from browser support
    1. Cookies are useless if the browser disables them!
    2. If cookies are disabled by the browser, sessions can be traced by URL rewriting.
  6. Compare across domains
    1. Cookie can set the domain property to implement cross-domain
    2. Session is valid only for the current domain name

9 One-time image verification code case

After the verification code is generated, the data of the verification code is saved in the Session domain object to check whether the entered verification code is consistent with the data of the Session domain object. And after the correct judgment, need to prevent repeated submission, in the actual development process should strictly ensure idempotent, the following case is very simple, only for demonstration, can not be used in the development.

Image captcha tool class:

public class ImageVerificationCode {

    private int weight = 100;           // The length and width of the captcha image
    private int height = 30;
    private String text;                // To save the text content of the verification code
    private Random r = new Random();    // Get a random number object
    / / private String [] fontNames = {" tahoma ", "Chinese regular script", "black", "Microsoft jas black", "regular script _GB2312"}; // Array of fonts
    // Array of fonts
    private String[] fontNames = {"Georgia"};
    // Verification code array
    private String codes = "23456789abcdefghjkmnopqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ";

    /** * gets a random color **@return* /
    private Color randomColor(a) {
        int r = this.r.nextInt(225);  When r, g, and b are 255, the color is white. In order to recognize them, the color needs to be darker.
        int g = this.r.nextInt(225);
        int b = this.r.nextInt(225);
        return new Color(r, g, b);            // Return a random color
    }

    /** * get random font **@return* /
    private Font randomFont(a) {
        int index = r.nextInt(fontNames.length);  // Get a random font
        String fontName = fontNames[index];
        int style = r.nextInt(4);         // Randomly get the font style, 0 is no style, 1 is bold, 2 is italic, 3 is bold and italic
        int size = r.nextInt(10) + 20;    // Get the font size randomly
        return new Font(fontName, style, size);   // Return a random font
    }

    /** * gets a random character **@return* /
    private char randomChar(a) {
        int index = r.nextInt(codes.length());
        return codes.charAt(index);
    }

    /** ** Draw interference lines, verification code interference lines are used to prevent computers from parsing images **@param image
     */
    private void drawLine(BufferedImage image) {
        int num = r.nextInt(10); // Define the number of interference lines
        Graphics2D g = (Graphics2D) image.getGraphics();
        for (int i = 0; i < num; i++) {
            int x1 = r.nextInt(weight);
            int y1 = r.nextInt(height);
            int x2 = r.nextInt(weight);
            inty2 = r.nextInt(height); g.setColor(randomColor()); g.drawLine(x1, y1, x2, y2); }}/** * Create the image method **@return* /
    private BufferedImage createImage(a) {
        // Create the image buffer
        BufferedImage image = new BufferedImage(weight, height, BufferedImage.TYPE_INT_RGB);
        // Get the brush
        Graphics2D g = (Graphics2D) image.getGraphics();
        // Set the background color to random
        g.setColor(new Color(255.255, r.nextInt(245) + 10));
        g.fillRect(0.0, weight, height);
        // Return an image
        return image;
    }

    /** * how to get a captcha image **@return* /
    public BufferedImage getImage(a) {
        BufferedImage image = createImage();
        Graphics2D g = (Graphics2D) image.getGraphics(); // Get the brush
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 4; i++)             // Draw four characters
        {
            String s = randomChar() + "";      // Randomly generate characters, because there is only a method to draw strings, there is no method to draw characters, so we need to draw characters as strings
            sb.append(s);                      // Add to StringBuilder
            float x = i * 1.0 F * weight / 4;   // Define the x coordinate of the character
            g.setFont(randomFont());           // Set the font, randomly
            g.setColor(randomColor());         // Set the color randomly
            g.drawString(s, x, height - 5);
        }
        this.text = sb.toString();
        drawLine(image);
        return image;
    }

    /** * How to get the captcha text **@return* /
    public String getText(a) {
        return text;
    }

    public static void output(BufferedImage image, OutputStream out) throws IOException                  // How to write the captcha image
    {
        ImageIO.write(image, "JPEG", out); }}Copy the code

Servlet to generate captcha:

/ * * *@author lx
 */
@WebServlet("/VerifyCodeServlet")
public class VerifyCodeServlet extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        ImageVerificationCode vc = new ImageVerificationCode();
        BufferedImage image = vc.getImage();
        request.getSession().setAttribute("vCode", vc.getText()); ImageVerificationCode.output(image, response.getOutputStream()); }}Copy the code

Registered Servlet:

```java
@WebServlet("/RegisterServlet")
public class RegisterServlet extends HttpServlet {
    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html; charset=utf-8");
        String username = request.getParameter("username");
        String vCode = request.getParameter("code");
        if (vCode.equalsIgnoreCase((String) request.getSession().getAttribute("vCode"))) {
            // Here simple, step by step judgment and deletion do not guarantee atomicity, redis can be used
            //LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
            // Delete session after success to prevent duplicate commits
            request.getSession().removeAttribute("vCode");
            LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
            System.out.println(username + , "that's great! Registration successful!");
            response.getWriter().print(username + , "that's great! Registration successful!");
        } else {
            response.g
etWriter(a).print("Verification code error!");
            System.out.println("Verification code error!"); }}}Copy the code

Page:

<html lang="zh-CH">
<head>
    <meta charset="utf-8">
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <title>JSP - Hello World</title>
    <script type="text/javascript">
        function _change() {
            var imgEle = document.getElementById("vCode");
            imgEle.src = "VerifyCodeServlet?" + new Date().getTime();
        }
    </script>
</head>
<body>
<form action="RegisterServlet" method="post">User name:<label>
    <input type="text" name="username"/>
</label><br/>Verification code:<label>
    <input type="text" name="code" size="3"/>
</label>
    <img id="vCode" src="VerifyCodeServlet"/>
    <a href="javascript:_change()">I can't see it. Change it</a>
    <br/>
    <input type="submit" value="Submit"/>
</form>
</body>
</html>
Copy the code

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!