深刻浅出FE(三)跨域Cross-Origin

目录css


1. 跨域是什么html

2. 为何有跨域前端

2.1 防止csrf攻击java

2.2 防止xss攻击node

3. 跨域解决方案?webpack

3.1 jsonpios

3.2 "跨域资源共享"(Cross-origin resource sharing)CROSnginx

3.3 document.domain + iframe跨域web

3.4 window.name + iframeajax

3.5 location.hash + iframe跨域

3.6 window.postmessage

3.7 websocket

3.8 nginx代理(来自前端常见跨域解决方案(全))

3.9 中间件代理(其余语言同理)

四、拓展

五、参考资料

1. 跨域是什么

介绍跨域以前,先来了解同源策略。同源策略就是浏览器为了保证用户信息的安全,防止恶意的网站窃取数据,禁止不一样域之间的JS进行交互。对于浏览器而言只要域名、协议和端口其中一个不一样就会引起同源策略,从而限制他们之间以下的交互行为。

浏览器的同源策略会致使跨域,即两个页面只要域名、协议和端口三者任何一个不一致,请求都会跨域。

2. 为何有跨域

跨域是为了防止csrf和xss攻击。

2.1 防止csrf攻击

(1)当用户访问银行www.bank.com,登录并操做,这时用户凭证如cookie等都生成并存放在浏览器;

(2)此时用户又访问了另外一个钓鱼网站,这时该钓鱼网站网站就能够在它的页面中,拿到银行的cookie,好比用户名,登录token等,而后发起对www.bank.com的操做;

(3)若是这时浏览器若是没有同源策略,银行也没有作响应的安全处理,那么用户就会被攻击。

2.2 防止xss攻击

xss攻击包括三种类型-存储型、反射型和DOM型,这里主要是DOM型攻击。

(1)有攻击者制做了一个钓鱼网站,这个网站内部嵌了一个和www.bank.com外观一致的iframe;

(2)此时用户进来后忽视了url不是银行的的页面,直接输入帐户名和密码;

(3)若是这时浏览器若是没有同源策略,那么这些敏感信息就会被黑客获取,银行也没有作相应的处理,那么用户就会被攻击。

3. 跨域解决方案?

3.1 jsonp

3.1.1 原理

jsonp 是一种数据调用的方式。利用<script>标签没有跨域限制的“漏洞”。

3.1.2 用法

主要有两种方式,一种是写在url中,或者是动态生成script标签,并插入到dom中,

当须要和后端通信时,本站脚本建立一个<script>元素,地址指向第三方的API网址,形如:

写在url中

<script src="http://querydata.com/jsonp/api/defalt?callback=querydata;"></script>复制代码

动态生成script,插入到dom中

// 建立一个脚本,而且告诉后端回调函数名叫querydata
var body = document.getElementsByTagName('body')[0];
var script = document.gerElement('script');
script.type = 'text/javasctipt';
script.src = 'demo.js?callback=querydata';
body.appendChild(script);复制代码


在本地提供一个回调函数querydata来接收数据(函数名可约定,或经过地址参数传递)。

function querydata(data){
    console.log(data)
}复制代码

产生的响应为json数据的包装(故称之为jsonp,即json padding),形如:

callback({"name":"coolsummer","gender":"Male"})复制代码

这样浏览器会调用querydata函数,并传递解析后json对象做为参数。本站脚本可在callback函数里处理所传入的数据。

3.1.3 优缺点及使用场景
3.1.3.1 jsonp 只能使用get请求,不能用于其余类型的请求。

3.1.3.2 没有关于 JSONP 调用的错误处理。

若是动态脚本插入有效,就执行调用;若是无效,就静默失败。失败是没有任何提示的。例如,不能从服务器捕捉到 404 错误,也不能取消或从新开始请求。不过,等待一段时间尚未响应的话,就不用理它了。(将来的 jQuery 版本可能有终止 JSONP 请求的特性)。

3.1.3.3 JSONP 被不信任的服务使用时会很危险。

由于 JSONP 服务返回打包在函数调用中的 JSON 响应,而函数调用是由浏览器执行的,这使宿主 Web 应用程序更容易受到各种攻击。若是打算使用 JSONP 服务,了解它能形成的威胁很是重要。

