1. Same-origin policy

The same origin policy is a famous security policy proposed by Netscape. It is the core and most basic security feature of the browser. Now all browsers that support JavaScript use this policy. The same protocol, domain name, and port must be the same. The same-origin policy is based on security considerations, the strategy itself is no problem, but we are in the actual development, due to various reasons and often have a need to cross domain, the traditional cross-domain solutions is the json, although the json can solve the cross-domain but there are a lot of limitations, it is only support a GET request, does not support other types of request, CORS (Cross-Origin Resource Sharing) is a W3C standard. It is a specification of browser technology that provides a way for Web services to sandbox scripts from different domains to avoid the same origin policy of browsers. This is the modern version of the JSONP pattern.

The Spring framework also provides a solution for CORS.

Nonhomologous restriction

  • Cookies, LocalStorage, and IndexedDB of non-cognate web pages cannot be read
  • DOM of non-homologous web pages cannot be accessed
  • Unable to send AJAX requests to non-same-origin addresses

Second, Java backend implementation of CORS cross-domain request

  1. Returns a new CorsFilter
  2. Rewrite WebMvcConfigurer
  3. Use the annotation @crossorigin
  4. Manually set the response header (HttpServletResponse)
  5. Custom Web Filters are implemented across domains

Note:

  • CorFilter / WebMvConfigurer / @CrossOriginOnly SpringMVC 4.2 or later is supported, corresponding to springBoot 1.3 or later
  • The first two methods belong to global CORS configurations, while the last two methods belong to local CORS configurations. Local cross-domain rules override global cross-domain rules if used, so you can use @crossorigin annotations for more fine-grained cross-domain resource control.
  • In either case, the goal is to modify the response header and add the data required by the browser to the response header to cross domains

1. Return new CorsFilter(global cross-domain)

In any configuration class, return a new CorsFIlter Bean and add the mapping path and the specific CORS configuration path.

@Configuration
public class GlobalCorsConfig {
    @Bean
    public CorsFilter corsFilter(a) {
        //1. Add CORS configuration information
        CorsConfiguration config = new CorsConfiguration();
        // Which primitive fields are allowed
        config.addAllowedOrigin("*");
        // Whether to send cookies
        config.setAllowCredentials(true);
        // Which requests are allowed
        config.addAllowedMethod("*");
        // Which original request headers are allowed
        config.addAllowedHeader("*");
        // What header information is exposed
        config.addExposedHeader("*");
        //2. Add a mapping path
        UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
        corsConfigurationSource.registerCorsConfiguration("/ * *",config);
        //3. Return a new CorsFilter
        return newCorsFilter(corsConfigurationSource); }}Copy the code

2. Rewrite WebMvcConfigurer(global Cross-domain)

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/ * *")
              // Whether to send cookies
              .allowCredentials(true)
              // Which primitive fields are allowed
              .allowedOrigins("*")
              .allowedMethods(new String[]{"GET"."POST"."PUT"."DELETE"})
              .allowedHeaders("*")
              .exposedHeaders("*"); }}Copy the code

3. Use annotations (local cross-domain)

Use the annotation @crossorigin: on the controller (on the class) to indicate that all methods of the class are allowed to cross domains.

@RestController
@CrossOrigin(origins = "*")
public class HelloController {
    @RequestMapping("/hello")
    public String hello(a) {
        return "hello world"; }}Copy the code

Use annotations on methods @crossorigin:

@RequestMapping("/hello")
@CrossOrigin(origins = "*")
// @crossorigin (value = "http://localhost:8081") // Specify a specific IP address that can cross domains
public String hello(a) {
    return "hello world";
}
Copy the code

4. Manually set response headers (local cross-domain)

The HttpServletResponse object is used to add a response header (Access-Control-allow-Origin) to authorize the original field, where the Origin value can also be set to “*” to permit all.

@RequestMapping("/index")
public String index(HttpServletResponse response) {
    response.addHeader("Access-Allow-Control-Origin"."*");
    return "index";
}
Copy the code

5. Use custom filters to achieve cross-domain implementation

Start by writing a filter, which you can call mycorsfilter.java

