Spring Security (11) [cross-domain]

Eleven, cross-domain


Introduction

Cross-domain problems are a very common requirement in practical application development. There are several solutions for cross-domain problems in the Spring framework. After the introduction of Spring Security, the solutions for cross-domain problems have been added.

What is CORS

CORS (Cross-Origin Resource-Sharing) is a cross-domain resource sharing technical standard developed by W3C, and its purpose is to solve cross-domain requests from the front end. In JavaEE development, the most common front-end cross-domain request solution is the early JSONP , but JSONP only supports GETrequests, which is a big flaw, while CORS supports multiple HTTP request methods, which is also the current mainstream cross-domain solution plan

A new set of HTTP request header fields has been added to CORS. Through these fields, the server tells the browser which websites have permission to access which resources through the browser. At the same time, it is stipulated that for those HTTP request methods that may modify server data (such as HTTP requests other than GET, etc.), the browser must first use the method OPTIONSto initiate a pre-inspection request ( Pre inspection request ). The purpose of the pre-inspection request is to check whether the server Support the upcoming cross-domain request, and only send the actual HTTP request if the server allows it. In the return of the preflight request, the server can also notify the client whether to carry identity credentials (such as cookies, HTTP authentication information, etc.)

CORS: same origin/same domain = protocol + host + port

simple request

Take the GET request as an example. If you need to initiate a cross-domain request, the request header is as follows

Host: locahost:8080
Origin: http://locahost:8081
Referer: http://locahost:8081/index.html

If the server (8080) supports the cross-domain request, the returned response header will contain the following fields

Access-Control-Allow-Origin: http://locahost:8081

The Access-Control-Allow-Origin field is used to tell the browser the domain that can access the resource. When the browser receives such response header information, it extracts the Access-Control-Allow-Originvalue in the field and finds that the value contains the domain where the current page is located. This domain is allowed, so there are no restrictions on front-end cross-domain requests. This is a simple request, that is, a cross-domain request that does not require a preflight request

non simple request

For some non-simple requests, an AND check request will be sent first. The preflight request looks like this

