background

In A multi-service project based on SpringCloud, inter-service invocation is implemented through Spring’s RestTemplate. Background module A has A task to periodically clean up invalid business data. When it calls the API of Web service B, Unexpectedly, the Token has been expired.

Technical Background:

  1. The Web service permission authentication uses a Token. After the login verification is successful, the Token is refreshed and invokedresponse.addCookieReturn to the caller — browser or internal service.
  2. An interceptor for a Web service that extracts tokens in the order of request parameters, Cookie, Header, and then validates the Token. The Token is refreshed to the Cookie of the response object.
  3. The validity period of the Token is 30 minutes.
  4. Background scheduled task moduleAThe module is invoked every hourBThe API.

For Web service module B, the request source is the user’s browser and internal service A. After passing the Token check, the Token will be refreshed and set in the Cookie of the response object, which will be sent to the server in the next request.

After each service is stable, scheduled task A calls service B’s API, never successfully.

Cookie failure during background service invocation

Problem Description: When the background service A calls an API of B, new Token information is generated and the header domain is set, but the Token expires problem occurs in all the calls except the successful call once when the application is started.

Problem analysis: After analyzing the authentication process, the Web interceptor refreshes the Token information and returns it to the RestTemplate object if an internal service invocation is detected. The interceptor extracts the Token in the following sequence: request parameters, Cookie, and Header.

// Request parameter String token = request.getParameter("token"); // Cookie if (StringUtils.isEmpty(token)) { Cookie[] cs = request.getCookies(); if (cs ! = null) { for (Cookie c : cs) { if ("token".equals(c.getName())) { token = c.getValue(); break; } } } } //Header if (ObjectUtils.isEmpty(token)) { token = request.getHeader("token"); }Copy the code

The root of the problem: The background module A executes once an hour, and each time it calls the Spring-hosted RestTemplate singleton to send A request to B, it contains the Cookie information received in the last request. Although the header field information of the Token is generated at the same time, the Cookie is verified preferentially on the Web end. Therefore, the authentication result is always invalid and the request is rejected.

The revelation of

One of the best lessons I’ve learned from the Internet is never to copy and paste code you didn’t write yourself. If you must copy, type it word for word and force yourself to think about what the code actually means.

This is a sentence that impressed me deeply when I read science and Technology weekly yesterday. Although the problem in this paper is not caused by copy and paste, I have never deeply analyzed other people’s code or other people’s thoughts.

When I was temporarily assigned to solve this problem, I briefly looked at the code analysis as follows:

  1. The backend service sets the Token to the header field
  2. The Web module gets the Token only from cookies and request parameters, not from headers

I thought I would just fetch it from the Header, and I mistakenly added it after the Cookie, so I didn’t solve the problem.

After repeatedly adding logs, the Token of each information is printed. It is found that the Token used in parsing is different from the header field, and the Token expiration time is regular, that is, the time when the last scheduled task is invoked. Suddenly I realized that the Token used by the Web service was different from what I thought. Who changed the Token?

The answer is Cookie, and the RestTemplate sends the request with the Cookie from the previous one. The 30-minute period of validity will be long gone by the time the next scheduled task is executed.

Note: When using RestTemplate for service calls, it is best to clear the Cookie information to avoid the situation in this article. Second, as a singleton hosted by Spring, if you access the API of a different system, there is bound to be Cookie confusion and failure!