同源策略及跨域的解决方案

1,644 阅读5分钟

同源策略的基本概念

同源策略简介:1995年,同源政策由Netscape引入浏览器,目前,所有的浏览器都实行这个政策.

同源:指的是协议,域名,端口号相同都称为是同源网站

同源策略的目的

同源策略的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据

同源策略的限制范围

随着互联网的发展,'同源策略'越来越严格,目前,如果非同源,下面的行为将会受到限制:

  1. Cookie、LocalStorage 和 IndexDB 无法读取。
  2. DOM 无法获得。
  3. AJAX 请求在浏览器端有跨域限制

虽然这些限制是保证了数据的安全性,但是同时他也给开发者带来了一定的困扰,由于网络的不断发展,不同公司的业务更加庞大,所需要的技术也更加精准,所以各个部门都会有自己的域名,这时就会出现跨域现象

解决跨域的方案

方法一JSONP:

JSONP(JSON with Padding)、可用于解决主流浏览器的跨域数据访问的问题。

由于DOM中的src属性支持跨域,所以JSONP的原理也就是利用了script标签中的src来实现跨域的,除了script标签之外,还有img和link标签.

<!--不受同源策略限制的标签-->
<img src="http://www.api.com/1.jpg" alt="">
<link rel="stylesheet" href="http://www.api.com/1.css">
<script src="http://www.api.com/1.js"></script>

JSONP实现代码

 <script>
      var inp = document.getElementById('inp');
      var ul = document.querySelector('ul');

      function suggest_so(data){
        // console.log(data)
        ul.innerHTML =''
        var {result} = data;
        result.forEach(function(ele){
          var li = document.createElement('li');
          li.innerHTML = ele.word;
          ul.appendChild(li)
        })
      }
      inp.oninput = function(){
        if(document.getElementsByClassName('ss').length>0){
          document.getElementsByClassName('ss')[0].remove()
        }
        var val = inp.value;
        // 通过动态创建src属性实现
        var jsonp = document.createElement('script');
        jsonp.src = 'https://sug.so.360.cn/suggest?callback=suggest_so&encodein=utf-8&encodeout=utf-8&format=json&fields=word&word='+val;
        jsonp.className = 'ss';
        document.body.appendChild(jsonp)

      }
    </script>

在script属性中有一个src属性,里面有一个callback回调函数,这个回调函数是在src里面的,他的作用就是当响应来到时,应该在页面中调用的函数,而数据就是传入回调函数的JSON数据.

JSONP实现原理

  • 1)利用了script的src属性支持跨域访问
  • 2)script标签的后面需要写上需要请求的页面,发送了一个方法的名字到服务器.
  • 3)服务器接收到名字之后,拼接一个方法的调用在参数中传入需要给浏览器的数据
  • 4)返回浏览器,浏览器把他们当js解析,没有跨域问题,从服务器获取数据
原理:服务端返回一个定义好的js函数的调用,并且将服务器的数据以该函数参数的形式传递过来,这个方法需要前后端配合.

jQuery实现跨域

$(function(){
  $('#inp').on('input',function(){
    $.ajax({
      url:'https://sug.so.360.cn/suggest?callback=suggest_so&encodein=utf-8&encodeout=utf-8&format=json&fields=word&word='+$(this).val(),
      dataType:'jsonp',
      jsonpCallback:'suggest_so',
      success:function(data){
        var {result} = data;
        var str = template('tpl',{result});
        $('ul').html(str)
      }
    })
  })
})

这里用的是dataType和jsonpCallback,来实现跨域

JSONP的缺点:

  • 1它只支持GET请求而不支持POST等其它类型的HTTP请求
  • 2它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。
  • 3 jsonp在调用失败的时候不会返回各种HTTP状态码。
  • 4缺点是安全性。万一假如提供jsonp的服务存在页面注入漏洞,即它返回的javascript的内容被人控制的。那么结果是什么?所有调用这个 jsonp的网站都会存在漏洞。于是无法把危险控制在一个域名下…所以在使用jsonp的时候必须要保证使用的jsonp服务必须是安全可信的。

只支持get请求的原因:

由于script标签只能发送get请求 所以jsonp不支持post方式的跨域

方法二CORS: 具体流程: 浏览器发送跨域请求

服务器端收到一个跨域请求后,在响应头中添加Access-Control-Allow-Origin Header资源权限配置。发送响应

浏览器收到响应后,查看是否设置了header('Access-Control-Allow-Origin:请求源域名或者*');

如果当前域已经得到授权,则将结果返回给JavaScript。否则浏览器忽略此次响应。

app.get('/getmsg',(req,res)=>{
    let msg =[{name:'zs',age:18,gender:'boy'}];
    res.header('Access-Control-Allow-Origin',"*")
    res.json({msg:'ok',code:200,data:msg})
})

这种方法虽然简单,但是这种方法是h5新出的,所以这种方法有兼容性问题

方法三Nginx:

同源策略只是针对的浏览器端,所以这里通过服务端反向代理的方式实现跨域, Nginx解决跨域问题通过Nginx反向代理将对真实服务器的请求转移到本机服务器来避免浏览器的"同源策略限制"。

方法四:

window.name+iframe 需要目标服务器响应window.name。

方法五:

window.location.hash+iframe 同样需要目标服务器作处理。

方法六:

html5的 postMessage+ifrme 这个也是需要目标服务器或者说是目标页面写一个postMessage,主要侧重于前端通讯。

HTML5为了解决这个问题,引入了一个全新的API:跨文档通信 API(Cross-document messaging)。

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

举例来说,父窗口http://aaa.com向子窗口http://bbb.com发消息,调用postMessage方法就可以了。

var popup = window.open('http://bbb.com', 'title');
popup.postMessage('Hello World!', 'http://bbb.com');

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

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

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

方法七:

Cookie 是服务器写入浏览器的一小段信息,只有同源的网页才能共享。但是,两个网页一级域名相同,只是二级域名不同,浏览器允许通过设置document.domain共享 Cookie。

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

document.domain = 'example.com';

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

document.cookie = "test1=hello";

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

var allCookie = document.cookie;

注意,这种方法只适用于 Cookie 和 iframe 窗口,LocalStorage 和 IndexDB 无法通过这种方法,规避同源政策,