[Spring MVC series] don't use Filter. Try Spring's own way to deal with CORS cross domain problems

Posted by ziah75 on Thu, 02 Dec 2021 22:52:41 +0100

From CORS to Spring MVC

Cross origin resource sharing (CORS) is also often translated as cross domain resource sharing. As a W3C standard, it allows browsers to make requests to cross source servers, overcoming the limitation that AJAX can only be used in the same source.

CORS requires both browser and server support. When the browser initiates a cross domain request, it will automatically carry some request headers. If the server allows cross domain, it will also automatically add some response headers. As a server, Spring MVC also supports CORS and provides a variety of solutions.

To understand CORS, start with browser homology strategy

The same origin policy was introduced by Netscape in 1995. At present, all browsers follow the same origin policy.

Browser homology policy

Homology means that the protocol, domain name and port number of the two web pages are consistent. The original purpose of the homology policy is to protect the cookie s set by web page A from being read in web page B.

Imagine that if the user opens the bank website and phishing website at the same time, if there is no homologous strategy, the phishing website will initiate transfer or read the user's sensitive information after getting the cookie from the bank website, which will be very dangerous.

Non homologous web pages have strict restrictions on cookies, LocalStorage, IndexDB reading, Dom document acquisition and AJAX requests. The same origin policy stipulates that AJAX requests can only be made to the same origin web address. CORS is the standard solution to cross domain AJAX, which is more flexible than using JSONP.

CORS processing flow

Browser requests can be divided into simple requests and non simple requests. Browsers have different CORS processing methods for different types of requests.

Simple request

A request that meets the following three conditions can be called a simple request.

  1. The request method is one of GET, HEAD and POST.
  2. The allowed request headers include Accept, Accept language, content language, and content type.
  3. Content type values are limited to text/plain, multipart / form data, application/x-www-form-urlencoded.

The simple request process is shown in the figure below.

When the browser initiates a request, it adds an Origin field in the request header to indicate the source of the current resource. The server checks this field after receiving the request. If the request is allowed, it adds an access control allow Origin field in the response header. Otherwise, it can reject the request and return an incorrect HTTP response code, If the browser finds that there is no access control allow Origin field or the value of access control allow Origin field is incorrect after receiving the response, it will print an error message that cross domain is not allowed on the console.

Non simple request

For non simple requests, the browser will first initiate a Preflight Request to check whether cross domain is allowed. If cross domain is allowed, the real request will be executed. The process is as follows.

The HTTP method of pre request is OPTIONS, and the carried request header is as follows:

  • Origin: indicates the source of the resource.
  • Access control request method: represents the request header of the real HTTP request method.
  • Access control request headers: represents the custom request header of the real HTTP request method.

If the server allows cross domain requests, the following fields will be added to the response header:

  • Access control allow origin: indicates the source allowed for cross domain requests, * indicates any source allowed.
  • Access control request method: indicates the allowed request methods for cross domain requests, which can be more than those in the request header.
  • Access control request headers: indicates the request headers allowed for cross domain requests.

If the server does not allow cross domain requests, you can directly return the HTTP response code indicating the error.

After the browser receives the pre request response, check the response header to determine whether cross domain is allowed. If cross domain is not allowed, print the cross domain error message directly on the console. If cross domain requests are allowed to be initiated normally, the request header Origin, access control request method, access control request headers and custom request headers are carried.

Cross domain request with user identity

After the server checks the cross domain request, in addition to returning the basic response header, the following additional response headers can be added:

  • Access control Max age: indicates the number of seconds that cross domain inspection results can be cached in the browser.
  • Access control expose headers: by default, JS can only obtain some basic response headers. This field allows JS to obtain other response headers except the basic response headers.

In addition, the server can also return the response header access control allow credentials with the value of true, which allows the browser to carry Cookie information when making cross domain requests. Of course, withCredentials=true needs to be configured when initiating requests.
If the server returns the response header access control allow credentials, access control allow origin cannot return *, otherwise the request will fail.

Spring MVC CORS processing

Because each interface needs to handle cross domain requests, Filter is usually used for global processing in traditional Java Web projects.

The core class for cross domain processing in Spring MVC is HandlerMapping. When the request arrives at the DispatchServlet, if the request is a pre request, Spring will replace the processor with a cross domain processor. If the request is a non pre request, Spring will add a cross domain interceptor in front of the interceptor chain, and then process it according to the CORS configuration. Then the dispatcher servlet flow chart is presented. The parts related to CORS can be seen in the upper right corner.

CorsFilter

Filter is a traditional way to solve cross domain problems. Before Spring, we often wrote a cross domain filter to add fixed fields to the response header when the request arrives.

Spring MVC provides a CorsFilter with the same function, so we don't need to write a separate Filter for cross domain processing for each project in the future. For the method of configuring Filter in spring MVC, see [Spring MVC series] there are six ways to configure filters in Spring MVC. Let's see what you know.

An example of configuring CorsFilter in SpringBoot environment is as follows.

@Configuration
public class WebMvcConfig {

