跨域

    1. 跨域问题如何解决
      1. 什么是跨域

同源策略:

所谓同源策略,它是浏览器的一种最核心最基本的安全策略。它对来至不同源的文档或这脚本对当前文档的读写操作做了限制。

同协议、ip、端口的脚本才会执行。

只要协议、域名、端口有任何一个不同,都被当作是不同的域.

js跨域是指通过js在不同的域之间进行数据传输或通信.

      1. 为什么限制跨域

为什么要有这个策略,想必你已经知道,那就是因为保证用户的信息安全。

假如没有同源策略

  1. 假设现在有a.com和b.com两个域,如果没有这一安全策略,那么当用户在访问a.com时,a.com的一段脚本就可以在不加载b.com的页面而随意修改或者获取b.com上面的内容。这样将会导致b.com页面的页面发生混乱,甚至信息被获取,包括服务器端发来的session。这样的话,我们的web世界将是一片混乱。也是因为浏览器的同源策略,保证来至不同源的对象不会互相干扰,保证了我们访问页面最基本的安全。
  2. 比如,cookie一般用于状态控制,常用于存储登录的信息,如果允许跨域访问,那么别的网站只需要一段脚本就可以获取你的cookie,从而冒充你的身份去登录网站,造成非常大的安全问题,因此,现代浏览器均推行同源策略。
      1. 什么会限制跨域

限制跨域

  1. cookie
  2. localStorage
  3. ajax请求

src可以跨域

在这里需要注意的是,文档中的所有带“src”属性的标签都可以跨域加载资源,而不受同源策略的限制。

如<script>、<img>、<iframe>、<link>等。如果你在页面定义了这些标签,在页面加载时都对不同源的资源发起了一次GET请求。但是通过src加载的资源,浏览器限制了脚本对其返回的内容无法读写。特别是在ajax请求的时候,特别要注意XMLHttpRequest的时候是无法跨域访问的。

      1. 怎么解决跨域
        1. 通过修改document.domain来跨子域

同源策略认为域和子域属于不同的域,如:

child1.a.com 与 a.com,

child1.a.com 与 child2.a.com,

xxx.child1.a.com 与 child1.a.com

两两不同源,可以通过设置 document.damain='a.com',浏览器就会认为它们都是同一个源。想要实现以上任意两个页面之间的通信,两个页面必须都设置documen.damain='a.com'。

此方式的特点:

1. 只能在父域名与子域名之间使用,且将 xxx.child1.a.com域名设置为a.com后,不能再设置成child1.a.com。

2. 存在安全性问题,当一个站点被攻击后,另一个站点会引起安全漏洞。

3. 这种方法只适用于 Cookie 和 iframe 窗口。

 

        1. 使用window.name来进行跨域

window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。

// 打开必应 https://www.bing.com/

// 保留控制台log(勾上Preserve log

 

> window.name

""

> window.name='text';

"text"

> location.href='http://www.google.com';

"http://www.google.com"

Navigated to https://www.google.com/

 

> window.name

"text

我们知道,使用iframe的src属性,可以加载不同域中的网页,

我们也可以使用$('iframe').contentWindow来拿到iframe中页面的window对象,

只是这个window对象中可以访问的属性是很少的。

window.name这个属性不是一个简单的全局属性 ---

只要在一个window下,无论url怎么变化,只要设置好了window.name,那么后续就一直都不会改变,同理,在iframe中,即使url在变化,iframe中的window.name也是一个固定的值,利用这个,我们就可以实现跨域了。

下面是localhost:8088/test2.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <title>test2</title>

</head>

<body>

  <h2>test2页面</h2>

  <script>

    var person = {

      name: 'wayne zhu',

      age: 22,

      school: 'xjtu'

    }

    window.name = JSON.stringify(person)

  </script>

</body>

</html>

  即我们希望吧test2.html中的数据传递出去,到localhost:8081/test1.html中去。  

  下面是localhost:8081/test1.html

       这里的意图很明确,就是使用iframe将test2.html加载过来,因为只是为了实现跨域,所以将之隐藏,但是,这时已经完成了最重要的一步,就是将iframe中window.name已经成功设置,但是现在还获取不了,因为是跨域的,所以,我们可以把src设置为当前域的proxy.html。

  另外,这里之所以要设置flag,是因为每当改变location的时候,就会重新来一次onload,所以我们希望获取到数据之后,就直接close(),故采用此种方法。

这个proxy.html内容如下:

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <title>proxy</title>

</head>

<body>

  <p>这是proxy页面</p>

</body>

</html>

  是的,什么都没有,当然,我们也可以把src就替换成test1.html,但是这样的坏处很明显,就是需要把test1.html中的数据加载一遍,这不是我们所希望的。

 

        1. 使用HTML5新提供的postMessage api

/* 子窗口 */