3.1.3.4 拓展(来自:jsonp的工做原理):

  • html标签的src属性没有同源限制(支持跨域),浏览器解析script标签时,会自动下载src属性值(url)指向的资源;
  • script标签指向的资源文件被下载后,其中的内容会被当即执行
  • 服务器端的程序会解析src属性值中的url传递的参数,根据这些参数针对性返回一个/多个函数调用表达式,这些函数调用表达式的参数就是客户端跨域想获得的数据
  • 服务器生成、返回的文件中,表达式调用的函数是已经在本地提早定义好的,而参数就是但愿从跨域服务器拿到的数据。
  • 字面的script标签能够,动态添加到dom树中的script也能够,后者更方便绑定事件。

因此只要拥有”src”这个属性的标签都拥有跨域的能力,相似的能够跨域内嵌资源的还有:

(1)<script src=""></script>标签嵌入跨域脚本。语法错误信息只能在同源脚本中捕捉到。上面jsonp也用到了呢。

(2) <link src="">标签嵌入CSS。因为CSS的松散的语法规则,CSS的跨域须要一个设置正确的Content-Type消息头。不一样浏览器有不一样的限制: IE, Firefox, Chrome, Safari (跳至CVE-2010-0051)部分 和 Opera。

(3)<video> 和 <audio>嵌入多媒体资源。

(4)<object>, <embed> 和 <applet>的插件。

(5)@font-face引入的字体。一些浏览器容许跨域字体( cross-origin fonts),一些须要同源字体(same-origin fonts)。

(6) <frame> 和 <iframe>载入的任何资源。站点可使用X-Frame-Options消息头来阻止这种形式的跨域交互。

(7) image

图像ping是与服务器进行简单、单向的跨域通讯的一种方式,请求的数据是经过查询字符串的形式发送的,而相应能够是任意内容,但一般是像素图或204相应(No Content)。 图像ping有两个主要缺点:首先就是只能发送get请求,其次就是没法访问服务器的响应文本。

var img = new Image();
img.onload = img.onerror = function(){
    alert("done!");
};
img.src = "跨域资源路径";
document.body.insertBefore(img,document.body.firstChild);复制代码

3.2 "跨域资源共享"(Cross-origin resource sharing)CROS

3.2.1 使用场景及优缺点

目前,全部浏览器都支持该功能(IE8+:IE8/9须要使用XDomainRequest对象来支持CORS),CORS也已经成为主流的跨域解决方案。

整个CORS通讯过程,都是浏览器自动完成,不须要用户参与。对于开发者来讲,CORS通讯与同源的AJAX通讯没有差异,代码彻底同样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感受。

CORS与JSONP的使用目的相同,可是比JSONP更强大。JSONP只支持GET请求,CORS支持全部类型的HTTP请求。JSONP的优点在于支持老式浏览器,以及能够向不支持CORS的网站请求数据。

3.2.2 原理

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。

MDN解释:

跨域资源共享(CORS)是一种机制,该机制使用附加的HTTP标头来告诉浏览器以使Web应用程序在一个来源运行,并从另外一个来源访问选定的资源。Web应用程序请求其来源(域,协议或端口)不一样的资源时,将执行跨域HTTP请求

3.2.3 用法

实现CORS通讯的关键是服务器。只要服务器实现了CORS接口,就能够跨源通讯。普通跨域请求只服务端设置Access-Control-Allow-Origin便可,前端无须设置.

若要带cookie请求:先后端都须要设置。因为同源策略的限制,所读取的cookie为跨域请求接口所在域的cookie,而非当前页。

浏览器请求分为两种:简单请求和非简单请求。

3.2.3.1 简单请求

对于简单请求,浏览器直接发出CORS请求。

具体来讲,就是在头信息之中,增长一个Origin字段。Origin字段用来讲明本次请求来自哪一个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否赞成此次请求。而且origin中的值必须在后端设置的Access-Control-Allow-Origin的值中,若是后端设置了Access-Control-Allow-Origin这个属性为‘*’,即origin能够为任意值。

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...复制代码

