http跨域底层逻辑大揭秘,3000字干货颠覆认知

一、什么是跨域

跨域是浏览器加载了与当前域名(a.x.com与b.x.com是跨域)、协议(http与https是跨域)、端口不同(80端口与8080端口是跨域)的另一站点下的资源。

这与各大支持JavaScript的浏览器的同源策略是违背的。所谓同源策略,它是由Netscape提出的一个著名的安全策略。

现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指,域名相同,协议相同,端口相同。

比如下面几个域名是不同源的:

http://example.com/

http://example.com:8080/

http://www.example.com/

https://example.com:80/

https://example.com/

http://example.org/

因为它们有不同的协议或不同的域名或不同的端口

跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript实施的安全限制。

同源策略限制了一下行为:

  Cookie、LocalStorage 和 IndexDB 在跨域情况下无法读取

  DOM 和 JS 对象在跨域情况下无法获取

  Ajax请求在跨域情况下发送不出去

二、跨域的解决方法

CORS是目前主流的跨域解决方案 CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。 

它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。 

因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)

简单请求

只要满足下面条件就是简单请求

1 请求方式为HEAD、POST 或者 GET

2 http头信息不超出以下字段:

Accept、

Accept-Language 、 

Content-Language、 

Last-Event-ID、 

Content-Type(限于三个值:application/x-www-form-urlencoded、multipart/form-data、text/plain)

对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。 

下面是一个例子,浏览器发现这次跨源AJAX请求是简单请求,就自动在头信息之中,添加一个Origin字段。

GET /cors HTTP/1.1

Origin: http://api.bob.com  //浏览器自动添加Origin字段 用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。

Host: api.alice.com

Accept-Language: en-US

Connection: keep-alive

User-Agent: Mozilla/5.0

如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。 浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段(详见下文),就知道出错了,从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。

如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。

  Access-Control-Allow-Origin: http://api.bob.com

  Access-Control-Allow-Credentials: true

  Access-Control-Allow-Headers: FooBar

上面的头信息之中,有三个与CORS请求相关的字段,都以Access-Control-开头

Access-Control-Allow-Origin

该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求

 Access-Control-Allow-Credentials

 该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。 设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。

Access-Control-Allow-Headers

该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Allow-Headers里面指定。

HTTP 请求头中的 Origin 字段通常由浏览器在发送跨域请求时自动添加

如果你发现 HTTP 请求头中没有 Origin 字段,可能是以下几种原因

1. 非浏览器客户端

  如果你使用的是非浏览器客户端(如 curl、Postman、Node.js 等),这些客户端默认不会添加 Origin 字段。你需要手动添加这个字段。

2. 同源请求

  对于同源请求(即请求的域名、协议和端口与当前页面相同),浏览器不会添加 Origin 字段,因为这些请求被认为是安全的。

3. 禁用了 CORS 请求

  如果你的请求是跨域请求,但浏览器禁用了 CORS(跨源资源共享),那么 Origin 字段可能不会被添加。

非简单请求

非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。

非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。

这个"预检"请求的HTTP头信息。

OPTIONS /cors HTTP/1.1

Origin: http://api.bob.com

Access-Control-Request-Method: PUT

Access-Control-Request-Headers: X-Custom-Header

...

  "预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。

  除了Origin字段,"预检"请求的头信息包括两个特殊字段。

  Access-Control-Request-Method:该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT。

  Access-Control-Request-Headers:该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header

预检请求的回应

  服务器收到"预检"请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应

  回应的响应头如下

Access-Control-Allow-Origin: http://api.bob.com

Access-Control-Allow-Methods: GET, POST, PUT

Access-Control-Allow-Headers: X-Custom-Header

  上面的HTTP回应中,关键的是Access-Control-Allow-Origin字段,表示http://api.bob.com可以请求数据。该字段也可以设为星号,表示同意任意跨源请求。

  如果浏览器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。

服务器回应的其他CORS相关字段如下:

Access-Control-Allow-Methods: GET, POST, PUT  //该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。

Access-Control-Allow-Headers: X-Custom-Header //如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。

Access-Control-Allow-Credentials: true  //是否允许发送Cookie

Access-Control-Max-Age: 1728000 //该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。

浏览器正常请求回应

一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。

不过浏览器禁用缓存的话(比如浏览器F12的network中的disable cache启用),以后每次非简单请求还是会发送“预检”请求  开启浏览器缓存的话 就不会再发预检请求了

options预检请求的响应状态码是200还是204

在处理 CORS(跨源资源共享)的预检请求(Preflight Request)时,OPTIONS 请求的响应状态码通常是 204 No Content。这是因为预检请求的主要目的是获取服务器的 CORS 相关信息,而不是获取实际的内容。因此,服务器通常会返回 204 状态码,表示请求成功,但没有内容返回。

 HTTP/1.1 204 No Content

 Access-Control-Allow-Origin: *

 Access-Control-Allow-Methods: GET, POST, PUT, DELETE

 Access-Control-Allow-Headers: Content-Type, Authorization

 Access-Control-Max-Age: 86400

  为什么使用 204 No Content?

符合语义:204 No Content 表示请求成功,但服务器不会返回任何内容。这与预检请求的目的相符,因为预检请求只是为了获取 CORS 头信息,而不是实际的数据。

节省带宽:204 状态码不会返回任何响应体,从而节省了带宽。

标准化:大多数现代浏览器和服务器框架在处理预检请求时都遵循这一标准。

虽然 204 No Content 是最常用的响应状态码,但 200 OK 也可以用于预检请求。在这种情况下,服务器会返回一个空的响应体,但状态码仍然是 200。

HTTP/1.1 200 OK

Access-Control-Allow-Origin: *

Access-Control-Allow-Methods: GET, POST, PUT, DELETE

Access-Control-Allow-Headers: Content-Type, Authorization

Access-Control-Max-Age: 86400

Access-Control-Max-Age只能用于预检请求吗 

正常请求响应中能不能带Access-Control-Max-Age

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值