前端基礎重點回顧4:前后端通信

同源策略及限制

同源策略的概念

  • 同源:http協(xié)議,域名, 端口三者均相同
  • 同源策略是用來限制在一個源上加載的文檔或腳本與來自另一個源的資源進行交互。這是一個用于隔離潛在惡意文件的關鍵的安全機制

同源策略的限制

  • cookie localStorage indexDB 無法讀取
  • dom 無法獲得 ajax請求不能發(fā)送

前后端通信的常見幾種方式

  • Ajax(同源通信)
  • WebSocket(協(xié)議不同的不同源通信)
  • CORS(用于支持不同源之間ajax通信的方法)

Ajax通信

參考

Ajax 概念

  • Ajax(Async JavaScript And XML)是一種依賴CSS/HTML/JAVASCRIPT 等現(xiàn)有技術使用XMLHttpRequest
    對象發(fā)送http 請求并接受響應的一種技術方案

實現(xiàn)一個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 原理就是在頁面上動態(tài)添加一個script標簽,給標簽的src 指定一個url 路徑并加上回調函數(shù)query 參數(shù),發(fā)送給后端后,后端利用需返回的數(shù)據(jù)和回調函數(shù)的query 參數(shù)拼接成類似handleJsonp({ a:1, b:2 })的字符串返回前端,前端定義的handleJsonp 的函數(shù)會直接運行并處理{ a:1, b:2 } 這個后端返回的數(shù)據(jù)

  • 只能發(fā)送GET請求
  • 可能會被注入惡意代碼 callback=alter('111')
  • 任何域都可以發(fā)送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 跨域請求資源的方式, 普遍用于前后端分離開發(fā)環(huán)境
  • 原理就在于Access-Control-Allow-Origin 響應頭,它指定瀏覽器在何種域下發(fā)送的ajax 請求服務器資源時可以跨域
  • 服務器響應還可以設置其它header:
    1Access-Control-Allow-Methods: POST, GET, OPTIONS表明服務器允許客戶端使用 POST, GET 和 OPTIONS 方法發(fā)起請求
    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 協(xié)議進行前后端跨域通信

// 前端代碼
      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>使用降域實現(xiàn)跨域</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實現(xiàn)跨域</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值

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容