前端基础重点回顾4:前后端通信

同源策略及限制

同源策略的概念

  • 同源:http协议,域名, 端口三者均相同
  • 同源策略是用来限制在一个源上加载的文档或脚本与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制

同源策略的限制

  • cookie localStorage indexDB 无法读取
  • dom 无法获得 ajax请求不能发送

前后端通信的常见几种方式

  • Ajax(同源通信)
  • WebSocket(协议不同的不同源通信)
  • CORS(用于支持不同源之间ajax通信的方法)

Ajax通信

参考

Ajax 概念

  • Ajax(Async JavaScript And XML)是一种依赖CSS/HTML/JAVASCRIPT 等现有技术使用XMLHttpRequest
    对象发送http 请求并接受响应的一种技术方案

实现一个Ajax

/**
 * {string} param.url
 * {string} param.type? || 'get'
 * {object} param.data
 * {function} param.success
 * {function} param.error
 */
var ajax = function(param) {
    var xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")
    var type = (param.type ||  'get').toUpperCase()
    var url = param.url
    if(!url) return
    var data = param.data
    var dataArr = []
    for (var k in data) {
      dataArr.push(k + '=' + data[k])
    }

    if (type === 'GET') {
      url = url + '?' + dataArr.join('&')
      xhr.open(type, url)
      xhr.send()
    }

    if (type === 'POST') {
      xhr.open(type, url)
      xhr.setRequestHeader("Content-type", "application/x-www.form-urlencoded")
      xhr.send(dataArr.join('&'))
    }
    
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
        if (xhr.status === 200 || xhr.status === 304) {
          var res
          if (param.success && typeof param.success === 'function') {
            res = xhr.responseText
            if (typeof res === 'string') {
              res = JSON.parse(res)
              param.success.call(xhr, res)
            }
          }
        } else {
          param.error.call()
        }
      }
    }
}

跨域通信的几种方式

  • 首先我们先给host 设置几个子域名来模拟跨域


跨域代码示例

$ npm install
$ npm start
port: 3000 
使用示例前记得设置本机host

JSONP

jsonp 原理就是在页面上动态添加一个script标签,给标签的src 指定一个url 路径并加上回调函数query 参数,发送给后端后,后端利用需返回的数据和回调函数的query 参数拼接成类似handleJsonp({ a:1, b:2 })的字符串返回前端,前端定义的handleJsonp 的函数会直接运行并处理{ a:1, b:2 } 这个后端返回的数据

  • 只能发送GET请求
  • 可能会被注入恶意代码 callback=alter('111')
  • 任何域都可以发送jsonp请求,需进行验证,如token
// 前端代码
      jsonpBtn.addEventListener('click', function() {
        const script = document.createElement('script')
        script.src = 'http://b.yang.com:3000/jsonp?callback=handleJsonp'
        document.head.appendChild(script)
        // document.head.removeChild(script)
      })

      function handleJsonp(data) {
        console.log(data)
      }
// 后端代码
// JSONP
router.get('/jsonp', function(req, res, next) {
  let { callback: cb } = req.query
  const data = {
    type: 'jsonp',
    data: 'data'
  }
  cb = cb.replace(/\(/g, ''); // 替换掉() 防止恶意代码注入
  cb = cb.replace(/\)/g, '');
  res.send(cb + '(' + JSON.stringify(data) + ')')
})

CORS

  • CORS(cross-origin resource sharing) 跨域资源共享,是一种ajax 跨域请求资源的方式, 普遍用于前后端分离开发环境
  • 原理就在于Access-Control-Allow-Origin 响应头,它指定浏览器在何种域下发送的ajax 请求服务器资源时可以跨域
  • 服务器响应还可以设置其它header:
    1Access-Control-Allow-Methods: POST, GET, OPTIONS表明服务器允许客户端使用 POST, GET 和 OPTIONS 方法发起请求
    2Access-Control-Allow-Headers: X-PINGOTHER, Content-Type表明服务器允许请求中携带字段 X-PINGOTHER 与 Content-Type
    3Access-Control-Max-Age: 86400表明该响应的有效时间为 86400 秒
    4Access-Control-Allow-Credentials: true 表明跨域请求允许携带cookie
    MDN