window.onmessage = function(e) {  

  if (e.origin !== 'http://bbb.com') return;  

  var payload = JSON.parse(e.data);  

  switch (payload.method) {  

    case 'set':  

      localStorage.setItem(payload.key, JSON.stringify(payload.data));  

      break;  

    case 'get':  

      var parent = window.parent;  

      var data = localStorage.getItem(payload.key);  

      parent.postMessage(data, 'http://aaa.com');  

      break;  

    case 'remove':  

      localStorage.removeItem(payload.key);  

      break;  

  }  

};  

/* 父窗口 */

var win = document.getElementsByTagName('iframe')[0].contentWindow;  

var obj = { name: 'Jack' };  

// 存入对象  

win.postMessage(JSON.stringify({key: 'storage', method: 'set', data: obj}), 'http://bbb.com');  

// 读取对象  

win.postMessage(JSON.stringify({key: 'storage', method: "get"}), "*");  

window.onmessage = function(e) {  

  if (e.origin != 'http://aaa.com') return;  

  // "Jack"  

  console.log(JSON.parse(e.data).name);  

};  

 

        1. 1. 通过jsonp跨域

jsonp在页面上引入不同域上的js脚本文件实现请求不同域上的数据

(1) 通过script标签引入一个js文件

(2) js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作为参数传入

注:需要服务器端的页面进行相应的配合。

JSONP的原理:(举例:a.com/jsonp.html想得到b.com/main.js中的数据)在a.com的jsonp.html里创建一个回调函数xxx,动态添加<script>元素,向服务器发送请求,请求地址后面加上查询字符串,通过callback参数指定回调函数的名字。请求地址为http://b.com/main.js?callback=xxx。在main.js中调用这个回调函数xxx,并且以JSON数据形式作为参数传递,完成回调。

function addScriptTag(src) {  

  var script = document.createElement('script');  

  script.setAttribute("type","text/javascript");  

  script.src = src;  

  document.body.appendChild(script);  

}

window.onload = function () {  

  addScriptTag('http://example.com/ip?callback=foo');  

}

//window.onload是为了让页面加载完成后再执行

function foo(data) {  

   console.log(data.name+"欢迎您");

};

//b.com/main.js中的代码

foo({name:"hl"})

这样便实现了跨域的参数传递。

 

采用jsonp跨域也存在问题:

1. 使用这种方法,只要是个网站都可以拿到b.com里的数据,存在安全性问题。需要网站双方商议基础token的身份验证,这里不详述。

2. 只能是GET,不能POST。

3. 可能被注入恶意代码,篡改页面内容,可以采用字符串过滤来规避此问题。

客户端页面完整代码

<!DOCTYPE html> 

<html> 

<head> 

<meta charset="utf-8"> <title>JSONP 实例</title> 

</head> 

<body> 

<div id="divCustomers"></div> 

<script type="text/javascript">

function callbackFunction(result, methodName) { 

var html = '<ul>';

for(var i = 0; i < result.length; i++) {

 html += '<li>' + result[i] + '</li>';

} 

html += '</ul>';

document.getElementById('divCustomers').innerHTML = html; }

</script> 

<script type="text/javascript" src="http://www.runoob.com/try/ajax/jsonp.php?jsoncallback=callbackFunction">

</script> 

</body>

 </html>
        1. jQuery 使用 JSONP:$.getJSON()

 

        1. WebSocket

WebSocket是HTML5开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

 

在WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。

        1. CORS

CORS全称cross-origin resource sharing,意为跨站点资源共享,是w3c官方推荐的一种跨域方案,目前所有浏览器对其兼容性都表现良好,而且支持所有的请求方式,可以预见是未来最为广泛使用的跨域方案。

在支持CORS方案的浏览器中发送AJAX请求,请求地址为目标域的绝对路径时,请求头中会带有一个字段:withCredentials: true,这个字段会让浏览器发送身份信息到服务端,如SSL、cookie等。与此同时,在服务端中设置响应头中的Access-Control-Allow-Origin: *,则可以实现一个跨域请求。

注意:在实际使用中,最好对Access-Control-Allow-Origin进行指定域名的设置,如:Access-Control-Allow-Origin: www.test.com。如果使用Access-Control-Allow-Origin: *,则会允许所有来源进行跨域访问,这会带来比较大的安全问题。

        1.  服务器代理

Nginx proxy_pass

# 80 cat

upstream pig-client {

server 127.0.0.1:80 fail_timeout=0;

}

# app

upstream pig-control {

server 127.0.0.1:8080 fail_timeout=0;

}

#page 新阳蓝光项目

upstream pig-xinyang {

server 127.0.0.1:4200 fail_timeout=0;

}



server {

listen 80;

server_name hardboy.cn;

index index.html index.htm;

location / {

proxy_pass http://pig-client;

try_files $uri $uri/ /index.html =404;

}

}

server {

listen 80;

server_name notapig.cn;

index index.html index.htm;

location / {

proxy_pass http://pig-xinyang;

try_files $uri $uri/ /index.html =404;

}

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章