1)当origin不是服务端设置的Access-Control-Allow-Origin属性中的值,服务器会返回一个正常的http响应,这时浏览器发现返回的response中没有包含Access-Control-Allow-Origin字段,那么会抛出错误,这个错误能够被xhr的onerror回调函数捕获,可是这个错误没法经过http状态码识别,由于可能状态码为200.

2)当origin是服务端设置的Access-Control-Allow-Origin属性的值(或者Access-Control-Allow-Origin属性设置为*),服务器会多返回几个和跨域相关的字段:

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8复制代码
  • Access-Control-Allow-Origin 服务端设置的Access-Control-Allow-Origin属性的值(或者Access-Control-Allow-Origin属性设置为*,即容许全部的请求,这样一般会致使更多的麻烦,好比对接口的攻击等,通常设置为指定的域);
  • Access-Control-Allow-Credentials 可选布尔值,服务端是否发送cookie,默认状况下,Cookie不包括在CORS请求之中,即此值为false;此时若是想要在请求中带上cookie,那么前端须要设置带上cookie的字段withCredentials字段。

以xhr、ajax或者axios请求为例:

xhr

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;复制代码

ajax

$.ajax({
        url: "http://localhost:9000",
        type: 'GET',
        xhrFields: {
            withCredentials: true // 这里设置了withCredentials
        },
        success: function(data) {
            console.log(data)
        },
        error: function(err) {
            console.error(err)
        }
    })复制代码

axios

axios.defaults.withCredentials=true复制代码

服务端也要设置这个属性为true,不一样语言设置方式各有不一样,在此不一一举例

Access-Control-Allow-Credentials: true复制代码

注意:前端和服务端必须同时设置,不然两端任意一端设置,另外一端不设置则不会携带cokie。可是省略withCredentials设置,有的浏览器仍是会一块儿发送Cookie。这时,能够显式关闭withCredentials。

xhr.withCredentials = false;复制代码
  • Access-Control-Expose-Headers 字段可选。

CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。若是想拿到其余字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')能够返回FooBar字段的值。

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

简单请求是知足如下全部条件的请求(如下内容来自MDN:跨域资源共享(CORS),稍有修改):

注意:这些与Web内容已经能够发出的跨站点请求种类相同,除非服务器发送适当的标头,不然不会向请求者释放响应数据。所以,能够防止跨站点请求伪造的站点没必要担忧HTTP访问控制。

注: WebKit每日和Safari浏览器技术预览放置在容许的值的额外限制AcceptAccept-LanguageContent-Language头。若是这些标头中的任何一个具备“非标准”值,则WebKit / Safari不会将请求视为“简单请求”。没有记录WebKit / Safari认为“非标准”的值,如下WebKit错误除外:

没有其余浏览器实现这些额外的限制,由于它们不是规范的一部分。

3.2.3.2 非简单请求

非简单请求会在简单请求发送以前,增长一次“预检”请求。

“简单请求”即不须要“预检”,“预检”请求首先经过OPTIONS方法将HTTP请求发送到另外一个域上的资源,以肯定实际请求是否能够安全发送。跨站点请求这样被预检,由于它们可能会影响用户数据。

浏览器在发送请求前只要不是简单请求,就会先发一个预检请求检测请求是否被服务器接受,好比域名是否在服务器所容许的白名单中,以及可使用哪些HTTP动词和头信息字段。

一个常见的预检请求头以下所示:

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...复制代码

方法必须是OPTIONS

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

  • Access-Control-Request-Method告知服务器实际请求所使用的 HTTP 方法。
  • Access-Control-Request-Headers告知服务器实际请求所携带的自定义首部字段。该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段

服务器基于从预检请求得到的信息来判断,是否接受接下来的实际请求。

服务器收到"预检"请求之后,检查了OriginAccess-Control-Request-MethodAccess-Control-Request-Headers字段之后,确认容许跨源请求,就能够作出回应---是否容许跨域访问。

//来自MDN
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2019 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复制代码

上面一段代码是常见的预检请求返回内容,最关键是Access-Control-Allow-Origin这个字段,表示http://api.bob.com能够请求数据。该字段也能够设为星号,表示赞成任意跨源请求。

