同源政策和跨域

同源政策

同源政策(same-origin policy),作为浏览器安全的基石,可以保证用户信息的安全,防止恶意的网站窃取数据,

1.何为同源

  • 协议相同
  • 域名相同
  • 端口相同

2.非同源受限制的行为

  • 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
  • 无法接触非同源网页的 DOM
  • 无法向非同源地址发送 AJAX 请求(可以发送,但浏览器会拒绝接受响应)

注意: 对于当前页面来说页面存放的 JS 文件的域不重要,重要的是加载该 JS 页面所在什么域

跨域

当浏览器在一个页面上去请求另一个域名的资源,这两个域是不同源的,就需要跨域的操作;

跨域的方式:JSONP、CORS、降域 和 postMessage

1.JSONP(JSON with padding)

通过JSONP实现跨域的基本思想是:HTML中的<script>标签可以下载其它域下的js文件,这种做法是不受同源政策的限制的。因此,可以利用这个特性从不同源的域下获取数据。下面通过一个例子讲解实现方式。

  • HTML的内容
<body>
  <ul></ul>
  <button>展示数据</button>
</body>
<script>
function $(selector) {
  return document.querySelector(selector)
}

//这里给button绑定事件,点击button时,在<head>里添加一个<script>标签,指定src
$('button').onclick = function(){
  var script = document.createElement('script')
  script.src = 'http://127.0.0.1:8080/getData?callback=showData';
  document.head.appendChild(script)
}

//这里声明showData函数
function showData(data) {
  var html = ''
  for(var i=0; i<data.length;i++) {
    html += '<li>' + data[i] + '</li>'
  }
  $('ul').innerHTML = html
}
</script>
  • 搭建一个服务器
var http = require('http')
var fs = require('fs')
var path = require('path')
var url = require('url')

var server = http.createServer(function(req,res) {
  routePath(req,res)
})

function routePath(req,res) {
  var pathObj = url.parse(req.url,true)
  switch(pathObj.pathname) {
    case '/getData':
    var data = [
      '数据1',
      '数据2',
      '数据3'
    ]

//<script>标签加载的是JS文件,服务器返回的数据是JSON格式的数据,所以这里对返回的数据做处理
    if(pathObj.query.callback) {
      res.end(pathObj.query.callback + '(' + JSON.stringify(data) + ')')
//最终得到的是执行showData函数,参数是原本要返回的数据,即上面的data
    }else{
      res.end(JSON.stringify(data))
    }

    break;

//这里是没有路由时,处理静态目录
    default:
      fs.readFile(path.join(__dirname, pathObj.pathname), function(e, data){
        if(e){
          res.writeHead(404, 'not found')
          res.end('<h1>404 Not Found</h1>')
        }else{
          res.end(data)
        }
      }) 
  }
}

server.listen(8080)

我们打开html的url是http://localhost:8080/index.html,而我们在html中添加的<script>的标签的src是http://127.0.0.1:8080/getData?callback=showData,根据同源政策,这两个是不同源的。但是,<script>标签加载数据是不受同源政策限制的。

正常的用ajax发送请求
//对js做个修改
$('button').onclick = function(){
var xhr = new XMLHttpRequest()
xhr.open('GET','http://127.0.0.1:8080/getData?callback=showData')
xhr.onload = function(){
 console.log(xhr.responseText)
 }
 xhr.send()
}

点击按钮,发送请求后,查看控制台,发现浏览器拒绝接受响应,因为发送请求的url和当前页面的url是不同源的

2.CORS

CORS,全称是“跨域资源共享”(Cross-origin resource sharing),它允许浏览器向跨域的服务器,发出XMLHttpRequest请求,从而克服了 AJAX 只能同源使用的限制。

基本原理是:当使用 XMLHttpRequest 发送请求时,浏览器如果发现该请求不符合同源政策,会给该请求加一个请求头:Origin,服务器进行一系列处理,如果确定接受请求则在返回结果中加入一个响应头:Access-Control-Allow-Origin; 浏览器接收响应时,判断响应头中是否包含 Origin 的值,如果有则浏览器会处理响应,我们就可以拿到响应数据,如果不包含浏览器直接驳回,这时我们无法拿到响应数据。

一个例子:

  • HTML的内容
<body>
  <ul></ul>
  <button>展示数据</button>
</body>
<script>
  function $(selector) {
    return document.querySelector(selector)
  }

  function showData(data) {
    var html = ''
    for(var i=0; i<data.length;i++) {
      html += '<li>' + data[i] + '</li>'
    }
    $('ul').innerHTML = html
  }

  $('button').onclick = function(){
    var xhr = new XMLHttpRequest()
    xhr.open('GET','http://127.0.0.1:8080/getData')
    xhr.onload = function(){
      showData(JSON.parse(xhr.responseText))
    }
    xhr.send()
  }
</script>
  • 搭建服务器
var http = require('http')
var fs = require('fs')
var path = require('path')
var url = require('url')

var server = http.createServer(function(req,res) {
  routePath(req,res)
})

function routePath(req,res) {
  var pathObj = url.parse(req.url,true)
  switch(pathObj.pathname) {
    case '/getData':
      var data = [
        '数据1',
        '数据2',
        '数据3'
      ]
//这里是关键的一步,给响应头添加Access-Control-Allow-Origin,后面的值表示接受该域名的请求
      res.setHeader('Access-Control-Allow-Origin','http://localhost:8080')
      res.end(JSON.stringify(data))

    break;

    default:
      fs.readFile(path.join(__dirname, pathObj.pathname), function(e, data){
        if(e){
          res.writeHead(404, 'not found')
          res.end('<h1>404 Not Found</h1>')
        }else{
          res.end(data)
        }
      }) 
  }
}

server.listen(8080)

我们点击按钮,发送请求,同时查看请求头和响应头的内容。

请求头
响应头

我们可以看到,Origin的值和Access-Control-Allow-Origin的值一样,最终的请求也是有效的,浏览器页接受了响应。

与JSONP的比较

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

3.降域

降域是通过设置 document.domain 的值,解决跨域问题;
降域的适用范围较窄,一般是页面中有iframe元素,需要获取iframe中的页面的数据时使用降域来解决跨域的问题;

基本原理:例如
A页面域名为:a.com;ifram中引入B页面
B页面域名为:b.com;
在A、B页面写入document.domain = 'localhost:8080.com';(这里的值是主域名)
这样的话就可以在A页面中获取B页面的DOM数据,操作B页面中的元素;

4.postMessage(不常用)

这个方法是利用HTML5中引入的一个API:window.postMessage() ;
它的原理就是,比如一个父窗口,向一个子窗口(比如ifram)发送消息,子窗口可以通过message事件,监听到父窗口发送过来的消息,并作出相应的操作,同理,子窗口也能向父窗口发送消息。通过这种操作也能实现跨域。

参考

降域和postMessage
JavaScript教程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容