前天面试被问到了跨域的问题,自我感觉回答的并不理想,下面我就分享一下整理后的总结分享给大家

一、为什么要跨域

  安全限制

  JavaScript或Cookie只能访问同域下的内容——同源策略

  同源策略

  下表相对于: http://h5.jd.com/dir/ajax.js

  

  注意

  • 协议和端口造成的跨域问题,非前端解决范畴
  • 所谓域,是通过“url首部”来识别,而非判断域与ip的对应关系

  (“URL的首部”指window.location.protocol +window.location.host)

二、跨域方案

1. jsonp

  原理

  HTML里面所有带src属性的标签都可以跨域,如iframe,img,script等。

  所以可以把需要跨域的请求改成用script脚本加载即可,服务器返回执行字符串,但是这个字符串是在window全局作用域下执行的,你需要把他返回到你的代码的作用域内,这里就需要临时创建一个全局的回调函数,并把到传到后台,最后再整合实际要请求的数组,返回给前端,让浏览器直接调用,用回调的形式回到你的原代码流程中。

  • 首先,利用 script 标签的 src 属性实现跨域
  • 通过将前端方法作为参数传递到服务器端,然后由服务器注入参数之后再返回,实现服务器端向客户端通信
  • 由于使用script 标签的src 属性,因此只支持 get 方法

  一个简单的jsonp实现,其实就是拼接url,然后将动态添加一个script元素到头部

  1. 1. 设定一个script标签
  2. <script src="http://jsonp.js?callback=xxx"></script>
  3. 2. 服务器
  4. $callback = !empty($_GET['callback'] ? $_GET['callback'] : 'callback');
  5. echo $callback.'(.json_encode($data).)';

  详见博客 JSON 和 JSONP两兄弟

2. cors

方案对比
  JSONP CORS
目的 跨域 跨域
支持

get

(受IE下url长度不能超过2083个字节的限制和出于安全考虑,一般不用来提交数据)

所有类型的http请求
支持度 包括老式浏览器 不支持部分浏览器,移动端支持很好
缺点

1)安全问题(请求代码中可能存在安全隐患)

2)确定jsonp请求是否失败不太容易

3)只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题

支持率
原理

被包含在一个回调函数中的JSON

核心则是动态添加<script>标签来调用服务器提供的js脚本

(允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了)

使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败

(只需由服务器发送一个响应标头即可)

CORS需要浏览器和服务器同时支持

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


1)两种请求方式

  简单请求、非简单请求

  a)简单请求:

  

  

  跨域时,浏览器自动在头部信息中添加一个origin 字段(指定请求源-协议+域名+端口),如下图所示

  

  服务器判断origin在域名许可范围内,返回响应:

  

  若不存在 Access-Control-Allow-Origin 字段,则出错

  以上头部信息中,CORS相关字段有

  • Access-Control-Allow-Origin
    必须字段,其值为 origin / *(可接受任意域名请求)
  • Access-Control-Allow-Credentials
    可选,是否允许发送Cookie
  • Access-Control-Expose-Headers
    可选,是否需要Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma之外的字段

  withCredentials 属性

  CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段

  1. Access-Control-Allow-Credentials: true

  另一方面,开发者必须在AJAX请求中打开withCredentials属性。

  1. var xhr = new XMLHttpRequest();
  2. xhr.withCredentials = true;

  注意

  如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。

  b)非简单请求(不同时满足以上条件)

  请求方法是PUTDELETE,或者Content-Type字段的类型是application/json

  浏览器对于非简单请求,就自动发出一个"预检"请求,要求服务器确认可以这样请求。下面是这个"预检"请求的HTTP头信息

  

  

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

  • Access-Control-Request-Method

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

  • Access-Control-Request-Headers

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

2.CORS 支持度

3. document.domain

两个网页一级域名相同,只是二级域名不同,浏览器允许通过设置document.domain共享 Cookie。

举例来说,A网页是http://w1.example.com/a.html,B网页是http://w2.example.com/b.html,那么只要设置相同的document.domain,两个网页就可以共享Cookie

  1. document.domain = 'example.com';

现在,A网页通过脚本设置一个 Cookie

  1. document.cookie = "test1=hello";

B网页就可以读到这个 Cookie。

  1. var allCookie = document.cookie;

