跨域问题

124 阅读2分钟

产生原因

浏览器会根据同源策略来限制访问,是一种浏览器的安全机制,不符合同源策略的请求就会产生跨域问题。

同源策略

符合下面任何一种的都视为不是同源:协议不同、域名不同、端口不同。

具体错误表现

Access to fetch at 'https://www.baidu.com/' from origin 'https://juejin.cn' has been blocked by CORS policy: 
No 'Access-Control-Allow-Origin' header is present on the requested resource.
If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

解决办法

总体思路就是规避浏览器的同源策略,让浏览器的这种策略不发挥作用即可,常见的做法就是后端服务器告诉浏览器不需要进行同源检查,告诉浏览器是“自己人”。

  1. 后端接口是自己能够修改的,比如后端是由自己人编写的,那么就在服务器接口响应头里面添加
"Access-Control-Allow-Origin","*" // 表示允许所有请求访问,也可以添加特定的域
  1. 后端接口是第三方的,比如百度的接口等等,这个我们可以使用代理服务器的方法,实际操作就是由我们的后端访问第三方的接口,然后将数据返回给前端。

image.png

springboot项目中可以使用的操作

本质上就是在响应头里面设置Access-Control-Allow-Origin参数

(1)全局配置

实现WebMvcConfigurer 接口,重写addCorsMappings方法,新增跨域规则

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 CorsConfig implements WebMvcConfigurer 
{ 
    @Override 
    public void addCorsMappings(CorsRegistry registry) { 
        registry.addMapping("/**") 
                .allowedOrigins("*") 
                .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS") 
                .allowCredentials(true) 
                .allowedHeaders("*"); 
    } 
}

(2)配置拦截器

新建拦截器,在拦截器中添加响应头


import org.springframework.context.annotation.Configuration; 
import javax.servlet.*; 
import javax.servlet.annotation.WebFilter; 
import javax.servlet.http.HttpServletResponse; 
import java.io.IOException; 
@WebFilter(filterName = "CorsFilter ") 
@Configuration 
public class CorsFilter implements Filter {
    @Override 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-Credentials", "true"); 
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH, DELETE, PUT"); 
        response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
        chain.doFilter(req, res); 
    } 
}

(3)使用springboot 跨域的注解 @CrossOrigin

@CrossOrigin可用于类和方法上,目前是粒度最小的一种解决跨域的方式

public class GoodsController { 
    @CrossOrigin(origins = "http://localhost:4000") 
    @GetMapping("goods-url") 
    public Response queryGoodsWithGoodsUrl(@RequestParam String goodsUrl) throws Exception {
    } 
}

简单请求和复杂请求遇到跨域问题的不同处理

简单请求会先执行服务端程序,然后再判断是否跨域,此时服务器的相关逻辑会执行一遍,非简单请求先发一个预检请求,然后通过后再发送真正的http请求,这种情况下服务器不会执行相关逻辑。

区分

简单请求:

请求方式:head、get、post

请求头:没有自定义请求头、content-type值为:text/plain 、multipart/form-data、 application/x-www-form-urlencoded

非简单请求:

请求方式为:put、delete 的ajax请求、发送json格式的ajax、自定义头