Cross-domain problem pitfall records - with detailed solutions for multiple error reports

Cross-domain is due to the same-origin policy restriction of the browser. Same-origin policy (Sameoriginpolicy) is a convention. It is the core and most basic security function of the browser. When a request URL's protocol, domain name, and port are used, 2>If any one of the three is different from the current page URL, it is cross-domain.

The same origin policy is a security feature of the browser. Client scripts from different origins cannot read or write each other's resources without explicit authorization.

Scenes:

The front-end calls the interface to receive file streams and download files. Taking the local environment as an example, the system domain name ishttp://localhost:8090, and the interface The domain name is'http://127.0.0.1:8080, and the backend uses springboot

At this time, the browser console reports an error:

Access to XMLHttpRequest at 'http://127.0.0.1:8080/zyfbpt/dbreport/export?domainKey=zyfzyfbpt' from origin 'http://localhost:8090' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Attempt 1: Set the response header Access-Control-Allow-Origin when the interface returns the file stream

String origin = request.getHeader("Origin");
response.addHeader("Access-Control-Allow-Origin", origin);

After restarting the service, I found that it did not take effect and the error message remained unchanged (can anyone help analyze why this does not take effect)

Attempt 2: Use @CrossOrigin annotation

//1.在controller类上使用
@Controller
@CrossOrigin
public class MyController {

}
//2.在方法上使用
@CrossOrigin
@RequestMapping(value = "/test",method = RequestMethod.POST)
public void test(@RequestBody TestRequest testRequest){
    
}

After adding it directly, I found that it still did not take effect. After searching, I summarized the reasons as follows:

Case 1: Use the @CrossOrigin annotation on the method, but the Get and Post methods are not specified in the @RequestMapping annotation. Specify it through method = RequestMethod.POST/GET or specify @CrossOrigin After methods = {RequestMethod.POST} in the annotation, the problem is solved.

//在@CrossOrigin中methods指定方式
@CrossOrigin(methods = {RequestMethod.POST})

//或者@RequestMapping注解定义method
@RequestMapping(value = "/test",method = {RequestMethod.POST})

Case 2:The cross-domain access response status code is 405-Method Not Allowed, and the request method specified in the request line cannot be used to request the corresponding resource. This is an incorrect request method, check the code.

Case 3: Check the springboot version. If it is version 2.0 or later, the default value of the allowCredentials attribute is false, and the returned response header AccessControlAllowCredentials attribute value is also false. If the client carries Cookie requests cannot be accessed across domains at this time, so you need to manually set allowCredentials to true in the annotation

For example: the front-end ajax request parameter withCredentials=true means that the request carries cookie information.

//a标签下载文件
axios({
      method: 'POST',
      url: downloadUrl,
      data: params,
      withCredentials:true,
      responseType: 'blob'
    }).then(res => {
      let url = window.URL.createObjectURL(new Blob([res.data]));
      let link = document.createElement("a");
      link.style.display = "none";
      link.href = url;
      link.setAttribute("download", `report-${Date.now()}.docx`);
      document.body.appendChild(link);
      link.click();
});

At this time, you need to set allowCredentials=true on the backend to solve the cross-domain problem. However, after the following configuration, an error is reported when requesting:

@CrossOrigin(allowCredentials = "true")
@RequestMapping(value = "/dbreport/export", method = RequestMethod.POST)
@ResponseBody
public Object export(@RequestBody JSONObject json HttpServletResponse response) {

}

Backend error:

java.lang.IllegalArgumentException: When allowCredentials is true, allowedOrigins cannot contain the special value "*" since that cannot be set on the "Access-Control-Allow-Origin" response header. To allow credentials to a set of origins, list them explicitly or consider using "allowedOriginPatterns" instead.

Translated it is:

When allowCredentials is true, allowedorigin cannot contain the special value "*" because this value cannot be set in the Access-Control-Allow-Origin response header. To allow credentials to a set of origins, list them explicitly, or consider using "allowedOriginPatterns" instead.

solve:

The problem is solved after adding originPatterns = "*" as prompted.

@CrossOrigin( originPatterns = "*", allowCredentials = "true", allowedHeaders = "*")
@RequestMapping(value = "/dbreport/export", method = RequestMethod.POST)
@ResponseBody
public Object export(@RequestBody JSONObject json HttpServletResponse response) {

}

Attempt 3: Global CORS configuration

After configuration, there is no need to add @Configuration annotation to each controller or method.

@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
            // 放行哪些域名,可以多个
            .allowedOriginPatterns("*")  
            // 是否发送Cookie信息                       
            .allowCredentials(true)
            // 允许的模式
            .allowedOriginPatterns("*");
            // 放行哪些请求方式                      
            .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS", "PATCH") 
            // 放行哪些原始域(头部信息)
            .allowedHeaders("*")
            // 暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)                             
            .exposedHeaders("Header1", "Header2") 
            // 预请求的结果有效期,默认1800分钟,3600是一小时
            .maxAge(3600);                                      
    }
}

After searching, I found that some netizens said that this configuration has the following situations, but my own interface is to use the file stream returned by HttpServletResponse, and it was successful without adding the following response header. For reference

Applicable situations:

1Interface that returns a string or object

2. Provide file download function interface throughResponseEntity

Not applicable:

The interface that directly applies HttpServletResponse to return content needs to add the following content to the response header.

response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS,DELETE");
response.addHeader("Access-Control-Max-Age", "9600");
response.addHeader("Access-Control-Allow-Headers", "x-requested-with");

While solving the problem, I also encountered the following error:

Mistake one:

At this time, the front-end request carries withCredentials=true and carries a cookie request.

The backend configuration is:

@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**");                                
    }
}
Access to XMLHttpRequest at 'http://127.0.0.1:8080/zyfbpt/dbreport/export?domainKey=zyfzyfbpt' from origin 'http://localhost:8090' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

Reason:If withCredentials:true is set for a cross-domain request, the browser will carry the cookie information of the domain when it initiates the request, and the cross-domain standard stipulates that the request response header Access-Control-Allow-Origin cannot be set to * and must be set to a specific cross-domain domain name.

Solution one:

Remove the withCredentials=true parameter on the front end

Solution two:

@Override
public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping("/**")
            .allowCredentials(true)
            .allowedOriginPatterns("*");
}

Mistake 2:

Access to XMLHttpRequest at 'http://127.0.0.1:8080/zyfbpt/dbreport/export?domainKey=zyfzyfbpt' from origin 'http://localhost:8090' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values '*, http://localhost:8090', but only one is allowed.

Cause:Access-Control-Allow-Origin response header was added repeatedly, causing an error. Generally, this reason occurs because the operation and maintenance and interface parties added Access-Control-Allow at the same time. -Origin response header.

Solution:Check whether Access-Control-Allow-Origin is configured in multiple places, such as CROS global configuration, @CrossOrigin annotation in controller, and Access-Control-Allow-set in code Origin response header, nginx and other gateway layer additions, etc.

Just keep one place

おすすめ

転載: blog.csdn.net/qq_35751333/article/details/128939612