// 前端代码
     cors.addEventListener('click', function() {
        let reqHeaders = new Headers()
        reqHeaders.append('Content-Type', 'application/x-www-form-urlencoded')
        fetch('http://b.yang.com:3000/cors/', {
          method: 'post',
          headers: reqHeaders,
          mode: 'cors',
          body: 'post body'
        }).then(function (response) {
          console.log(response)
        })
      })
// 后端代码
// CORS
router.post('/cors', function(req, res, next) {
  // res.header('Access-Control-Allow-Origin', 'http://a.yang.com:3000')
  res.header('Access-Control-Allow-Origin', '*')
  res.send('cors ok')
})

WebSocket

利用websocket 协议进行前后端跨域通信

// 前端代码
      var ws
      socket.addEventListener('click', function() {
        ws = new WebSocket(`ws://b.yang.com:3000/`)
        ws.onmessage = (data) => console.log(data);
        ws.onerror = () => console.log('WebSocket error');
        ws.onopen = () => console.log('WebSocket connection established');
        ws.onclose = () => console.log('WebSocket connection closed');
      })
      sendmsg.addEventListener('click', function() {
        ws.send('send a msg')
      })
// 后端代码
var express = require('express');
var app = express();
const WebSocket = require('ws')
var server = http.createServer(app);

const wss = new WebSocket.Server({ server })
wss.on('connection', (ws, req) => {
  ws.on('message', message => {
    console.log(message)
    ws.send(message)
  })
})
server.listen(3000)

降域(使用iframe)

// URL: http://a.yang.com:3000/a
<div class="ct">
  <h1>使用降域实现跨域</h1>
  <div class="main">
    <h4>URL: http://a.yang.com:3000/a</h4>
    <input type="text" placeholder="http://a.yang.com:3000/a">
  </div>
  <iframe src="http://b.yang.com:3000/b" frameborder="0" ></iframe>
</div>

<script>
  document.querySelector('.main input').addEventListener('input', function(){
    console.log(location.host, this.value);
    window.frames[0].document.querySelector('input').value = this.value;
  })
  document.domain = "yang.com"
</script>
// URL: http://b.yang.com:3000/b
<input id="input" type="text"  placeholder="http://b.yang.com:3000/b">
<script>
  document.querySelector('#input').addEventListener('input', function(){
    console.log(location.host, this.value);
    window.parent.document.querySelector('input').value = this.value;
  })
  document.domain = 'yang.com';
</script>

postMessage(使用iframe)

//URL: http://a.yang.com:3000/c
<div class="ct">
  <h1>使用postMessage实现跨域</h1>
  <div class="main">
    <h4>URL: http://a.yang.com:3000/c</h4>
    <input type="text" placeholder="http://a.yang.com:3000/c">
  </div>
  <iframe src="http://localhost:3000/d" frameborder="0" ></iframe>
</div>

<script>
  var input = document.querySelector('.main input')
  input.addEventListener('input', function(){
    console.log('a.yang.com - input event value', this.value);
    window.frames[0].postMessage(this.value,'*');
  })
  window.addEventListener('message',function(e) {
    input.value = e.data
    console.log('a.yang.com - message event value', e.data);
  });
</script>
// URL: http://b.yang.com:3000/d
<input id="input" type="text"  placeholder="http://b.yang.com:3000/d">
<script>
  var input = document.querySelector('#input')
  input.addEventListener('input', function(){
    console.log('b.yang.com - input event value', this.value);
    window.parent.postMessage(this.value, '*');
  })
  window.addEventListener('message',function(e) {
    input.value = e.data
    console.log('b.yang.com - message event value', e.data);
  });

</script>

其他hack

改变hash值

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270