相关文章推荐

同源策略

域名地址由 协议(protocol) 域名(host) 端口(port) 请求资源地址 等部分组成。

HTTP访问控制(CORS)、同源策略、跨域和预检请求等问题_同源策略

如果两个 URL 的 协议 域名 端口 都相同,我们就称这两个 URL 同源

同源策略(same-origin policy)是一种出于浏览器安全方面的考虑而出台的一种策略,它可以保护用户信息的安全,防止恶意的网站窃取、身份伪造等。同源策略会阻止一个域的JS脚本和另外一个域的内容进行交互(只允许与 本域 下的接口交互)。不同源的客户端脚本在没有明确授权的情况下,不能读写对方的资源。

HTTP访问控制(CORS)、同源策略、跨域和预检请求等问题_跨域资源共享_02

注意:很多人以为同源策略是浏览器不让请求发出去、或者后端拒绝返回数据。实际情况是,请求能正常发出,后端接口正常响应,只是 数据到了浏览器后被丢弃了

受同源策略限制的方面:

1)DOM节点,限制不同源JS脚本对当前DOM对象的读写操作;

2)数据层面,限制不同源站点读取当前站点 Cookie、IndexDB、LocalStorage等数据。

3)网络层面,限制通过 XMLHttpRequest 等方式将站点的数据发送给不同源的站点(即AJAX的跨域问题)。

浏览器遵守同源策略,同时有三个标签又允许跨域加载资源:

<img src=’’>

<link href=‘’>

<script src=‘’>

所以 HTML 中引入外站的图片、样式、脚本等不会因跨域报错。这些标签一般是加载静态资源的,和后端关系不大,我们应该关心如何解决 AJAX 跨域问题。


跨域

当一个请求 URL 的协议、域名、端口三者之间任意一个与当前页面 URL 不同即为跨域 。由于同源策略的存在,当从一个域的网页去请求另一个域的资源时(如:XMLHttpRequest 和 Fetch),就无法成功获取到资源。

HTTP访问控制(CORS)、同源策略、跨域和预检请求等问题_同源策略_03

提示:以上在协议不同、主域名不同、子域名不同和端口号不同的情况下,向不同源的 URL 发送请求就是跨域。

要解决 AJAX 请求跨域的问题,网上有不少的总结:

CORS、postMessage、JSONP、WebSocket

扩展阅读:​ ​跨域解决方法​

本文主要谈谈 CORS。


CORS介绍

CORS 是跨域资源共享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,属于跨域 AJAX 请求的根本解决方法。

CORS 由一系列传输的 HTTP 头组成,这些 HTTP 头决定浏览器是否阻止前端 JavaScript 代码获取跨域请求的响应。

同源安全策略默认阻止“跨域”获取资源。但是 CORS 给了web服务器这样的权限,即服务器可以选择,允许跨域请求访问到它们的资源。


CORS头信息

Access-Control-Allow-Origin

指示请求的资源能共享给哪些域。

Access-Control-Allow-Credentials

指示当请求的凭证标记为 true 时,是否响应该请求。

Access-Control-Allow-Headers

用在对预请求的响应中,指示实际的请求中可以使用哪些 HTTP 头。

Access-Control-Allow-Methods

指定对预请求的响应中,哪些 HTTP 方法允许访问请求的资源。

Access-Control-Expose-Headers

指示哪些 HTTP 头的名称能在响应中列出。

Access-Control-Max-Age

指示预请求的结果能被缓存多久。

Access-Control-Request-Headers

用于发起一个预请求,告知服务器正式请求会使用那些 HTTP 头。

Access-Control-Request-Method

用于发起一个预请求,告知服务器正式请求会使用哪一种 HTTP 请求方法。

Origin

指示获取资源的请求是从什么域发起的。

出于安全原因,浏览器限制从脚本内发起的跨域HTTP请求。 例如,XMLHttpRequest和Fetch API遵循同源策略。

CORS 机制,使用额外的 HTTP 头来告诉浏览器让运行在一个 Origin (domain) 上的 Web 应用被准许访问来自不同源服务器上的指定资源。这意味着正确设置 CORS 头信息,使用这些API的Web应用就可以跨域了。

HTTP访问控制(CORS)、同源策略、跨域和预检请求等问题_跨域资源共享_04

什么情况需要CORS

跨域资源共享标准允许在下列场景中使用跨域HTTP请求:

1)由 XMLHttpRequest 或 Fetch 发起的跨域 HTTP 请求。

2)Web 字体 (CSS 中通过 @font-face 使用跨域字体资源)。

网站可以发布 TrueType 字体资源,并只允许已授权网站进行跨站调用。

3)WebGL 贴图。

4)使用 drawImage 将 Images/video 画面绘制到 canvas。


CORS机制

跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。

另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法,浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。

CORS请求失败会产生错误,但是为了安全,在 JavaScript 代码层面是无法获知到底具体是哪里出了问题。你只能查看浏览器的控制台以得知具体是哪里出现了错误。

HTTP访问控制(CORS)、同源策略、跨域和预检请求等问题_同源策略_05

意思是说:从A到B的AJAX请求已被 CORS策略阻止:出现了不允许(预定义之外)的请求头字段;

扩展阅读:​ ​HTTP 的 OPTIONS 预检请求简介、特点、触发和优化​


跨域请求场景

客户端文件 ajax_post.html,其中原生的AJAX代码如下:

const xhr = new XMLHttpRequest
xhr.open('POST', 'http://127.0.0.1:8000/server')
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
xhr.send()
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
console.log(xhr.response);
}
}
}

