1. The background

The initial solution to Springboot’s cross-domain problem was to add the @Crossorigin annotation directly to the Controller, enabling cross-domain requests to be separated from the front and back ends. However, with the writing of business code, token session persistence check and the addition of interceptors, cross-domain problems appeared again. I wonder why cross-domain interception is suddenly a problem, since cross-domain requests are supposedly allowed in the background and previous tests have shown this to be the case.

2. Analysis and solutions

The login interceptor verifies the request. For the interface that can be accessed only after login, if the request header does not carry the token, the request is illegal and the 404 code is directly returned. Since the token in the request header of the intercepted request is always printed, when the problem occurs, the token value printed by the console is null, and the developer tool of the browser is opened to view the request header, but the request header is not carried successfully. The above problems do not exist before the addition of interceptors, are normal, so do not consider is the front end of the problem, the problem must be in the back end, to be accurate is the interceptor.

So why can’t browsers successfully send tokens? According to the clues, after a more detailed review of CROS ‘introduction, it was found that CROS would first send an OPTIONS request for sniffing when a complex request was made, to test whether the server supported the request. The real request would be sent only after the request was successful. However, the OPTIONS request does not carry any data. As a result, the request does not comply with the verification rules of our interceptor and is intercepted, and the status code is returned directly. The response header does not carry the header information needed to solve the cross-domain problem. Therefore, the browser debugging tool will find that the request does not carry the token, and the backend console will print the token as null.

== But, even so, why did the request end prematurely before adding cross-domain related headers? Do custom interceptors take precedence over @Crossorigin annotations? = =

If a Controller annotates a class with a @Crossorigin annotation or a method with a @Crossorigin annotation, Spring will record the corresponding cross-domain request mapping when recording the mapper mapping. Return the results to the AbstractHandlerMethodMapping, when a cross-domain request to come over, the Spring in acquiring handler will determine whether the request is a cross-domain request, and if so, will return a handler can handle cross domain. In summary, @Crossorigin is a strong binding to Handler.

So now the question is: what is the order of execution between handlers and interceptors? = =

DispatchServlet. DoDispatch () method is the core of for SpringMVC entry method, after analysis found that all the = = = = preHandle interceptor () method of execution are in actual handler methods (such as an API corresponding business method) = = = = before, Any interceptor that returns false skips all subsequent processing. On preview for SpringMVC request processing are PreFlightHandler. HandleRequest () in the treatment, in the entire process chain from the rear. Because there is no data in the precheck request, it was intercepted by the permission interceptor first.

Therefore, every request that fails to obtain the token is an OPTIONS request, so the solution is clear: all OPTIONS requests are allowed.

// The interceptor checks the request first, and permits the request if it is an OPTIONS request
if("OPTIONS".equals(httpServletRequest.getMethod().toUpperCase())) {
    System.out.println("Method:OPTIONS");
	return true;
 }
Copy the code

When I tested it, I found that the OPTIONS message was printed, indicating that there was an OPTIONS request and that the problem was successfully resolved.

Supplement 1: Another solution:

Instead of using @Crosorigin annotations to solve cross-domain problems, use filters: The example uses a CorsFilter, which is just a filter that encapsulates solving cross-domain problems.

Since CorsFilter is a Filter defined in the Web container (which implements javax.servlet.filter), its execution sequence precedes the servlet, and SpringMVC’s entry point is the DispatchServlet. Therefore, this Filter executes before all SpringMVC interceptors. Analyzing the code, we know that CorsFilter can handle the Cors configuration corresponding to the single request obtained. By the time the request reaches the interceptor, the cross-domain sniffing OPTIONS request will have returned with the answer and will not reach the interceptor after testing.

package com.example.pahms.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class GlobalCorsConfig {

    @Bean
    public CorsFilter corsFilter(a) {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOrigin("*");
        config.setAllowCredentials(true);
        config.addAllowedMethod("*");
        config.addAllowedHeader("*");
        config.addExposedHeader("token");
        UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
        configSource.registerCorsConfiguration("/ * *", config);
        return newCorsFilter(configSource); }}Copy the code

Supplement 2: There is another method that only provides ideas but does not implement.

== Using AOP wrap to enhance the preHandle() method of all custom interceptors to skip the interception of precheck requests (sniffs) is equivalent to putting the clearance OPTIONS operation in the interceptor into the AOP entry function to achieve global clearance. = =

Interceptors rely on the Web framework, which in SpringBoot’s case is the SpringMVC framework. On implementation, based on the Java reflection mechanism, is a kind of aspect oriented programming (AOP) is applied, is in front of the service or a method, call a method, or in the method, call a method, simple implementation, such as dynamic proxy is interceptor before calling method to print out a string (or do other business logic operation), You can also print a string after calling a method, or even do business logic when an exception is thrown. Because interceptors are web framework-based calls, Spring’s dependency injection (DI) can be used for some business operations, and an interceptor instance can be called multiple times over the lifetime of a Controller. However, the disadvantage is that only controller requests can be intercepted, and other requests such as direct access to static resources cannot be intercepted.

3. The conclusion

Above, hope to help you ~