注意,这种方法只适用于 Cookie 和 iframe 窗口,LocalStorage 和 IndexDB 无法通过这种方法,规避同源政策,而要使用下文介绍的PostMessage API。

4. iframe

 (只有在主域相同时才能使用)

  1)www.a.com/a.html中:

  1. document.domain = 'a.com';
  2. var ifr = document.createElement('iframe');
  3. ifr.src = 'http://www.script.a.com/b.html';
  4. ifr.display = none;
  5. document.body.appendChild(ifr);
  6. ifr.onload = function(){
  7. var doc = ifr.contentDocument || ifr.contentWindow.document;
  8. //在这里操作doc,也就是b.html
  9. ifr.onload = null;
  10. };

  2) 在www.script.a.com/b.html中:

  1. document.domain = 'a.com';

5. window.postMessage:

该方法是 HTML5 新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。

window.postMessage的功能是允许程序员跨域在两个窗口/frames间发送数据信息。基本上,它就像是跨域的AJAX,但不是浏览器跟服务器之间交互,而是在两个客户端之间通信

postMessage方法的第一个参数是具体的信息内容,第二个参数是接收消息的窗口的源(origin),即"协议 + 域名 + 端口"。也可以设为*,表示不限制域名,向所有窗口发送。

子窗口向父窗口发送消息的写法类似。

  1. window.opener.postMessage('Nice to see you', 'http://aaa.com');

父窗口和子窗口都可以通过message事件,监听对方的消息。

  1. window.addEventListener('message', function(e) {
  2. console.log(e.data);
  3. },false);

message事件的事件对象event,提供以下三个属性。

  • event.source:发送消息的窗口
  • event.origin: 消息发向的网址
  • event.data: 消息内容

下面的例子是,子窗口通过event.source属性引用父窗口,然后发送消息。

  1. window.addEventListener('message', receiveMessage);
  2. function receiveMessage(event) {
  3. event.source.postMessage('Nice to see you!', '*');
  4. }

event.origin属性可以过滤不是发给本窗口的消息。

  1. window.addEventListener('message', receiveMessage);
  2. function receiveMessage(event) {
  3. if (event.origin !== 'http://aaa.com') return;
  4. if (event.data === 'Hello World') {
  5. event.source.postMessage('Hello', event.origin);
  6. } else {
  7. console.log(event.data);
  8. }
  9. }

【a.qq.com页面代码】

【b.qq.com页面代码】

以上demo简单解决了前端跨域通信,跨域带cookie等问题,在逻辑上完全可以实现跨域通信。但是对于不支持PostMessage特性的老版浏览器是行不通的。比如IE8-浏览器就不能很好的支持PostMessage特性

6. 服务端代理

在数据提供方没有提供对JSONP协议或者window.name协议的支持,也没有对其它域开放访问权限时,我们可以通过server proxy的方式来抓取数据。

例如当www.a.com域下的页面需要请求www.b.com下的资源文件asset.txt时,直接发送一个指向 www.b.com/asset.txt的ajax请求肯定是会被浏览器阻止。

这时,我们在www.a.com下配一个代理,然后把ajax请求绑定到这个代理路径下,例如www.a.com/proxy/, 然后这个代理发送HTTP请求访问www.b.com下的asset.txt,跨域的HTTP请求是在服务器端进行的,客户端并没有产生跨域的ajax请求。

这个跨域方式不需要和目标资源签订协议,带有侵略性,另外需要注意的是实践中应该对这个代理实施一定程度的保护,比如限制他人使用或者使用频率。

其他跨域方案

window.name:

在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的。

动态创建script

JSONP也就是利用这个原理。

利用iframe和location.hash

淘汰类技术

利用flash

淘汰类技术

参考链接:

http://tech.jandou.com/cross-domain.html

http://www.cnblogs.com/JChen666/p/3399951.html

http://www.ruanyifeng.com/blog/2016/04/cors.html

用HTML5里的window.postMessage在两个网页间传递数据