若是浏览器否认了"预检"请求,会返回一个正常的HTTP回应,可是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不一样意预检请求,所以触发一个错误,XMLHttpRequest对象的onerror回调函数捕获。控制台会打印出以下的报错信息。

XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.复制代码

服务器回应的其余CORS相关字段以下:

Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000复制代码
  • Access-Control-Allow-Methods它的值是逗号分隔的一个字符串,代表服务器支持的全部跨域请求的方法。注意,和请求的Access-Control-Allow-Method字段区别,请求的Access-Control-Allow-Method字段是单个的,表示档次请求的方法,是单个方法,而服务端响应的Access-Control-Allow-Methods返回的是全部支持的方法,而不单是浏览器请求的那个方法。这是为了不屡次"预检"请求。
  • Access-Control-Allow-Headers若是浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,代表服务器支持的全部头信息字段,不限于浏览器在"预检"中请求的字段。
  • Access-Control-Allow-Credentials该字段与简单请求时的含义相同,是否携带cookie。
  • Access-Control-Max-Age该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即容许缓存该条回应1728000秒(即20天),在此期间,不用发出另外一条预检请求。

服务端经过预检请求后:

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

下面是"预检"请求以后,浏览器的正常CORS请求。

PUT /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...复制代码

上面头信息的Origin字段是浏览器自动添加的。

下面是服务器正常的回应。

Access-Control-Allow-Origin: http://api.bob.com
Content-Type: text/html; charset=utf-8复制代码

上面头信息中,Access-Control-Allow-Origin字段是每次回应都一定包含的。

3.3 document.domain + iframe跨域

3.3.1 使用场景及优缺点

对于主域名相同,而子域名不一样的状况,可使用document.domain来跨域 这种方式很是适用于iframe跨域的状况。

缺点是只能用于iframe,并且若是两个页面通讯必须经过第三个页面,而且传递值只能是单向。

MDN解释:

Document 接口的 domain 属性获取/设置当前文档的原始域部分,经常使用于同源策略

3.3.2 原理

两个页面都经过js强制设置document.domain为基础主域,就实现了同域。

3.3.3 用法(来自前端常见跨域解决方案(全)

1.)父窗口:(www.a.com/a.html)

<iframe id="iframe" src="http://child.a.com/b.html"></iframe>
<script>
    document.domain = 'a.com';
    var user = 'admin';
</script>复制代码

2.)子窗口:(child.a.com/b.html)

<script>
    document.domain = 'a.com';
    // 获取父窗口中变量
    alert('get js data from parent ---> ' + window.parent.user);
</script>复制代码

3.4 window.name + iframe

3.4.1 使用场景及优缺点

浏览器窗口有window.name属性。这个属性的最大特色是,不管是否同源,只要在同一个窗口里,前一个网页设置了这个属性,后一个网页能够读取它。而且能够支持很是长的 name 值(2MB)。

这种方法的优势是,window.name容量很大,能够放置很是长的字符串;缺点是必须监听子窗口window.name属性的变化,影响网页性能。

3.4.2 原理

经过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操做。

3.4.3 用法

父窗口先打开一个子窗口,载入一个不一样源的网页,该网页将信息写入window.name属性。

window.name = data;复制代码

接着,子窗口跳回一个与主窗口同域的网址。

location = 'http://parent.url.com/xxx.html';复制代码

而后,主窗口就能够读取子窗口的window.name了。

var data = document.getElementById('myFrame').contentWindow.name;复制代码

3.5 location.hash + iframe跨域

3.5.1 实现原理及优缺点

a与b跨域相互通讯,经过中间页c来实现(且c与a是同域)。 三个页面,不一样域之间利用iframe的location.hash传值,相同域之间直接js访问来通讯。

3.5.2 用法

A域:a.html -> B域:b.html -> A域:c.html,a与b不一样域只能经过hash值单向通讯,b与c也不一样域也只能单向通讯,但c与a同域,因此c可经过parent.parent访问a页面全部对象。

嵌套关系为: a.html中嵌套iframe(b.html),b.html中嵌套iframe(c.html),c中能够得到a.html的对象。

1.)a.html:(www.domain1.com/a.html)