提示:客户端文件域 http://127.0.0.1:5500/ 向另一个域 http://127.0.0.1:8000 发送 POST请求,是典型的跨域;

后端代码:使用express框架搭建的Web服务;

const express = require('express')
const app = express()
app.post('/server', (request, response) => {
response.setHeader('Access-Control-Allow-Origin', '*')
console.log(request.method);
response.send('Hello Ajax post!')
})
app.listen('8000', () => {
console.log('Web服务已经启动,端口8000监听中... ...');
})

提示:只需在服务端添加共享域为 “*”,意思是不限来源,就可以实现跨域。

response.setHeader('Access-Control-Allow-Origin', '*')

控制台输出:正常输出。

HTTP访问控制(CORS)、同源策略、跨域和预检请求等问题_同源策略_06

响应头信息:

HTTP访问控制(CORS)、同源策略、跨域和预检请求等问题_跨域资源共享_07

不设置 CORS 头信息的话就会报错:

HTTP访问控制(CORS)、同源策略、跨域和预检请求等问题_同源策略_05

请求被 CORS 策略阻止;


预检请求场景

预检请求是首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求,正确响应后才可以发起真正的数据请求。

什么情况会触发OPTIONS

1)跨域请求,非跨域请求不会出现options请求;

2)自定义请求头,如客户端:

xhr.setRequestHeader('name', 'Jacky')

3)请求头中的 Content-Type 出现了以下三种之外的格式;

Content-Type: application/x-www-form-urlencoded   表单提交

Content-Type: multipart/form-data   文件上传

Content-Type: text/plain  文本

扩展阅读:​ ​HTTP Content-Type​

如客户端定义:

xhr.setRequestHeader('Content-Type', 'application/json;charset=utf-8')

当满足条件12或者13的时候,AJAX 请求就会出现 OPTIONS 预检请求。


看下预检请求示意图

HTTP访问控制(CORS)、同源策略、跨域和预检请求等问题_同源策略_09



预检请求示例

修改上例客户端代码:

const xhr = new XMLHttpRequest
xhr.open('POST', 'http://127.0.0.1:8000/server')
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
xhr.setRequestHeader('name', 'Jacky')
xhr.send()
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
console.log(xhr.response);
}
}
}

提示1:同样是跨域;

提示2:自定义头信息字段;

即跨域了又使用了自定义头信息字段,所以会触发OPTIONS预检请求;

修改上例服务端代码:

const express = require('express')
const app = express()
app.all('/server', (request, response) => {
response.setHeader('Access-Control-Allow-Origin', '*')
response.setHeader('Access-Control-Allow-Headers', '*')
console.log(request.method);
response.send('Hello Ajax post!')
})
app.listen('8000', () => {
console.log('Web服务已经启动,端口8000监听中... ...');
})

提示:后端又添加了 ​ Access-Control-Allow-Headers ​ 头信息,允许自定义头信息。

控制台输出:

HTTP访问控制(CORS)、同源策略、跨域和预检请求等问题_同源策略_06

后端服务输出:

HTTP访问控制(CORS)、同源策略、跨域和预检请求等问题_跨域资源共享_11

提示:这次连接一共发起两次请求,分别是 OPTIONS 和 POST;

Network 查看请求信息:

HTTP访问控制(CORS)、同源策略、跨域和预检请求等问题_跨域资源共享_12

开发者工具中的 Network 中也可以看到这两次请求:

其中一次是 preflight 即 OPTIONS预检请求。

查看请求头信息:

HTTP访问控制(CORS)、同源策略、跨域和预检请求等问题_跨域资源共享_13

提示1:OPTIONS 是 HTTP/1.1 协议定义的方法,用以从服务器获取更多信息,该方法不会对服务器资源产生影响。

提示2:请求行中显示本次请求方法是 OPTIONS;(红框)

提示3:预检请求中携带了两个 CORS 请求头字段;(黄框)

Access-Control-Request-Method: POST

告知服务器,实际请求将使用 POST方法;

Access-Control-Request-Headers: name

告知服务器,实际请求携带自定义请求头字段:name

查看响应头信息:

HTTP访问控制(CORS)、同源策略、跨域和预检请求等问题_跨域资源共享_14

提示1:响应行显示OK;

提示2:CORS 头信息响应;

Access-Control-Allow-Origin: *

不限制请求域,也可以设置具体请求的域 http://127.0.0.1:5500;

Access-Control-Allow-Headers: *

不限制自定义请求头,也可以设置具体的头信息字段,如:name


预检请求优化

HTTP访问控制(CORS)、同源策略、跨域和预检请求等问题_跨域资源共享_15

触发预检请求时,跨域请求便会发送 2 次请求,既增加了请求数,也延迟了实际请求发起的时间,影响性能。

添加一行头信息,把预检请求的响应缓存起来,在有效期内只需一次预检;

response.setHeader('Access-Control-Max-Age', '600')

提示:设置600秒缓存时间,有效期内,不会再次触发预检;

HTTP访问控制(CORS)、同源策略、跨域和预检请求等问题_同源策略_16



python 将循环获取的值加入数组 python将循环结果以列表输出

Python基础四(for循环、列表)循环控制break和continuefor循环遍历for循环range(start, stop[, step])列表序列基本概念序列的分类列表列表的使用通用操作修改列表列表的方法 循环控制break和continuebreak 可以用来立即退出循环语句,包括else语句i = 0 while i < 5: i += 1 if i ==

 
推荐文章