跨域相关
跨域是什么
跨域本质上是浏览器为保护用户安全,限制不同源的网页相互访问资源的现象。浏览器的安全策略即 同源策略
是具体实现方式,该策略要求两个交互的网页必须满足协议、域名、端口完全一致。否则,所有跨域请求将被浏览器拦截。这种设计旨在防止恶意网站窃取用户敏感数据(如 Cookie、登录凭证),保障 Web 应用的安全性。
限制范围
限制范围包括:DOM 操作(如 iframe 跨域内容)、AJAX/Fetch
请求响应拦截等。
特别注意:跨域请求会正常发送出去,而不是直接被浏览器拦截
- 浏览器会正常发送跨域请求(比如前端调用后端不同域的 API);
- 服务器也会正常处理请求并返回响应;
- 当响应到达浏览器时,浏览器会检查 “响应头是否允许当前源访问”:如果不允许,就会拦截响应,并在控制台抛出跨域错误(如 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 等请求的发出。
触发条件:
- 请求方法不是 GET、HEAD、POST
- POST 请求的 Content-Type 不是 application/x-www-form-urlencoded、multipart/form-data 或 text/plain
- 设置了自定义的 header 字段
举例说明:
- 浏览器向服务端提交一个跨域的 POST 请求,请求的
Content-Type
是application/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: 表明发送请求或者预请求的域
- 服务端响应预请求
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
预请求的所有跨域头得到允许以后(及以上三个请求头一一对应), 浏览器才会发送实际请求, 所以服务端要根据预请求响应这三个请求头
- 发送实际请求
- 响应实际请求
总结跨域请求失败的解决办法
-
检查预请求(即
options
请求)的三个请求头 Origin, Access-Control-Allow-Headers, Access-Control-Request-Method -
服务端分别加上
Access-Control-Allow-Origin: [预请求的 Origin 内容 | * ]
Access-Control-Request-Headers:[预请求的 Access-Control-Request-Headers 内容]
Allow:[预请求的 Access-Control-Request-Method 的内容]