<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe>
<script>
    var iframe = document.getElementById('iframe');

    // 向b.html传hashsetTimeout(function() {
        iframe.src = iframe.src + '#user=admin';
    }, 1000);
    
    // 开放给同域c.html的回调方法
    function onCallback(res) {
        alert('data from c.html ---> ' + res);
    }
</script>复制代码

2.)b.html:(www.domain2.com/b.html)

<iframe id="iframe" src="http://www.domain1.com/c.html" style="display:none;"></iframe>
<script>
    var iframe = document.getElementById('iframe');

    // 监听a.html传来的hash值,再传给c.html
    window.onhashchange = function () {
        iframe.src = iframe.src + location.hash;
    };
</script>复制代码

3.)c.html:(www.domain1.com/c.html)

<script>
    // 监听b.html传来的hash值
    window.onhashchange = function () {
        // 再经过操做同域a.html的js回调,将结果传回
        window.parent.parent.onCallback('hello: ' + location.hash.replace('#user=', ''));
    };
</script>复制代码

3.6 window.postmessage

3.6.1 使用场景和优缺点

a) 页面和其打开的新窗口的数据传递

b) 多窗口之间消息传递

c) 页面与嵌套的iframe消息传递

d) 上面三个场景的跨域数据传递

优势是公共的标准,使用简单,缺点是用于接收消息的页面可能存在跨站安全问题,由于没法检查origin和source属性会致使跨站点脚本攻击。

这个应该就是之后解决dom跨域通用方法了

3.6.2 原理

API为window对象新增了一个window.postMessage方法,容许跨窗口通讯,不论这两个窗口是否同源。

3.6.3 用法(来自MDN)

/*
 * A窗口的域名是<http://example.com:8080>,如下是A窗口的script标签下的代码:
 */

var popup = window.open(...popup details...);

// 若是弹出框没有被阻止且加载完成

// 这行语句没有发送信息出去,即便假设当前页面没有改变location(由于targetOrigin设置不对)
popup.postMessage("The user is 'bob' and the password is 'secret'",
                  "https://secure.example.net");

// 假设当前页面没有改变location,这条语句会成功添加message到发送队列中去(targetOrigin设置对了)
popup.postMessage("hello there!", "http://example.org");

function receiveMessage(event)
{
  // 咱们能相信信息的发送者吗?  (也许这个发送者和咱们最初打开的不是同一个页面).
  if (event.origin !== "http://example.org")
    return;

  // event.source 是咱们经过window.open打开的弹出页面 popup
  // event.data 是 popup发送给当前页面的消息 "hi there yourself! the secret response is: rheeeeet!"
}
window.addEventListener("message", receiveMessage, false);复制代码
/*
 * 弹出页 popup 域名是<http://example.org>,如下是script标签中的代码:
 */

//当A页面postMessage被调用后,这个function被addEventListenner调用
function receiveMessage(event)
{
  // 咱们能信任信息来源吗?
  if (event.origin !== "http://example.com:8080")
    return;

  // event.source 就当前弹出页的来源页面
  // event.data 是 "hello there!"

  // 假设你已经验证了所受到信息的origin (任什么时候候你都应该这样作), 一个很方便的方式就是把event.source
  // 做为回信的对象,而且把event.origin做为targetOrigin
  event.source.postMessage("hi there yourself! the secret response " +
                           "is: rheeeeet!",
                           event.origin);
}

window.addEventListener("message", receiveMessage, false);复制代码

注意:任何窗口能够在任何其余窗口访问此方法,在任什么时候间,不管文档在窗口中的位置,向其发送消息。 所以,用于接收消息的任何事件监听器必须首先使用origin和source属性来检查消息的发送者的身份。 这不能低估:没法检查origin和source属性会致使跨站点脚本攻击。

3.7 websocket

3.7.1 应用场景及优缺点

咱们知道websocket是H5为了解决http协议单项请求的缺陷。虽然能够用轮训解决-每隔一段时间查询状态变化,可是这种方式存在延时且对服务端形成很大负载。

WebSocket 协议在2008年诞生,2011年成为国际标准。全部浏览器都已经支持了。

