美文网首页
同源政策于跨源通信

同源政策于跨源通信

作者: MajorDong | 来源:发表于2019-08-06 18:03 被阅读0次

浏览器安全的基石是同源政策(same-origin policy)。

一、概述

1.1含义和目的

1995年,同源政策由Netscape公司引入浏览器。目前,所有浏览器都实行这个政策。

最初的含义是指,A网页设置的cookie,B网页不能打开,除非这两个网页同源

同源指的是“三个相同”

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

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

如果cookie包含隐私,这些信息就会泄漏。

cookie往往用来保存用户的登录状态,如果用户没有退出登陆,其他网站就可以冒充用户。

因为浏览器同时还规定,提交表单不受同源政策的限制。

“同源政策”是必须的,否则cookie可以共享,互联网就毫无安全可言了。

1.2限制范围

目前,如果非同源,共有三种行为受到限制

  • Cookie、LocalStorage 和 IndexDB 无法读取。
  • DOM无法获得
  • AJAX请求不能发送

二、同源规避方法

同源政策规定,AJAX请求只能发给同源的网址,否则就报错

除了架设服务器代理(浏览器请求同源服务器,再由后者请求外部服务),由三种方法规避这个限制

  • JSONP
  • WebSocket
  • CORS

2.1 JSONP

可跨域HTML元素

用form可以发送post请求,但是会刷新页面或新开页面。可跨域但拿不到响应

用a可以发GET请求,但是也会刷新页面或新开页面。可跨域

用img可以发GET请求,不用放在body标签里,但是只能以css、favicon的形式展示。可跨域

button.addEventListener('click', (e)=>{
  let image = document.createElement('img')
  image.src = '/pay'
  image.onload = function(){
    alert('成功')
    }
  image.onerror = function(){ // 状态码大于等于 400 则表示失败
        alert('失败')
  }
})

用script可以发GET请求,要放在body标签里但是只能以脚本的形式运行,完成后清楚script标签,可跨越

