跳到主要内容

跨域相关

· 阅读需 4 分钟

跨域是什么

跨域本质上是浏览器为保护用户安全,限制不同源的网页相互访问资源的现象。浏览器的安全策略即 同源策略 是具体实现方式,该策略要求两个交互的网页必须满足协议、域名、端口完全一致。否则,所有跨域请求将被浏览器拦截。这种设计旨在防止恶意网站窃取用户敏感数据(如 Cookie、登录凭证),保障 Web 应用的安全性。‌

限制范围

限制范围包括:DOM 操作(如 iframe 跨域内容)、AJAX/Fetch 请求响应拦截等。

特别注意:跨域请求会正常发送出去,而不是直接被浏览器拦截

  1. 浏览器会正常发送跨域请求(比如前端调用后端不同域的 API);
  2. 服务器也会正常处理请求并返回响应;
  3. 当响应到达浏览器时,浏览器会检查 “响应头是否允许当前源访问”:如果不允许,就会拦截响应,并在控制台抛出跨域错误(如 Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy)。

解决方案

  • CORS(Cross-Origin Resource Sharing)跨域资源共享机制:服务器在响应头中添加 Access-Control-Allow-Origin 等字段,明确允许某个源访问(最推荐的方案)
  • 代理服务器:前端请求本地代理服务器(同源的),再由代理服务器转发请求到目标服务器(如 Vue CLI、Webpack 的 devServer 代理)
  • JSONP:利用 script 标签不受同源限制的特性,加载服务器返回的回调函数(仅支持 GET 请求,过时方案)

Preflighted Reqeusts

预检请求(Preflight Request)是浏览器在跨域请求中自动发送的 OPTIONS 请求,用于确认服务器是否允许跨域访问。

复杂跨域请求会发送一个预检请求 options,如果 options 获得的回应是拒绝性质的,比如 404\403\500 等 http 状态,就会停止 post、put 等请求的发出。

触发条件:

  1. 请求方法不是 GET、HEAD、POST
  2. POST 请求的 Content-Type 不是 application/x-www-form-urlencoded、multipart/form-data 或 text/plain
  3. 设置了自定义的 header 字段

举例说明:

  1. 浏览器向服务端提交一个跨域的 POST 请求,请求的 Content-Typeapplication/json,这是一个非简单请求,所以浏览器会发送一个 method 为 OPTIONS 的预请求,到服务端查询是否支持该跨域请求
请求信息
Request URL:http://stu.dev/post
Request Method:OPTIONS
Access-Control-Request-Headers:content-type
Access-Control-Request-Method:POST
Origin:http://localhost:8080

Access-Control-Request-Method: 在发出预检请求时带有这个头信息, 告诉服务器在实际请求时会使用的请求方式

Access-Control-Request-Headers: 在发出预检请求时带有这个头信息, 告诉服务器在实际请求时会携带的自定义头信息. 如有多个, 可以用逗号分开

Origin: 表明发送请求或者预请求的域

  1. 服务端响应预请求
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Origin: *
Allow:GET,HEAD,POST

服务端需要对预请求里对应的三个头进行响应:

Access-Control-Allow-Headers:Content-Type 表明服务端支持该跨域请求(实际请求)的请求头, 对应预请求里的 Access-Control-Request-Headers:content-type

Access-Control-Allow-Origin: * 表明服务端支持跨域响应的域, 对应对应预请求里的 Origin

Allow:GET,HEAD,POST 表明服务端支持该跨域请求的 Method

预请求的所有跨域头得到允许以后(及以上三个请求头一一对应), 浏览器才会发送实际请求, 所以服务端要根据预请求响应这三个请求头

  1. 发送实际请求
  2. 响应实际请求

总结跨域请求失败的解决办法

  1. 检查预请求(即 options 请求)的三个请求头 Origin, Access-Control-Allow-Headers, Access-Control-Request-Method

  2. 服务端分别加上
    Access-Control-Allow-Origin: [预请求的 Origin 内容 | * ]
    Access-Control-Request-Headers:[预请求的 Access-Control-Request-Headers 内容]
    Allow:[预请求的 Access-Control-Request-Method 的内容]