跨域是什么?跨域的具体解决方案?

201 阅读4分钟

跨域:当页面运行中请求的url地址的源和当前页面url地址的源不相同时,就发生了跨域(也叫跨源)

浏览器同源策略会对跨域请求有限制,其中对ajax的限制最为严格:不允许ajax访问跨域资源

源 = 协议 + 主机 + 端口

跨域的解决方案

代理(适用开发环境跨域)

通过配置开发服务器,来进行拦截转发,例如vue项目,可以在vue.config.js中配置

// 配置代理解决、vue.config.js
devServer: {
  proxy: {
    "/api": {
      target: "http://your-backend-api.com", // 目标接口域名
      changeOrigin: true, // 是否启用代理
      pathRewrite: { "^/api": "" } // 重写路径,去掉/api
    }
  }
}

/api 表示开发服务器会拦截路径以/api开头的请求,并转发请求到target的地址,开发服务器拿到响应后,交给浏览器,在浏览器看来这次请求并没有跨域,所以允许ajax访问资源

可以看出,浏览器同源策略只检查协议,主机和端口号,而并不限制端口号后面的路径path,所以代理正是利用path中的信息来瞒过浏览器的限制

Nginx反向代理(适用生产环境跨域)

Nginx反向代理也是利用Nginx服务器转发请求,从而跳过浏览器的限制

server {
  listen 80;
  server_name frontend.com;
  location /api/ {
    proxy_pass http://your-backend-api.com;
    add_header Access-Control-Allow-Origin *;
  }
}
CORS

CORS(跨域资源共享)是后端服务器要进行一些处理。服务器在响应头里增加一些属性,就可以让浏览器允许ajax访问相应资源。CORS对于不同的跨域请求有三种处理模式

1.简单请求

请求方法请求头
POST/GET/HEAD没有自定义的请求头. 并且如果请求头中有Content-Type属性,只能是“text/plain”,"multipart/form-data","application/x-www-form-urlencoded"中的一种

对于简单请求,后端响应需在响应头中加入属性:

Access-Control-Allow-Origin:请求的源 (如果是*号,则代表允许所有源)

2.需要预检的请求

如果不满足上面的简单请求判断条件,那就是需要预检的请求 对于需要预检的请求,浏览器在发送真正的请求前,先发送一个预检请求,用于向服务器确认是否允许实际请求

OPTIONS /api/data HTTP/1.1 //预检请求的请求方式为OPTIONS
Host: example.com  //请求的主机
Origin: https://your-site.com //发起请求的源
Access-Control-Request-Method: POST //实际请求将使用的 HTTP 方法
Access-Control-Request-Headers: Content-Type,a,b //实际请求将携带的自定义头字段

服务器如果允许实际请求,则需在响应头中添加以下属性

Access-Control-Allow-Origin: https://your-site.com      //实际请求的源
Access-Control-Allow-Methods: POST, GET, OPTIONS        //需包含实际请求的请求方法
Access-Control-Allow-Headers: Content-Type, a, b  //需包含实际请求的头字段
Access-Control-Max-Age: 86400  // 可选:缓存预检结果的秒数,表示在这个时间内不必再发起预检请求可以直接请求

随后浏览器就可以发真实请求,与简单请求相同,需要响应头中设置

Access-Control-Allow-Origin:请求的源

3.携带身份凭证的请求

前端配置: 默认情况下,ajax的跨域不会附带cookie,需要前端增加配置

fetch: credentials: 'include'

fetch('https://api.example.com/data', {
  method: 'GET',
  credentials: 'include', // 必须设置为 include 以携带 Cookie
  headers: {
    'Content-Type': 'application/json',
  },
})
.then(response => response.json())
.catch(error => console.error('Error:', error));

xhr: xhr.withCredentials = true

const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data', true);
xhr.withCredentials = true; // 关键配置,携带 Cookie
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(xhr.responseText);
  }
};
xhr.send();

服务器配置

Access-Control-Allow-Origin: https://your-frontend.com  // 必须明确指定域名,不能为 *
Access-Control-Allow-Credentials: true  //明确允许凭证cookie
Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS  // 允许的方法
Access-Control-Allow-Headers: Content-Type, Authorization  // 允许的请求头

简单请求和需要预检的请求都有可能携带凭证,无论是哪一种,响应头都需要显式声明允许凭证

Access-Control-Allow-Credentials: true //明确允许凭证cookie

Access-Control-Allow-Origin:请求的源 (不能是*)

JSONP(兼容老系统)

JSONP是在没有CORS之前的一种解决方案,通过生成一个script标签,并把src设置为要请求的地址

<script src="http://crossdomin.com/api/user"></script>

并且需要后端返回响应为一个函数,并将前端需要的数据作为函数的参数传入,这样浏览器请求script资源后,会执行js文件,也就会执行这个函数,前端在这个函数里就可以拿到需要的数据。

缺点:只支持GET请求

欢迎指正!