跨域

  • 跨域是啥?
    • 下面是一个例子来理解同源策略
    • 为什么会有同源策略?
  • 解决跨域的常见方式
    • JSONP
    • CORS(Cross-origin resource sharing)跨域资源共享
      • 什么情况下需要 CORS ?
      • 简单请求
      • 预检请求(复杂请求)
    • document.domain
    • postMessage

跨域是啥?

因为浏览器出于安全考虑,有同源策略。也就是说,如果协议、域名或者端口有一个不同就是跨域,Ajax 请求会失败。

下面是一个例子来理解同源策略

Web内容的源由用于访问它的URL 的方案(协议),主机(域名)和端口定义。只有当方案,主机和端口都匹配时,两个对象具有相同的起源。

为什么会有同源策略?

那么是出于什么安全考虑才会引入这种机制呢? 其实主要是用来防止 CSRF 攻击的。简单点说,CSRF 攻击是利用用户的登录态发起恶意请求。

也就是说,没有同源策略的情况下,A 网站可以被任意其他来源的 Ajax 访问到内容。如果你当前 A 网站还存在登录态,那么对方就可以通过 Ajax 获得你的任何信息。当然跨域并不能完全阻止 CSRF。

然后我们来考虑一个问题,请求跨域了,那么请求到底发出去没有? 请求必然是发出去了,但是浏览器拦截了响应。你可能会疑问明明通过表单的方式可以发起跨域请求,为什么 Ajax 就不会。因为归根结底,跨域是为了阻止用户读取到另一个域名下的内容,Ajax 可以获取响应,浏览器认为这不安全,所以拦截了响应。但是表单并不会获取新的内容,所以可以发起跨域请求。同时也说明了跨域并不能完全阻止 CSRF,因为请求毕竟是发出去了。接下来我们将来学习几种常见的方式来解决跨域的问题。

解决跨域的常见方式

JSONP

JSONP 的原理很简单,就是利用 script标签没有跨域限制的漏洞。通过 script 标签指向一个需要访问的地址并提供一个回调函数来接收数据当需要通讯时。

<script src="http://domain/api?param1=a&param2=b&callback=jsonp"></script>
<script>function jsonp(data) {console.log(data)}
</script>

JSONP 使用简单且兼容性不错,但是只限于 get 请求

在开发中可能会遇到多个 JSONP 请求的回调函数名是相同的,这时候就需要自己封装一个 JSONP,以下是简单实现。

function jsonp(url, jsonpCallback, success) {let script = document.createElement('script')script.src = urlscript.async = truescript.type = 'text/javascript'window[jsonpCallback] = function(data) {success && success(data)}document.body.appendChild(script)
}
jsonp('http://xxx', 'callback', function(value) {console.log(value)
})
CORS(Cross-origin resource sharing)跨域资源共享
什么情况下需要 CORS ?

跨域资源共享标准( cross-origin sharing standard )允许在下列场景中使用跨域 HTTP 请求:

  • 前文提到的由 XMLHttpRequest 或 Fetch 发起的跨域 HTTP 请求。
  • Web 字体 (CSS 中通过@font-face 使用跨域字体资源), 因此,网站就可以发布 TrueType 字体资源,并只允许已授权网站进行跨站调用。
  • WebGL贴图
  • 使用 drawImage 将 Images/video 画面绘制到 canvas 本文概述了跨域资源共享机制及其所涉及的 HTTP头。

跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。

简单请求

某些请求不会触发 CORS 预检请求。本文称这样的请求为“简单请求”,请注意,该术语并不属于 Fetch (其中定义了 CORS)规范。若请求满足所有下述条件,则该请求可视为“简单请求”。
使用下列方法之一

  • GET
  • HEAD
  • POST

Content-Type 的值仅限于下列三者之一

  • text/plain
  • multipart/form-data
  • application/x-www-form-urlencoded

请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器;XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。

请求中没有使用 ReadableStream 对象。

比如说,假如站点 http://foo.example 的网页应用想要访问 http://bar.other 的资源。http://foo.example 的网页中可能包含类似于下面的 JavaScript 代码:

var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/public-data/';function callOtherDomain() {if(invocation) {    invocation.open('GET', url, true);invocation.onreadystatechange = handler;invocation.send(); }
}

客户端和服务器之间使用 CORS 首部字段来处理跨域权限:

分别检视请求报文和响应报文:

GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,**;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.exampleHTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml[XML Data]

第 1~10 行是请求首部。第10行 的请求首部字段 Origin 表明该请求来源于 http://foo.example。

第 13~22 行是来自于 http://bar.other 的服务端响应。响应中携带了响应首部字段 Access-Control-Allow-Origin(第 16 行)。使用 Origin 和 Access-Control-Allow-Origin 就能完成最简单的访问控制。本例中,服务端返回的 Access-Control-Allow-Origin: * 表明,该资源可以被任意外域访问。如果服务端仅允许来自 http://foo.example 的访问,该首部字段的内容如下:

