文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

跨域是个什么鬼,你搞明白了吗?

2024-12-02 18:08

关注

跨域是个老生常谈的话题了,最近不管在和后端联调,或者搞微前端的时候都会遇到,正好写篇文章来总结一下吧。

跨域是什么

这里的“跨域”指的是不同源之间的资源访问。只要请求的 url 有以下不同,都属于“跨域”:

有人可能会觉得,我自己网站肯定只访问自己服务器,肯定都是部署在一个域名的呀。

但是有的时候,一个网页可能要对接后端多个服务:一会对接支付,一会对接用户信息。每个组的后端可能都会有自己的域名。在这样的场景下,跨域就非常常见了。

为什么会有跨域

我们常说的“跨域”问题,其实是在说“跨域”访问的限制问题,相信大家对下面的报错习以为常了:

这种“跨域”限制其实是 浏览器自带的安全机制,只有 在浏览器上 发生跨域请求操作时,浏览器就会自动抛出上面的错误。

注意,这仅在浏览器上会出现这样的限制,如果你用 Postman 这些工具访问 url 是没有“跨域”限制的,毕竟 Postman 连域名这些玩意都没有,哪来的“跨域”。

CORS

虽然浏览器出于安全考虑做了“跨域”访问的限制,但开发时不可避免会有这样不同源资源访问的需求,因此 W3C 就制定了 CORS(Cross-origin resource sharing 跨域资源共享) 的机制。

很多人一直以为 CORS = 跨域,其实 CORS 是一种解决“跨域”的方案。

需要注意的是,CORS 是一个“新”的协议(至少对于以前的 IE7 是新的),不仅需要浏览器支持,也后端服务器的支持。

浏览器支持没什么好说的,就是浏览器版本是否支持的问题:

然后就是后端服务器支持了,服务器需要在 Response Header 上添加 Access-Control-xxx-yyy 的字段,浏览器识别到了,才能放行该请求。比如,最常见的就是加 Access-Control-Allow-Origin 这个返回头,值设置为需要放行的域名。

简单请求 VS 非简单请求

浏览器将 CORS 请求分为 简单请求 和 非简单请求。

简单请求 会在发送时自动在 HTTP 请求头加上 Origin 字段,来标明当前是哪个源(协议+域名+端口),服务端来决定是否放行。

非简单请求 则会先发一个 OPTIONS 预检请求给服务端,当通过了再发正常的 CORS 请求。

对于 简单请求,请求方法为以下三种之一:

且 HTTP 请求头字段不能超过以下字段:

同时 Content-Type 只能三个值:

只要不满足上面条件的,都属于 非简单请求。

可能很多人会自然地觉得 POST 请求都是 非简单请求,因为我们常看到发 POST 时,都会先发 OPTIONS。其实是因为我们一般都会传 JSON 格式的数据,Content-Type 为 application/json,所以,这样的 POST 请求才属于 非简单请求。

Access-Control-xxx-yyyy

当 CORS 请求为 简单请求时,请求会检测返回头里的以下字段:

而当 CORS 请求为 非简单请求时,浏览器会先发一个 OPTIONS 预检(preflight)请求,这个请求会检查如下字段:

如果 OPTIONS 请求没有通过服务端的校验,就会返回一个正常的 HTTP 请求,不会带上 CORS 的返回信息,所以浏览器就会认定为“跨域”了。

总结一句话就是,当 Console 报哪个错,你就在服务端返回头上加上哪个字段就可以了。

CORS 中间件

无论对于 Express 还是 KOA,我们都不需要再手动添加上面的字段了,直接加一个 cors 中间件就可以很方便地添加上面的字段,写起来也更优雅:

  1. var cors = require('cors'); 
  2.  
  3. var corsOptions = { 
  4.   origin: function (origin, callback) { 
  5.     // db.loadOrigins is an example call to load 
  6.     // a list of origins from a backing database 
  7.     db.loadOrigins(function (error, origins) { 
  8.       callback(error, origins) 
  9.     }) 
  10.   } 
  11.  
  12. app.use('/', cors(corsOptions), indexRouter); 

JSONP

那对于浏览器不支持 CORS 的情况呢?虽然目前来看是不太可能,但是在还没有 CORS 的时代,大家是怎么解决跨域的呢?答案就是 JSONP。

它的原理也非常简单:虽然浏览器限制了 HTTP 的跨域,但是没有限制获取 script 标签内容的跨域请求呀。

 

当我们插入一个