OPTIONS /put HTTP/1.1
Host: locahost:8080
Connection: keep-alive
Accept: */*
Access-Control-Request-Method: PUT
Origin: http://localhost:8081
Referer: http://localhost:8081/index.html

The request method is OPTIONSthat the request header Origin tells the server the domain of the current page, and the request header Access-Control-Request-Methodstells the server the method to be used for the cross-domain request to be initiated. The server judges this, and if the upcoming cross-domain request is allowed, it will give the following response

HTTP/1.1 200
Access-Control-Allow-Origin: http://localhost:8081
Access-Control-Request-Methods: PUT
Access-Control-Max-Age: 3600
  • Access-Control-Request-Methods : Indicates allowed cross-domain methods
  • Access-Control-Max-Age : Indicates the validity period of the preflight request, in seconds. If the cross-domain request is initiated within the validity period, there is no need to initiate the preflight request again. After the preflight request is over, a real cross-domain request will be initiated next. The cross-domain request is similar to the previous simple request cross-domain steps

11.1 Spring cross-domain solution

Method 1: @CrossOrigin

The first way to deal with cross-domain in Spring is to @CrossOriginmark support for cross-domain through annotations, which can be added to methods or Controllers. When added on the Controller, it means that all interfaces in the Controller support cross-domain . The specific configuration is as follows

@RestController
public class TestController {
    
    

    @GetMapping("/cors")
    @CrossOrigin(origins = "http://127.0.0.1:8081")
    public String testCors() {
    
    
        return "Hello Cors!";
    }
}

insert image description here

@CrossOriginThe meaning of the annotation attributes is as follows

  • allowCredentials : whether the browser should send credential information, such as cookies (string)
  • allowedHeaders : The request header fields that the request is allowed *to represent all fields (array of strings)
  • exposedHeaders : which response headers can be exposed as part of the response (array of strings)
    • [ Note ] Here you can list them one by one, wildcards *are invalid here
  • maxAge : The validity period of the preflight request. During the validity period, there is no need to send the preflight request again. The default is 1800seconds
  • methods : Allowed request methods, *indicating that all methods are allowed (array of request methods)
  • origins : Allowed domains, *which means that all domains are allowed (string array)

Method 2: addCrosMapping

The @CrossOrigin annotation needs to be added to different Controllers. So there is another global configuration method, which is implemented by rewriting the WebMvcConfigurer# addCorsMappings () method. The specific configuration is as follows

package com.vinjcent.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
    
    

    // 用来全局处理跨域
    @Override
    public void addCorsMappings(CorsRegistry registry) {
    
    
        registry.addMapping("/**")  // 对哪些请求进行跨域
                .allowedMethods("*")
                .allowedOrigins("*")
                .allowedHeaders("*")
                .maxAge(3600);
    }
}

Method 3: CorsFilter

CorsFilter is a cross-domain filter provided in Spring Web, through which developers can also handle cross-domain

package com.vinjcent.filter;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
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;

import java.util.Arrays;

@Configuration
public class WebMvcFilter {
    
    

    @Bean
    public FilterRegistrationBean<CorsFilter> corsFilter() {
    
    
        // 1.定义需要注册的过滤器bean
        FilterRegistrationBean<CorsFilter> registrationBean = new FilterRegistrationBean<>();
        // 2.定义跨域配置信息
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
        corsConfiguration.setAllowedMethods(Arrays.asList("*"));
        corsConfiguration.setAllowedOrigins(Arrays.asList("*"));
        corsConfiguration.setMaxAge(3600L);

        // 3.定义过滤器生效的url配置
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfiguration);
        registrationBean.setFilter(new CorsFilter(source));
        registrationBean.setOrder(-1);  // 一般是0、1,代表过滤器顺序等级,-1代表优先


        return registrationBean;
    }
}

11.2 Spring Security cross domain solution

Principle analysis

When we added Spring Security dependencies to the project, we found that some of the above three cross-domain methods were invalid, while others could continue to be used

Configure cross-domain by @CorsOriginannotation or rewriting the addCorsMappings method, all of which are invalid. Whether cross-domain configuration through CorsFilter is invalid depends on the priority of the filter. If the filter priority is higher than the Spring Security filter, that is, it is executed before the Spring Security filter, the cross-domain processing configured by CorsFilter is still valid; if the filter priority is lower than the Spring Security filter, the cross-domain processing configured by CorsFilter domain processing will fail

In order to clarify this problem, let's briefly understand the execution order of Filter, DispatcherServlet and Interceptor

insert image description here

After understanding the order of execution, let's look at the cross-domain request process. Since the non-simple request first sends a pre-inspection request ( Pre inspection request ), and the pre-inspection request does not carry authentication information, the pre-inspection request may be intercepted by Spring Security. Therefore, cross-domain configuration through @CorsOriginannotations or rewriting the addCorsMappings () method will fail.If you use the cross-domain configuration of CorsFilter, as long as the filter priority is higher than the Spring Security filter, there will be no problem, and vice versa.

solution

Spring Security also provides a more professional way to solve the problems faced by preflight requests, such as

package com.vinjcent.config.security;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;

/**
 * 自定义 Security 配置
 * 在 springboot 2.7.x 后,WebSecurityConfigurerAdapter 配置不再存在
 */
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    
    

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    
    
        // 所有请求都需要认证
        http.authorizeRequests()
                .anyRequest()
                .authenticated();

        // 开启登录请求
        http.formLogin();

        // 解决跨域请求
        http.cors()
                .configurationSource(configurationSource());

        // 关闭 csrf 跨域请求伪造攻击
        http.csrf()
                .disable();
    }

    // 跨域请求配置
    public CorsConfigurationSource configurationSource() {
    
    
        // 1.定义跨域配置信息
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
        corsConfiguration.setAllowedMethods(Arrays.asList("*"));
        corsConfiguration.setAllowedOrigins(Arrays.asList("*"));
        corsConfiguration.setMaxAge(3600L);

        // 2.定义过滤器生效的url配置
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfiguration);

        return source;
    }
}

Guess you like

Origin blog.csdn.net/Wei_Naijia/article/details/128602826