websocket最大的特色就是服务器能够主动向客户端推送信息,客户端也能够主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。

websocket的特色:

(1)创建在 TCP 协议之上,服务器端的实现比较容易。

(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,而且握手阶段采用 HTTP 协议,所以握手时不容易屏蔽,能经过各类 HTTP 代理服务器。

(3)数据格式比较轻量,性能开销小,通讯高效。

(4)能够发送文本,也能够发送二进制数据。

(5)没有同源限制,客户端能够与任意服务器通讯。

(6)协议标识符是ws(若是加密,则为wss),服务器网址就是 URL。

3.7.2 原理

WebSocket是一种通讯协议,使用ws://(非加密)和wss://(加密)做为协议前缀。该协议不实行同源政策,只要服务器支持,就能够经过它进行跨源通讯。

3.7.3 用法

以下是websocket的一个简单的用法

var ws = new WebSocket("wss://echo.websocket.org");

ws.onopen = function(evt) { 
  console.log("Connection open ..."); 
  ws.send("Hello WebSockets!");
};

ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
  ws.close();
};

ws.onclose = function(evt) {
  console.log("Connection closed.");
};      复制代码

原生WebSocket API使用起来不太方便,可使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。本此由于是模拟就没有安装了用了WebSocket。

3.8 nginx代理(来自前端常见跨域解决方案(全))

一、 nginx配置解决iconfont跨域

浏览器跨域访问js、css、img等常规静态资源被同源策略许可,但iconfont字体文件(eot|otf|ttf|woff|svg)例外,此时可在nginx的静态资源服务器中加入如下配置。

location / {
  add_header Access-Control-Allow-Origin *;
}复制代码

二、 nginx反向代理接口跨域

跨域原理: 同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不须要同源策略,也就不存在跨越问题。

实现思路:经过nginx配置一个代理服务器(域名与domain1相同,端口不一样)作跳板机,反向代理访问domain2接口,而且能够顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登陆。

nginx具体配置:

#proxy服务器
server {
    listen       81;
    server_name  www.domain1.com;

    location / {
        proxy_pass   http://www.domain2.com:8080;  #反向代理
        proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
        index  index.html index.htm;

        # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
        add_header Access-Control-Allow-Origin http://www.domain1.com;  #当前端只跨域不带cookie时,可为*
        add_header Access-Control-Allow-Credentials true;
    }
}复制代码

1.) 前端代码示例:

var xhr = new XMLHttpRequest();

// 前端开关:浏览器是否读写cookie
xhr.withCredentials = true;

// 访问nginx中的代理服务器
xhr.open('get', 'http://www.domain1.com:81/?user=admin', true);
xhr.send();复制代码

2.) Nodejs后台示例:

var http = require('http');
var server = http.createServer();
var qs = require('querystring');

server.on('request', function(req, res) {
    var params = qs.parse(req.url.substring(2));

    // 向前台写cookie
    res.writeHead(200, {
        'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'   // HttpOnly:脚本没法读取
    });

    res.write(JSON.stringify(params));
    res.end();
});

server.listen('8080');
console.log('Server is running at port 8080...');复制代码

3.9 中间件代理(其余语言同理)

node中间件实现跨域代理,原理大体与nginx相同,都是经过启一个代理服务器,实现数据的转发,也能够经过设置cookieDomainRewrite参数修改响应头中cookie中域名,实现当前域的cookie写入,方便接口登陆认证。

四、拓展

1.注意本文主要讨论前端的CROS,关于服务端的CROS可参考:服务器端访问控制(CORS)

2.经过离线缓存,如localhost和indexdb也可以跨域

3.阮一峰文章浏览器同源政策及其规避方法中提到的片断识别码是一种不太常见的方式。

五、参考资料

1.跨域资源共享 CORS 详解

2.jsonp的工做原理

3.结合 JSONP 和 jQuery 快速构建强大的 mashup

4.跨域资源共享 CORS 详解

5.跨域资源共享(CORS)

6.Document.domain

7.前端常见跨域解决方案(全)

8.SendMessage、PostMessage原理

9.WebSocket 教程

10.浏览器同源政策及其规避方法

相关文章
相关标签/搜索