    @Bean
    public Filter corsFilter() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("http://hukp.cn");
        corsConfiguration.addAllowedMethod(HttpMethod.POST);
        corsConfiguration.addAllowedHeader("token");
        corsConfiguration.setExposedHeaders(Arrays.asList("header1", "header2"));
        corsConfiguration.setMaxAge(3600L);
        corsConfiguration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
        corsConfigurationSource.registerCorsConfiguration("/*", corsConfiguration);
        CorsFilter corsFilter = new CorsFilter(corsConfigurationSource);
        return corsFilter;
    }

}

When instantiating CorsFilter, you need to specify a CorsConfigurationSource instance to obtain cross domain configuration CorsConfiguration. The common implementation is UrlBasedCorsConfigurationSource.

Global CORS configuration

Spring MVC's official solution to CORS is to use PreFlightHandler as the processor or add the interceptor CorsInterceptor according to whether it is a pre request when handlermapping obtains the processor chain. For details, see the source code abstracthandlermapping#getcorshandleexecutionchain.

For users, you only need to configure CORS. The configuration is divided into global configuration and local configuration. Spring will merge the two configurations. For global configuration, there are two configuration methods: API and XML.

XML configuration

XML configuration is an early support provided by Spring. The CORS configuration equivalent to the CorsFilter above is as follows.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <mvc:cors>
        <mvc:mapping path="/*"
                     allowed-origins="http://hukp.com"
                     allowed-methods="POST"
                     allowed-headers="token"
                     exposed-headers="header1, header2"
                     max-age="3600"
                     allow-credentials="true"
        />
    </mvc:cors>
</beans>

API configuration

At present, annotations have become the mainstream use of Spring. After opening Web related features with @ EnableWebMvc, cross domain configuration can be carried out through the implementation interface webmvcconfigger. Finally, this configuration will be passed to AbstractHandlerMapping. The API equivalent to XML is configured as follows.

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/*")
                .allowedOrigins("http://hukp.cn")
                .allowedMethods("POST")
                .allowedHeaders("token")
                .exposedHeaders("header1", "header2")
                .maxAge(3600)
                .allowCredentials(true);
    }
}

Local CORS configuration

In addition to global configuration, Spring can also make special configuration for each processor.

API configuration

If you want to use a processor class to process a request, this processor class can implement the interface HttpRequestHandler, Controller or HandlerFunction. If you want to perform CORS processing for this processor, you also need to implement the interface CorsConfigurationSource. Take the login scenario as an example. The example code is as follows.

public class LoginHandler implements Controller, CorsConfigurationSource {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // Omit related logic
        return new ModelAndView();
    }

    // Get CORS configuration
    @Override
    public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("http://hukp.cn");
        corsConfiguration.addAllowedMethod(HttpMethod.POST);
        corsConfiguration.addAllowedHeader("token");
        corsConfiguration.setExposedHeaders(Arrays.asList("header1", "header2"));
        corsConfiguration.setMaxAge(3600L);
        corsConfiguration.setAllowCredentials(true);
        return corsConfiguration;
    }
}

Annotation configuration

After Spring adds support for annotations, we annotate the Controller class with @ Controller, and then annotate the processor method with @ RequestMapping in the class. In this way, since the request is processed by the method, we can't implement CorsConfigurationSource for cross domain configuration, but Spring also provides the corresponding solution.

We can use the @ CrossOrigin annotation for cross domain configuration. We can add this class to the controller class or controller method. The @ CrossOrigin on the controller class is applicable to all controller methods, and the @ CrossOrigin on the controller method is applicable to itself. If the class and method have @ CrossOrigin annotation, Spring will merge the configuration.

The sample code is as follows.

@Controller
@CrossOrigin(origins = "http://hukp.cn",
        allowedHeaders = "token")
public class LoginController {

    @CrossOrigin(methods = RequestMethod.POST,
            exposedHeaders = {"header1", "header2"},
            maxAge = 3600L,
            allowCredentials = "true")
    @PostMapping("/login")
    public ModelAndView login(HttpServletRequest request) {
        // Omit business logic
        return new ModelAndView();
    }
}

When the request / login arrives, the combined CORS configuration on the class and method will be used.

Spring Security CORS processing

In the Spring Boot environment, if Spring Security is introduced, Spring will automatically configure CorsFilter. At this time, read the CORS configuration from the beans of CorsConfigurationSource type, so configure CorsConfigurationSource as a bean. The sample code is as follows.

@Configuration
public class WebMvcConfig {

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("http://hukp.cn");
        corsConfiguration.addAllowedMethod(HttpMethod.POST);
        corsConfiguration.addAllowedHeader("token");
        corsConfiguration.setExposedHeaders(Arrays.asList("header1", "header2"));
        corsConfiguration.setMaxAge(3600L);
        corsConfiguration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
        corsConfigurationSource.registerCorsConfiguration("/*", corsConfiguration);
        return corsConfigurationSource;
    }
}

summary

This article mainly gives a brief introduction to the CORS specification, and comprehensively introduces several methods of CORS configuration in Spring MVC. I hope you will also try to understand the content behind it when learning technology, so as to know what it is and why. If you have any questions, please leave a message.

Topics: Spring mvc cross-domain cors