Access-Control-Allow-Origin: http://foo.example

现在,除了 http://foo.example,其它外域均不能访问该资源(该策略由请求首部中的 ORIGIN 字段定义,见第10行)。Access-Control-Allow-Origin 应当为 * 或者包含由 Origin 首部字段所指明的域名。

预检请求(复杂请求)

与前述简单请求不同,“需预检的请求”要求必须首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。

当请求满足下述任一条件时,即应首先发送预检请求:
使用了下面任一 HTTP 方法:

  • PUT
  • DELETE
  • CONNECT
  • OPTIONS
  • TRACE
  • PATCH

人为设置了对 CORS 安全的首部字段集合之外的其他首部字段。该集合为:

  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type (需要注意额外的限制)
  • DPR
  • Downlink
  • Save-Data
  • Viewport-Width
  • Width

Content-Type 的值不属于下列之一:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

请求中的XMLHttpRequestUpload 对象注册了任意多个事件监听器。
请求中使用了ReadableStream对象。

如下是一个需要执行预检请求的 HTTP 请求:

var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '<?xml version="1.0"?><person><name>Arun</name></person>';function callOtherDomain(){if(invocation){invocation.open('POST', url, true);invocation.setRequestHeader('X-PINGOTHER', 'pingpong');invocation.setRequestHeader('Content-Type', 'application/xml');invocation.onreadystatechange = handler;invocation.send(body); }
}......

上面的代码使用 POST 请求发送一个 XML 文档,该请求包含了一个自定义的请求首部字段(X-PINGOTHER: pingpong)。另外,该请求的 Content-Type 为 application/xml。因此,该请求需要首先发起“预检请求”。

OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,**;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-TypeHTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

预检请求完成之后,发送实际请求:

