[Detailed Explanation of Actual Combat] Detailed Explanation of Cookie and Session

Knowledge summary:

  1. Http request
  2. cookie
  3. session

1. HTTP session

To understand cookies and sessions, you must first understand the most important basic nature of http requests: http is stateless . That is to say, the http protocol will not recognize whether the two http requests are related, and whether the two http requests come from the same client. Each request should be a completely separate entity.

But at the same time, http has another fundamental property: http is extensible . It is reflected in the HTTP request header that contains the cookies field. Cookies can be saved to the browser. By writing the cookie to the request header, the browser allows multiple HTTP requests to share the same context information and provide it to the server for judgment. Note that HTTP is still stateless at this time, and a third party is required. It is the server to save the state.

2. Detailed description of cookies

2.1 Detailed introduction

We practice the life cycle of cookies through the interaction between springboot and the front end.

Look at Figure 1 first:

insert image description here

We can get the following information:

  1. A cookie is a key-value container, and a cookie can only hold one key-value [parameters passed in when new a cookie object].

  2. A cookie is said to have a valid scope, that is, it is stipulated that when the current cookie is accessed, it will be automatically written into the request header. For example, if I setPath("/"), it means that my current website accesses any backend interface, and there will be a cookie object in the request header of the request object. And if I setPath("/alpha"), it means that the request will only carry the cookie when accessing the /alpha related interface. Therefore, the cookie is sent through setPath and then automatically carried by the browser, rather than selectively carried by the front end.

  3. The cookie can set the survival time. We know that the cookie is stored in the file of the browser. The life cycle starts from the front-end receiving the response, and it will be automatically destroyed as soon as the time is up.

  4. The cookie is created by the response body, so the created cookie is added to the response body

Look at Figures 2 and 3 again:
insert image description hereinsert image description here

We can get more information:

  1. The cookie is indeed created by the response body, that is, the server will write the instruction to create the cookie into the response body, and execute set-cookie after the response body reaches the browser. Use set-cookie to set the content of the cookie, the lifetime of the cookie and the path of the cookie

  2. The cookie in the request header is indeed carried automatically

The interface obtains the cookie carried in the request header:

The key of the cookie can be specified in the parameter. Because there may be more than one cookie in the request header.

insert image description here

Of course, you can get all the cookies from the request object [this method gets Cookie[], you have to traverse to know where the cookie you want]. A ready-made cookie acquisition tool is provided here.

public class CookieUtil {
    
    
    public static String getValue(HttpServletRequest request, String name) {
    
    
        if(request == null || name == null) {
    
    
            throw new IllegalArgumentException("参数不可为空");
        }

        Cookie[] cookies = request.getCookies();
        if(cookies != null) {
    
    
            for(Cookie cookie : cookies) {
    
    
                if(cookie.getName().equals(name)) {
    
    
                    return cookie.getValue();
                }
            }
        }
        return null;
    }

    //删除cookie,删除的关键是把其寿命设置为0ms即可
    public static void removeCookie(HttpServletRequest request, HttpServletResponse response, String name){
    
    
        Cookie[] cookies = request.getCookies();
        if (null != cookies) {
    
    
            for (int i = 0; i < cookies.length; i++) {
    
    
                if (cookies[i].getName().equals(name)){
    
    
                    cookies[i].setPath("/");
                    cookies[i].setMaxAge(0);
                    response.addCookie(cookies[i]);
                    break;
                }
            }
        }
    }
}

Summarize:

  • A cookie is a key-value key-value pair data, and the value can only be a string, not an object
  • When the server creates a cookie, you can set the effective range setPath ("/") as the intrinsic attribute of the cookie. The path attribute of the cookie will determine whether the cookie will be automatically carried into the request header when the browser accesses the interface.
  • When the server creates a cookie, its life cycle can be set. The parameter unit is ms milliseconds
  • The cookie is actually created and saved in the user's browser by the response body in the user's browser.

2.2 For actual combat

After reading 2.1, you should know an "industrial chain" of cookies, that is, the circulation of cookies

The following uses login credentials, that is, to realize the "remember login status" business to deepen understanding

  • Front-end input form, click login button