JSONP && CORS的更多相关文章

  1. 同源策略与 JSONP CORS

    同源策略与 JSONP CORS 同源策略 同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响.可以 ...

  2. vue—你必须知道的 js数据类型 前端学习 CSS 居中 事件委托和this 让js调试更简单—console AMD && CMD 模式识别课程笔记(一) web攻击 web安全之XSS JSONP && CORS css 定位 react小结

    vue—你必须知道的   目录 更多总结 猛戳这里 属性与方法 语法 计算属性 特殊属性 vue 样式绑定 vue事件处理器 表单控件绑定 父子组件通信 过渡效果 vue经验总结 javascript ...

  3. 跨域请求解决方法(JSONP, CORS)

    1.跨域 假设我们页面或者应用部署在 http://www.aaa.com 上了,而我们打算从 http://www.bbb.com 请求提取数据.一般情况下,如果我们直接使用 AJAX 来请求将会失 ...

  4. 跨域访问JSONP CORS

    一.JSONP 常用的Jquery框架支持jsonp方式请求,该方式只支持GET方法,传参大小有限,而且需要后台根据jsonp的请求方式进行封装结果返回. 其中参数jsonp默认为callback,j ...

  5. 004. 前端跨域资源请求: JSONP/CORS/反向代理

    1.什么是跨域资源请求? https://www.cnblogs.com/niuli1987/p/10252214.html 同源: 如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有 ...

  6. 解决跨域问题-jsonp&cors

    跨域的原因 浏览器的同源策略 同源策略是浏览器上为安全性考虑实施的非常重要的安全策略. 指的是从一个域上加载的脚本不允许访问另外一个域的文档属性. 举个例子:比如一个恶意网站的页面通过iframe嵌入 ...

  7. 请求不同域的数据方法:requests Jsonp cors

    在需要访问不同域的接口的数据的时候,一般有两种方式: 第一种: 使用requests模块,在业务逻辑中直接访问别的域的接口,获取数据,然后将返回的数据显示到前端页面上; 这个时候,数据访问的流程是: ...

  8. 搞定所有的跨域请求问题 jsonp CORS

    网上各种跨域教程,各种实践,各种问答,除了简单的 jsonp 以外,很多说 CORS 的都是行不通的,老是缺那么一两个关键的配置.本文只想解决问题,所有的代码经过亲自实践.   本文解决跨域中的 ge ...

  9. 再也不学AJAX了!(三)跨域获取资源 ② - JSONP & CORS

    浏览器的"同源策略"固然保障了互联网世界的数据隐私与数据安全,但是如果当我们需要使用AJAX跨域请求资源时,"同源策略"又会成为开发者的阻碍.在本文中,我们会简 ...

随机推荐

  1. Responder Pro new version could analyze Win10 memory dump

    My friend John acquired a memory dump from Windows 10, but he could analyze this memory dump with an ...

  2. PHP UEditor富文本编辑器 显示 后端配置项没有正常加载,上传插件不能正常使用

    UEditor是由百度web前端研发部开发所见即所得富文本web编辑器,具有轻量,可定制,注重用户体验等特点,开源基于MIT协议,允许自由使用和修改代码... 问题描述 我的编辑器在本地测试的时候没问 ...

  3. 解决:mysql is blocked because of many connection errors;

    标签:because service foreign errors closed 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http:// ...

  4. 百度地图API显示多个标注点带百度样式信息检索窗口的代码

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  5. css cursor属性-显示的光标的类型(形状)的用法和定义

    在网页布局的时候,在特定的地方,光标形状各有区别.这个时候,就需要用到css的cursor属性.根据自身需要选择设置鼠标指针样式. 定义和用法 cursor 属性规定要显示的光标的类型(形状). 该属 ...

  6. dedecms环境优化

    路径:dedecms/dede/templates/index_body.htm <script type="text/javascript">function sho ...

  7. Django框架之正则表达式URL误区

    问题:我学习的视频大概是2015年录的,里面用的Django版本比较老关于正则表达式URL这一块都是用的url("url(r'^admin/', admin.site.urls)," ...

  8. QT5 Thread线程

    QT5 Thread线程继承QThread方式 一.首先分析一下 QTimer Class与 Sleep()函数之间的秘密 QTimer *t = new QTimer(*parent); //创建Q ...

  9. Linux下如何释放cache内存

    转自:http://www.linuxidc.com/Linux/2015-02/112685.htm 在Linux下频繁存取文件后,物理内存会很快被用光,当程序结束后,内存不会被正常释放,而是一直作 ...

  10. linkin大话设计模式--常用模式总结

    linkin大话设计模式--常用模式总结 一,常用设计模式定义 Abstract Factory(抽象工厂模式):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类. Adapter( ...