POST /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,**;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: http://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: http://foo.example
Pragma: no-cache
Cache-Control: no-cache<?xml version="1.0"?><person><name>Arun</name></person>HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain[Some GZIP'd payload]

浏览器检测到,从 JavaScript 中发起的请求需要被预检。从上面的报文中,我们看到,第 1~12 行发送了一个使用 OPTIONS 方法的“预检请求”。 OPTIONS 是 HTTP/1.1 协议中定义的方法,用以从服务器获取更多信息。该方法不会对服务器资源产生影响。 预检请求中同时携带了下面两个首部字段:

Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type

首部字段 Access-Control-Request-Method 告知服务器,实际请求将使用 POST 方法。首部字段 Access-Control-Request-Headers 告知服务器,实际请求将携带两个自定义请求首部字段:X-PINGOTHER 与 Content-Type。服务器据此决定,该实际请求是否被允许。

第14~26 行为预检请求的响应,表明服务器将接受后续的实际请求。重点看第 17~20 行:

Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400

首部字段 Access-Control-Allow-Methods 表明服务器允许客户端使用 POST, GET 和 OPTIONS 方法发起请求。该字段与 HTTP/1.1 Allow: response header 类似,但仅限于在需要访问控制的场景中使用。

首部字段 Access-Control-Allow-Headers 表明服务器允许请求中携带字段 X-PINGOTHER 与 Content-Type。与 Access-Control-Allow-Methods 一样,Access-Control-Allow-Headers 的值为逗号分割的列表。

最后,首部字段 Access-Control-Max-Age 表明该响应的有效时间为 86400 秒,也就是 24 小时。在有效时间内,浏览器无须为同一请求再次发起预检请求。请注意,浏览器自身维护了一个最大有效时间,如果该首部字段的值超过了最大有效时间,将不会生效。

HTTP 响应首部字段:
响应首部中可以携带一个 Access-Control-Allow-Origin 字段,其语法如下:

Access-Control-Allow-Origin: <origin> | *

其中,origin 参数的值指定了允许访问该资源的外域 URI。对于不需要携带身份凭证的请求,服务器可以指定该字段的值为通配符,表示允许来自所有域的请求。

例如,下面的字段值将允许来自 http://mozilla.com 的请求:

Access-Control-Allow-Origin: http://mozilla.com

如果服务端指定了具体的域名而非“*”,那么响应首部中的 Vary 字段的值必须包含 Origin。这将告诉客户端:服务器对不同的源站返回不同的内容。

document.domain

该方式只能用于二级域名相同的情况下,比如 a.test.com 和 b.test.com 适用于该方式。

只需要给页面添加 document.domain = ‘test.com’ 表示二级域名都相同就可以实现跨域。

postMessage

这种方式通常用于获取嵌入页面中的第三方页面数据。一个页面发送消息,另一个页面判断来源并接收消息。

// 发送消息端
window.parent.postMessage('message', 'http://test.com')
// 接收消息端
var mc = new MessageChannel()
mc.addEventListener('message', event => {var origin = event.origin || event.originalEvent.originif (origin === 'http://test.com') {console.log('验证通过')}
})

在此,跨域的总结就结束。

浏览器基础(2)-跨域相关推荐

  1. 爬取斗图网图片,使用xpath格式来匹配内容,对请求伪装成浏览器, Referer 防跨域请求...

    6.21自我总结 一.爬取斗图网 1.摘要 使用xpath匹配规则查找对应信息文件将请求伪装成浏览器Referer 防跨域请求 2.爬取代码 #导入模块 import requests#爬取网址 ur ...

  2. 什么是跨域,为什么浏览器会禁止跨域,以及实现跨域的几种方式

    首先我们来想一想 为什么会有跨域这个名词的出现呢? 跨域又是什么呢?为何要跨域? 浏览器的同源策略又是什么?怎么解决? jsonp又是什么? 跨域的原理又是什么呢? 名词解释: 1.什么是跨域 跨域的 ...

  3. chrome浏览器解决ajax跨域问题

    方法一 1.右键谷歌快捷方式,选择"属性". 2.打开属性窗口,切换到"快捷方式"选项卡. 3.在目标路径的后面添加[ --disable-web-securi ...

  4. 浏览器同源与跨域问题总结

    最近工作中的项目需要通过浏览器ajax请求访问本地的一个http服务,不可避免遇到了跨域的问题,还要兼容IE,踩了不少坑,在这里作一个总结吧 1.浏览器同源策略 跨域问题是由于浏览器的同源策略所引起的 ...

  5. ie浏览器设置允许跨域_ieTester允许跨域浏览窗口和框架

    在用 ieTester 测试网页时,每次打开 ie6 总是不断的弹出 Allow sub-frames to navigate across different domains?,意思是是否允许跨域浏 ...

  6. 解决升级Chrome浏览器之后出现跨域错误:Access to xxx has been blocked by CORS policy: XXXX

    一.错误产生的背景 Google Chrome浏览器升级 1.1 Google Chrome漏洞报告,要求升级 近日,Google Chrome官方发布Google Chrome远程代码执行漏洞(CV ...

  7. 新版浏览器出现的跨域问题及解决方案

    现象: 错误信息: Access to XMLHttpRequest at 'http://127.0.0.1:9581/GetInterface' from origin 'http://114.1 ...

  8. 浏览器同源策略以及跨域请求时可能遇到的问题

    跨域请求基础知识 浏览器的同源策略 浏览器的源指的是 协议://域名:端口 这样的URL组合.我们首先要明确几点 www.foo.com 和 foo.com 是不同域 www.foo.com 和 ww ...

  9. 不同版本浏览器前端标准兼容性对照表以及CORS解决跨域和CSRF安全问题解决方案

    CORS也已经成为主流的跨域解决方案,不过CORF也会引发CSRF,本文先分享第三方的一个前端工具箱全面展示那些浏览器版本支持CORS,由于各家浏览器厂商因为各自原因在不同的版本里支持的标准不同,这个 ...

  10. chrome浏览器的跨域设置 Google Chrome浏览器下开启禁用缓存和js跨域限制--disable-web-security...

    chrome用户默认路径 Win7:C:\Users\[用户名]\AppData\Local\Google\Chrome\User Data\ XP:C:\Documents and Settings ...

最新文章

  1. poj2112(floyd+二分+二分图多重匹配)
  2. 八、逆波兰计算器的分析与实现
  3. Spring Boot和Spring数据JPA集成
  4. 第五季1:AP模式USB-WIFI网卡移植与测试
  5. IDEA初次使用Tomcat运行项目(如何添加加载项目到Tomcat)
  6. TeleportPoint可瞬移的目标位置
  7. Django中文无法转换成latin-1编码的解决方案
  8. apt-get pip3
  9. 关于何种情况下使用DataGrid、DataList或Repeater的一些讨论(1) ambushaa [翻译] [转]
  10. mvn执行编译时使用lib下的jar包
  11. [2021]免费下载外文文献
  12. Flutter 修改自带Switch,实现轻量自定义Switch,使用screen_utils适配大屏
  13. 关于Windows Server 2008 Service Pack 1
  14. alpha对冲(股票+期货)——Python量化
  15. mysql5.7.26搭建MGR单主及多主模式
  16. 路由控制配置route-policy命令解析
  17. 如何避免软件行业的薪资天花板?
  18. word怎么添加水印 word添加水印后为什么看不见
  19. linux下磁盘查看和分区
  20. 少女时代动态android,少女时代攻「V APP」!连续8天live直播大放送

热门文章

  1. openeuler 21.3 : 使用LVM管理硬盘
  2. PostgreSQL 杀会话
  3. MySQL联合索引以及索引顺序优化
  4. 【聚英云农】无线节水灌溉系统应用智慧农业
  5. MCU低功耗引脚设置
  6. AJAX中的利用POST传送数据
  7. 大象无形11.4.4 实战:创建新的渲染代理 笔记
  8. maven镜像源及代理配置
  9. mac php 连接 mssql 2008,php5.3.x连接MSSQLserver2008
  10. 小米VR一体机、Oculus Go投屏到PC、TV教程