button.addEventListener('click', (e)=>{
    let script = document.createElement('script')
    script.src = '/pay'
    document.body.appendChild(script)
    script.onload = function(e){ // 状态码是 200~299 则表示成功
        e.currentTarget.remove()
    }
    script.onerror = function(e){ // 状态码大于等于 400 则表示失败
        e.currentTarget.remove()
    }
    
    //后端代码
...
if (path === '/pay'){
    let amount = fs.readFileSync('./db', 'utf8')
    amount -= 1
    fs.writeFileSync('./db', amount)
    response.setHeader('Content-Type', 'application/javascript')
    response.write('amount.innerText = ' + amount)
    response.end()
}
...

JSONP是服务器与客服端跨源通信的常用方法,最大特点就是简单实用,老式浏览器全部支持,服务器改造非常小。

JSOP原理(发源自SRJ-Server Rendered JavaScript)

网页通过添加一个<script>元素,向服务器请求JSON数据

这种做法不受同源政策限制,服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。

  1. 动态添加<script>元素。

  2. 向服务器发送请求,该请求的查询字符串有一个callback参数,用来制定回调函数的名字,这对于JSONP是必须的

    window.onload = function () {
      addScriptTag('http://example.com/ip?callback=foo');
    }
    
  3. 服务器收到这个请求后,会将数据放在回调函数的参数位置返回。

foo.call(undefined,{
  "ip": "8.8.8.8"
});
  1. <script>元素请求的脚本直接作为代码运行,只要浏览器定义了foo函数,该函数就会立即调用。作为参数的JSON数据被视为JavaScript对象,而不是字符串。避免了JSON.parse的使用

JSON+padding = JSONP

JSONP的具体实现

请求方:frank.com的前端页面(浏览器)

响应方:jack,com的后端(服务器)

  1. 请求方动态创建script标签,src指向响应方,同时传一个一查询参数?callback=xxx

  2. 响应方根据请求方的查询参数?callback=xxx,定制callback函数的名字,这对JSONP是必须的

  • xxx.call('undefined', {请求的数据})

  • xxx.({请求的数据})数据会放在回调函数的参数位置返回

响应方构造如上响应。

  1. 浏览器接收到响应后,就会执行xxx.call('undefined','请求的数据')

  2. 请求方就获得了想要请求的数据。

注意:后台都是返回一个符合对象规则的字符串,后台是没有办法返回一个对象给前端的。

问题:为什么JSONP不支持POST?

  1. 因为JSONP是通过动态创建script标签实现对跨源的跨源的服务器进行通信的。
  2. 动态创建的script标签只能用GET,不支持POST。

JSONP的具体实现

button.addEventListener('click',(e)=>{
  let script = document.createElement('script')
  let fucntionName = 'dong' + parseInt(Math.random()*100000,10) // 使用随机数做函数名避免污染全局变量
  window[functionName] = function (result){
    if(result === 'success'){
    amount.innerText = amount.innerText - 1//string转化为number
    }
  }
  script.src = '/pay?callback=' + functionName
  document.body.appendChlid(script)
  script.onload = function(e){ // 状态码是 200~299 则表示成功
        e.currentTarget.remove()
        delete window[functionName] 
    }
  script.onerror = function(e){ // 状态码大于400表示失败
        e.currentTarget.remove()
        delete window[functionName] // 请求完了就干掉这个随机函数
    }
})

//后端代码
if(path === '/pay'){r
  fs.writeFlieSync('./db','utf8')
  let callbackName = query.callback
  response.setHeader('Content-type','application/javascript')
  response.write(`${callbackName}.call(undefined,’success‘)
  response.end()
}

使用jQuery实现JSONP

$.ajax({//和ajax无关系只是JSONP
  url: "http://dong.com:80/pay",
  dataType: "jsonp",
  success: function(res) {
    if (response === "success"){
      amount.innerText = amount.innerText - 1
    }
  }  
})

2.2 WebSocket

websocket是一种通信协议,实用ws:// (非加密) wss:// (加密)作为协议前缀

该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信

2.3 CORS

CORS是跨源资源分享(Cross-Origin Resource Sharing)的缩写。它是W3C标准,是跨源AJAX请求的根本解决方法。相比JSONP只能发GET请求,CORS允许任何类型的请求。

aButton.addEventListener('click',(e)=> { //dongdong前端
  let request = new XMLHttpRequest
  request.open('get',   'http://dongdong.com:8001')//配置。open(method, url, asyns, user, password)后3个参数可选
  request.onreadystatechange = () => {
    if(request.readyState === 4){
      if(request.status >=200 && request.status < 300){
        let string = request.responseText
        // 把符合JSON语法的字符串转换为JavaScript对应的值
        let object = window.JSON.parse(string) 
        console.log(object.note)
      }else if(request.status >= 400){
        console.log('请求失败')
      }
    }
  }
  request.send()
})

// 后端代码 jack
}else if(path === '/xxx'){
  response.statusCode = 200
    response.setHeader('Content-Type', 'text/json;charset=utf-8')
    response.setHeader('Access-Control-Allow-Origin', 'http://dongdong.com:8001')//CORS
    response.setHeader('Access-Control-Allow-Origin', 'http://123456.com:8001')//CORS 允许多个域名跨源访问服务器。
    response.wtite(`{
      "note": {
        "to": "dongdong"
        "from": "小瓜太郎"
        "content": "hi"  
      }
    }`)
    response.end()
}

三、AJAX

AJAX = async + javascript + and + xml(现在为JSON)

在AJAX出现以前(SRJ以前只能通过特定的HTML标签发起有限制的HTTP求情)

用form可以发送post请求,但是会刷新页面或新开页面。

用a可以发GET请求,但是也会刷新页面或新开页面。

用img可以发GET请求,不用放在body标签里,但是只能以css、favicon的形式展示。

用script可以发GET请求,要放在body标签里但是只能以脚本的形式运行,完成后清楚script标签

有什么方式可以实现以下要求:

  1. GET 、POST、PUT、DELETE请求都行。
  2. 想以什么形式展示就以什么形式展示。

微软的突破

以前只能通过特定的HTML标签发起有限制的HTTP求情

IE5开始在js中引入activeX对象(API),让JS可以直接发起HTTP请求

随后Mozilla、Safari、Opera跟进,取名XMLHttpRequest纳入W3C规范。

AJAX

  1. 使用XMLHttpRequest发送请求
  2. 服务器返回XML格式的字符串
  3. JS解析XML,并更新局部页面

使用XMLHttpRequest

aButton.addEventListener('click',(e)=> {
  let request = new XMLHttpRequest
  request.open('get','/xxx')//配置。open(method, url, asyns, user, password)后3个参数可选
  request.onreadystatechange = () => {
    if(request.readyState === 4){ //0unset 1opened 2headers_received(send) 3loading 4done 
      if(request.status >=200 && request.status < 300){
        console.log('请求成功')
        console.log(typeof request.responseText)
        console.log(request.responseText)
        let string = request.responseText
        // 把符合JSON语法的字符串转换为JavaScript对应的值
        let object = window.JSON.parse(string)
        console.log(typeof object)
        console.log(object)
        console.log(object.note)
      }else if(request.status >= 400){
        console.log('请求失败')
      }
    }
  }
  request.send()
})

// 后端代码
}else if(path === '/xxx'){
  response.statusCode = 200
    response.setHeader('Content-Type', 'text/json;charset=utf-8')
    response.setHeader('Access-Control-Allow-Origin', 'http://frank.com:8001')
    response.wtite(`{
      "note": {
        "to": "dongdong"
        "from": "小瓜太郎"
        "content": "hi"  
      }
    }`)
    response.end()
}

四、封装AJAX、Promise

AJAX的所有功能

  • 客户端的JS发起请求(浏览器上的)
  • 服务器端的JS发送响应(Node.js上的
  1. JS可以设置任意请求header

第一部分 request.open('get','/xxx')

第二部分 request.setRequestHeader('Content-Type','x-www-form-urlencoded')

第四部分 request.send('a=1&b=2')//body)

  1. JS可以获取来自服务器端的任意响应header

第一部分 request.status/request.statusText//200/OK

第二部分 request.getResponseHeader()/request.getAllResponseHeaders

第四部分 request.reponseText

window.jQuery = function(nodeOrSelector){
  let nodes = {}
  nodes.addClass = function(){}
  nodes.html = function(){}
  return nodes
}
window.$ = window.jQuery

window.Promise = function(fn){ //Promise的简单原理
  // ...
  return {
    then: function(){}
  }
}

window.jQuery.ajax = function({url,method,body,headers}){// es6解构赋值
  return new Promise(function(resolve,reject){
    let request = new XMLHttpRequest()
    request.open(method,url)
    for(let key in headers){
      let value = headers[key]
      request.setRequestHeader(key,value)
    }
    request.onreadystatechange = (e) => {
      if(request.readyState === 4){
        if(request.status >= 200 && request.status < 300){
          resolve.call(undefined,request.responseText)
        }else if (request.status >= 400){
          reject.call(nudefined,request)
        }
      }
    }
    request.send(body) //若是POST
  })
}

aButton.addEventListener('click', (e) => {
  let promise1 = window.jQuery.ajax({
    url:'/xxx',
    method:'get',
    headers: {
      'content-type':'application/x-www-form-urlencoded',
      'frank': '18'
    }
  })

  promise1.then( //在不知道回调函数名字的情况下,拿到绑定的参数
    (text) => {console.log(text)},
    (request) => {console.log(request)}
  )  
})
// callback call a function back

回调函数

callback = call a function back

回调的问题

每个程序员的回调名不一样。

Promise解决了这个问题

function xxx(){
    return new Promise((f1, f2) => {
        doSomething()
        setTimeout(()=>{
            // 成功就调用 f1,失败就调用 f2
        },3000)
    })
}

xxx().then(success, fail)

// 链式操作
xxx().then(success, fail).then(success, fail)

??? 关于then return

相关文章

  • 同源政策于跨源通信

    浏览器安全的基石是同源政策(same-origin policy)。 一、概述 1.1含义和目的 1995年,同源...

  • 前端跨页面通信

    跨页面通信主要分两大类 同源页面间的跨页面通信 非同源页面间的跨页面通信 同源页面间的跨页面通信 1.BroadC...

  • 同源跨页面通信

    同源跨页面通信小记 BroadcastChannel The BroadcastChannel interface...

  • 有关跨域的相关问题和方法

    跨域是什么 同源策略 在讲解什么是跨域之前先要清楚什么是同源策略,“同源政策”(same-origin polic...

  • JS中同源政策及AJAX跨域

    一、同源政策  浏览器的同源政策限制了从一个源加载另外一个源的资源,它是浏览器的安全基石,所谓同源是指: 协议相同...

  • 前后端通信

    浏览器的同源政策限制:端口,域名,协议 ,只要一个不一样就跨域 前后端如何通信: Ajax : 短连接 Web...

  • 同源与跨域(一)

    参考:浏览器的同源策略浏览器同源政策及其规避方法同源政策 什么是同源策略? 同源策略限制了从同一个源加载的文档或脚...

  • 前后端通信

    1、浏览器的同源政策限制:端口,域名,协议 ,只要一个不一样就跨域 2、前后端通信的方式 ajax Websock...

  • 前端跨tab页通信

    一、同源页面间的跨页面通信 BroadCast Channel Service WorkerService Wor...

  • JS跨域

    什么是跨域 网络间的通信有同源策略,从一个源加载的脚本或文件如何和另外一个源的脚本进行交互。这是用于隔离潜在恶意文...

网友评论

      本文标题:同源政策于跨源通信

      本文链接:https://www.haomeiwen.com/subject/pnwwdctx.html