@Component
public class MyCorsFilter implements Filter {
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin"."*");
        response.setHeader("Access-Control-Allow-Methods"."POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age"."3600");
        response.setHeader("Access-Control-Allow-Headers"."x-requested-with,content-type");
        chain.doFilter(req, res);
    }
    public void init(FilterConfig filterConfig) {}
    public void destroy(a) {}}Copy the code

Configure this filter in web.xml for it to take effect

<! -- Cross-domain access START-->
<filter>
  <filter-name>CorsFilter</filter-name>
  <filter-class>com.mesnac.aop.MyCorsFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>CorsFilter</filter-name>
  <url-pattern>/ *</url-pattern>
</filter-mapping>
<! END -->
Copy the code

Three examples,

Create two common SpringBoot projects. The first one is named Provider to provide the service, the second one is named Consumer to provide the service, the first configuration port is 8080, the second configuration port is 8081, and then provide two Hello interfaces and one GET on the provider. A post, as follows:

@RestController
public class Provider {
    @GetMapping("/hello")
    public String hello(a) {
        return "hello";
    }

    @PostMapping("/hello")
    public String hello2(a) {
        return "post hello"; }}Copy the code

Create an HTML file in the Resources/Templates directory of the Consumer and send a simple Ajax request as follows:

<! DOCTYPEhtml>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>CORS</title>
  </head>
  <body>
    <div id="app"></div>
    <input type="button" onclick="btnClick()" value="get_button">
    <input type="button" onclick="btnClick2()" value="post_button">
    <script>
        function btnClick() {
            $.get('http://localhost:8080/hello'.function (msg) {$("#app").html(msg);
            });
        }

        function btnClick2() {
            $.post('http://localhost:8080/hello'.function (msg) {$("#app").html(msg);
            });
        }
    </script>
  </body>
</html>
Copy the code

Then launch the two projects separately, send the request button, and observe the browser console as follows:

Access to XMLHttpRequest at ‘http://localhost:8080/hello’ from origin ‘http://localhost:8081’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource

As you can see, the request could not be sent due to the same origin policy.

Using CORS allows cross-domain implementation without any changes to the front-end code, so let’s look at how to configure it in a provider. You can first configure a method to accept requests for a field using the @Crossorigin annotation as follows:

@RestController
public class Provider {
    @CrossOrigin(value = "http://localhost:8081")
    @GetMapping("/hello")
    public String hello(a) {
        return "hello";
    }

    @CrossOrigin(value = "http://localhost:8081")
    @PostMapping("/hello")
    public String hello2(a) {
        return "post hello"; }}Copy the code

This annotation indicates that the two interfaces accept requests from http://localhost:8081, and when configured, restart the provider and send the request again, so that the browser console does not report an error and the consumer can get the data.

In the Spring Boot, you can solve this problem in one time by configuring the addCorsMappings method in the configuration class, which is as follows:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/ * *")
                .allowedOrigins("http://localhost:8081")
                .allowedMethods("*")
                .allowedHeaders("*"); }}Copy the code

/** indicates that all methods of the application process cross-domain requests, allowedMethods indicates the number of requests that are allowed to pass, and allowedHeaders indicates the allowed request headers. After this configuration, it is not necessary to configure cross-domains separately on each method.

Four, existing problems

After understanding the whole working process of CORS, we send cross-domain request through Ajax. Although the user experience is improved, there are also potential threats, the common one is CROSS-site Request Forgery (CSRF). Cross-site request forgery, also known as one-click attack or Session riding, commonly abbreviated CSRF or XSRF, is an attack that forces a user to perform unintentional actions on a currently logged Web application. For example:

If a bank uses the following URL to run money transfers: http://icbc.com/aa?bb=cc, a malicious attacker could place the following code on another website: if a user visits a malicious site and her login information has not expired after a recent visit to the bank, she will suffer a loss.

Based on this, the browser will classify the request in actual operation, including simple request, pre-request, request with credentials, etc. Pre-request will first send an Options detection request, and negotiate with the browser whether to accept the request. By default, cross-domain requests do not require credentials, but the server can be configured to require credentials from the client, thus effectively avoiding CSRF attacks.