insert image description here

  • The backend receives parameters and matches them with the database to check whether the user is legal
  • If it is legal, set a credential [random string, which can be generated by UUID class] as a cookie and write it into the response object. Entering the response object does not require any operation, and the response object will be automatically passed to the front end. Must remember! We need to store this credential for us to match later. You can call the dao class to store in mysql, or directly store it in redis.

insert image description here

  • The front end receives the response header and creates a cookie, and at the same time logs in successfully and enters the home page

insert image description here

  • Refresh the current homepage, you can see that the browser automatically carries the cookie to the request header, which is automatically carried based on the path attribute of the cookie

insert image description here

  • Therefore, all interfaces corresponding to the backend need to add an additional @CookieValue(“code”) String code parameter to receive user login credentials. Because an interface is a service, we only provide services to legitimate users, so we judge that as long as the current certificate is legal, we will provide the current interface service. Otherwise, if there is no certificate or the certificate is wrong or the certificate is expired, an error will be returned. If there is a router in the front end, then Let the front end jump to the login page. If the front and back ends are not separated, redirect directly to the login page.

insert image description here

  • But it will be very troublesome to intercept cookies for each interface. It is recommended to use the interceptor to receive the cookie from the request and verify the validity of the credential, that is, match it with the database. If the certificate is legal, it is allowed to access the controller class, otherwise the interceptor class, that is, the interceptor class, will terminate the request.

    • In the preHandle of the interceptor class (that is, before entering the controller, it will enter the method) as follows: [The interceptor learns by itself]
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
          
          
        //requets请求中会自带所有的与本网站相关的cookie,利用刚刚的cookieUtil工具
        String cookie = CookieUtil.getValue(request, "ticket");
        if(cookie == null) {
          
          
            //没有凭证,跳转登录页面。我使用的vue,前端在响应体数据为null的情况下会跳转路由,所以return false即可。
            return false;
        }
        //如果有cookie,查询凭证是否是数据库合法凭证
        //我采用的是把cookie存入到redis中。这里看整体逻辑即可。
        String redisKey = RedisKeyUtil.getTicketKey(cookie);
        TicketCookie ticket = (TicketCookie) redisTemplate.opsForValue().get(redisKey);
        if(ticket == null) {
          
          
            //凭证不合法,即没有记录
            return false;
        }
        //如果合法,判断凭证是否过期
        if(ticket.getStatus() == 1) {
          
          
            //过期了,不给进
            //return false会直接终止线程,也就是前端会显示200,但是线程终止,无服务提供
            return false;
        }
        
        //我们存储凭证到数据库的时候,肯定要存储该cookie是谁的嘛,所以从数据库拉凭证的时候可以获取当前用户uid,这里提供user方便后面controller中需要使用。
        //把当前用户塞到request对象中,让其他服务获取
        request.setAttribute("user", userService.queryUserById(ticket.getUid()));
        return true;
    }
    

At this point, the process of using cookie credentials should be quite clear.

3. Session details

session life cycle

  • The user will create a session when accessing the servlet for the first time
  • Tomcat will clear long-term unused sessions, the default is 20 minutes, which can be modified [Once the access timer is cleared]
  • Call the invalidate method of Session to clear all sessions of the request
  • session can set the expiration time
  • The cookie storing the sessionid is automatically generated by the server. Its maxAge attribute is generally -1, which means that it is only valid in the current browser, and it is not shared between browser windows. It will become invalid when the browser is closed, so it can also be understood as closing the client . The end session will be invalid . Therefore, when two browser windows on the same machine access the server, two different Sessions will be generated

The first is to create a session to store data:

After we log in successfully, we can, of course, the premise is that the HttpSessionSession object is written in the interface method. Writing a session in the method parameter will create a session by default and return a cookie containing the sessionID. The process of creating a session is handed over to tomcat, and the process of returning the sessionID is also handed over to the response object.

insert image description here

What is sessionID? As long as a session is created for an interface you are currently requesting to access, tomcat will automatically assign a sessionID as the unique identifier of the new session. SessionID is a self-contained attribute of the session. We do not need to specify it . We only need to specify the data inside [The id in the picture is data instead of sessionID]!

After the sessionID is automatically created and assigned to the session object by tomcat, tomcat will also insert a cookie into the response body of the current processing request, similar to the behavior of response.addCookie. This cookie is sessionID, and this cookie will be automatically carried into the request header when accessing the controller method with session parameters. [Why JSESSIONID? J means java, the default name of tomcat]

insert image description here

Then fetch data in session:

As developers, we don't need to look up the session container. You only need to specify a session object, and tomcat can find the corresponding instance object and return it to us.

That is to say, as long as a request is made to access a method with a session, a cookie will be automatically carried, including a sessionID cookie.

Once this cookie arrives at the server, it is immediately handed over to tomcat to find the corresponding session instance and assign it to the current method parameter.
insert image description here

Summarize:

Solve a few questions when summed up:

Why can the session record the current user information? Will multiple users create sessions without access errors?

For example, session stores user information:

  • One browser corresponds to one user. When a login request is sent, springboot will open a thread [a user requests to open a thread]
  • After the login is successful, the server saves the current user's login information into the session and returns the jsession to the current user, and the current request thread ends.
  • That is to say, if multiple people log in, it will be processed by multiple threads, and each thread will create a session, so there will be n sessions stored on the server, and the data content of these n sessions will be different. Will return the sessionID to the browser.
  • The front end accepts the sessionID cookie and stores it in the browser. When the user requests the interface method with session parameters again, it will carry a unique sessionID. The sessionID of different users must be different, and it must be able to distinguish between n sessions. open. [If you want to use the data in the session, you must write the session in the method parameter]
  • Therefore, one user has one thread, one thread creates one session, and one session corresponds to a unique sessionID cookie. Throughout the process, each session corresponds to a user, which is unique.

Opening two browser windows to access the application will use the same session or different sessions?

Remember, requests are stateless, a browser is regarded as a user, and a user request corresponds to a thread.

Why is the session not used frequently by projects?

In the current era of pursuing high concurrency, there are many disadvantages of session.

  • If there is only one server, it is no problem to use session
  • But if it is a distributed deployment, the session cannot be shared.
    • Solution 1: One ip is fixedly assigned to one server. But there is another problem: it is difficult to achieve load balancing
    • Solution 2: Synchronize session. Sync conflict issues and performance issues. data redundancy problem
    • Solution 3: Share the session. Dedicate a new server to save the session, and other servers get it from this server. But stand-alone is easy to hang up
    • Solution 4: Use cookies if there are cookies, and store important information in the database cluster [already reliable by default]. Solved the sharing issue. There will be a performance bottleneck when the concurrency is large
    • Mature solution 5: Use cookies if there are cookies, and store important information in the redis cluster [reliable by default]. It can solve concurrency problems very well.
  • So session is now less used for large websites, and cookies are used for cookies

4. Summary

Get to know these two things first.

Now the mainstream use is to use cookie certificates, which is the method used in cookies just introduced, and sessions should be eliminated.

If the user credentials are stored in the database, who owns the credentials will definitely be stored. We only need to intercept the corresponding cookie in the interceptor. Match the credential information with the database, get the id of the owner of the credential, and then query it. In the interceptor, the user object can be put into the request object for the controller to use. This completely replaces the session functionality. Credentials stored in the redis cluster can also have good concurrency.

To recap:

  • Front-end input form, click login button

  • The backend receives parameters and matches them with the database to check whether the user is legal

  • [ Create cookie ] If it is legal, set a credential [random string, which can be generated by UUID class] as a cookie, set the path to setPath("/") , and write it into the response object. Entering the response object does not require any operation, and the response object will be automatically passed to the front end. Must remember! We need to store this credential for us to match later. You can call the dao class to store in mysql, or directly store it in redis.

  • The front end receives the response header and creates a cookie, and at the same time logs in successfully and enters the home page

  • Refresh the current homepage, you can see that the browser automatically carries the cookie to the request header , which is automatically carried based on the path attribute of the cookie

  • Because each interface intercepts cookies, it will be very troublesome. It is recommended to use the interceptor to receive the cookie from the request and verify the validity of the credential, that is, match it with the database. If the certificate is legal, it is allowed to access the controller class, otherwise the interceptor class, that is, the interceptor class, will terminate the request. After the matching is successful, obtain the current user information from the credential , find the corresponding user object in the database and add it into the request object for the released controller method to use.

Guess you like

Origin blog.csdn.net/NineWaited/article/details/126211560