搞懂:前端跨域問題JS解決跨域問題VUE代理解決跨域問題原理

什麼是跨域

跨域一個域下的文檔或腳本試圖去請求另外一個域下的資源javascript

廣義的跨域包含一下內容html

1.資源跳轉(連接跳轉,重定向跳轉,表單提交)
2.資源請求(內部的引用,腳本script,圖片img,frame)
3.script內部發起的請求(ajax,dom請求,和js跨域調用

跨域問題出現前端

只有瀏覽器端出現,直接用終端請求,是不會出現跨域攔截,是屬於瀏覽器端的安全策略,瀏覽器將不一樣源的請求進行了攔截,限制了跨域資源訪問

什麼是同源vue

同源策略:same origin policy,若是兩個資源(頁面)`協議`,`域名`,`端口`都相同,那麼就是同源。

即便:兩個不一樣域名指向同一個ip,也算非同源

非同源(跨域)有哪些限制java

* cookie,localstorage,indexDB沒法讀取
* Dom和JS對象沒法互通
* Ajax請求不能發送

常見的跨域demonode

URL                                      說明                    是否容許通訊
http://www.domain.com/a.js
http://www.domain.com/b.js         同一域名,不一樣文件或路徑           容許
http://www.domain.com/lab/c.js

http://www.domain.com:8000/a.js
http://www.domain.com/b.js         同一域名,不一樣端口                不容許
 
http://www.domain.com/a.js
https://www.domain.com/b.js        同一域名,不一樣協議                不容許
 
http://www.domain.com/a.js
http://192.168.4.12/b.js           域名和域名對應相同ip              不容許
 
http://www.domain.com/a.js
http://x.domain.com/b.js           主域相同,子域不一樣                不容許
http://domain.com/c.js
 
http://www.domain1.com/a.js
http://www.domain2.com/b.js        不一樣域名                         不容許

前端解決跨域的方法

  • JSONPwebpack

    • 原理:因爲在一個頁面內部js若是要訪問另外一個非同源域的數據是被瀏覽器限制的,可是瀏覽器在解析和加載script標籤的時候,是容許一個頁面加載多個域的js標籤,而js標籤其實又是相似一種get請求的方式,只是返回的數據是一個JSON格式的腳本對象。
    • 用法:將get請求封裝到script標籤中,利用script標籤作get請求,最後解析script標籤數據
    • 代碼:
    var  script = document.createElement('script')
      script.style = 'text/javascript'
    
      //將要請求的get地址傳到script的src屬性,而且添加回調函數
      script.setAttribute('scr','http://www.domain2.com:8080/login?user=admin&callback=handleCallback')
      //將script標籤放入文檔
      document.head.appendChild(script)
      //執行回調函數
      function handleCallback(res){
          //處理JSON格式數據
          alert(JSON.stringify(res))
      }
    • 因爲請求script只能是get方法,因此JSONP這種方式只能解決get請求,post或者其餘http方法沒法解決
  • 跨域資源共享ios

    • 原理:
      • cors:cross-origin -resouce sharing跨域資源共享,容許瀏覽器向跨源服務器發出http請求,須要瀏覽器和服務器同時支持
      • 瀏覽器在發出跨域請求時:1。簡單請求,直接在頭部信息中帶上origin信息,標識屬於哪一個源,服務端配置access-control-allow-origin接受哪些域名跨域訪問,能夠是*容許全部
      • 若是是非簡單請求會在正式請求以前,發送預檢請求
    • 簡單來講(簡單請求):
      • 是一個雙端配合,容許打卡跨域資源限制的一種手段
      • 瀏覽器端:請求頭攜帶origin信息
      • 服務端:配置access-control-allow-origin容許哪些源跨域
    • 代碼:
      • 原生:xmlHttpRequest不須要配置請求頭,簡單請求會自動帶上origin屬性
        var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容
        
        
            xhr.open('post', 'http://www.domain2.com:8080/login', true);
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            xhr.send('user=admin');
        
            xhr.onreadystatechange = function() {
                if (xhr.readyState == 4 && xhr.status == 200) {
                    alert(xhr.responseText);
                }
            };
      • AJAX:有一個屬性crossDomain,配置爲true以後會讓請求頭攜帶orgin跨域額外信息,可是不會自動包含cookie
        $.ajax({
            ...
        xhrFields: {
            withCredentials: true    // 前端設置是否帶cookie
        },
        crossDomain: true,   // 會讓請求頭中包含跨域的額外信息,但不會含cookie
            ...
        });
      • VUE中的axios,默認也會把origin放到請求頭,不須要額外配置
    • 服務端配置
      /*
         * 導入包:import javax.servlet.http.HttpServletResponse;
         * 接口參數中定義:HttpServletResponse response
         */
      
        // 容許跨域訪問的域名:如有端口需寫全(協議+域名+端口),若沒有端口末尾不用加'/'
        response.setHeader("Access-Control-Allow-Origin", "http://www.domain1.com"); 
      
        // 容許前端帶認證cookie:啓用此項後,上面的域名不能爲'*',必須指定具體的域名,不然瀏覽器會提示
        response.setHeader("Access-Control-Allow-Credentials", "true"); 
      
        // 提示OPTIONS預檢時,後端須要設置的兩個經常使用自定義頭
        response.setHeader("Access-Control-Allow-Headers", "Content-Type,X-Requested-With");
  • 反向代理(NodeJs中間件代理跨域)(Vue代理跨域)(nginx轉發代理)nginx

    • 場景:當服務器端沒法修改cors,而且client所有換成jsonp方式比較繁瑣時,只能經過中間代理服務器,將代理服務器設置爲支持CORS或者代理服務器和請求頁面同源,使頁面能夠直接請求代理服務器,在經過代理服務器進行接口代理,代理請求目標接口地址,返回數據web

    • 原理:跨域限制僅是瀏覽器端的安全策略,並非http協議的固有限制,因此中間服務器在參數和cookie有效的狀況下是能夠正常的請求目標服務器的,Vue(node+webpack+webpack-dev-server)中配置proxy就是啓動一個同源的服務進行接口代理

    • 注意:

      • 1.非vue ,webpack,dev-serve服務,單獨啓的nginx或者node express服務作反向代理時,通常是跟頁面非同源,非同源要訪問代理服務器也存在跨域問題,須要配置cors容許跨域訪問,配置access-control-allow-header
      • 在webpack-dev-server服務,默認應該是基本與測試環境頁面同源,不須要配置請求頭容許
    • 代碼:

      • webpack-dev-server代理
      module.exports = {
              entry: {},
              module: {},
              ...
              devServer: {
                  historyApiFallback: true,
                  proxy: [{
                      context: '/login',
                      target: 'http://www.domain2.com:8080',  // 代理跨域目標接口
                      changeOrigin: true,
                      secure: false,  // 當代理某些https服務報錯時用
                      cookieDomainRewrite: 'www.domain1.com'  // 能夠爲false,表示不修改
                  }],
                  noInfo: true
              }
          }
      • node express反向代理
      var express = require('express');
      var proxy = require('http-proxy-middleware');
      var app = express();
      
      app.use('/', proxy({
          // 代理跨域目標接口
          target: 'http://www.domain2.com:8080',
          changeOrigin: true,
      
          // 修改響應頭信息,實現跨域並容許帶cookie
          onProxyRes: function(proxyRes, req, res) {
              res.header('Access-Control-Allow-Origin', 'http://www.domain1.com');
              res.header('Access-Control-Allow-Credentials', 'true');
          },
      
          // 修改響應信息中的cookie域名
          cookieDomainRewrite: 'www.domain1.com'  // 能夠爲false,表示不修改
      }));
      
      app.listen(3000);
      console.log('Proxy server is listen at port 3000...');
      • nginx轉發代理
      #proxy服務器
          server {
              listen       81;
              server_name  www.domain1.com;
      
              location / {
                  proxy_pass   http://www.domain2.com:8080;  #反向代理
                  proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie裏域名
                  index  index.html index.htm;
      
                  # 當用webpack-dev-server等中間件代理接口訪問nignx時,此時無瀏覽器參與,故沒有同源限制,下面的跨域配置可不啓用
                  add_header Access-Control-Allow-Origin http://www.domain1.com;  #當前端只跨域不帶cookie時,可爲*
                  add_header Access-Control-Allow-Credentials true;
              }
          }
  • 其餘js HACK方法

    • 主域相同子域不一樣場景:docment.domain+iframe

    • 三個頁面跨域互傳 : location.hash + iframe

    • window.name + iframe

    • H5新增的postMessage(跨窗口消息互通方法)

    • 關於iframe:

      • 其實至關於在當前頁面下新開了個子頁面。遵循一切頁面與頁面之間資源訪問原則,例如
        • 1.當主頁面和iframe頁面不一樣源時,沒法互相訪問DOM
        • 2.當主域同樣,二級域名不一樣時,document.domain的設置,能夠規避同源策略,能夠互相訪問DOM
    • location.hash:

      • url 末尾#後面跟隨的時頁面片斷標識符,用來表示瀏覽器渲染哪部分頁面信息,可是改變這個值頁面不會從新刷新, 而且設置以後,全部頁面能夠經過document對象訪問該窗口的location 信息進行獲取,能夠實現巧妙跨域
      • 代碼:
      //父窗口能夠把信息,寫入子窗口的片斷標識符。
      
          var src = originURL + ‘#’ + data;
          document.getElementById(‘myIFrame’).src = src;
      
          //子窗口經過監聽hashchange事件獲得通知。
      
          window.onhashchange = checkMessage;
      
          function checkMessage() {
          var message = window.location.hash;
          // …
          }
    • 同理,window.name也是一種hack方法實現跨域的方式

      • 瀏覽器窗口有window.name屬性。這個屬性的最大特色是,不管是否同源,只要在同一個窗口裏,前一個網頁設置了這個屬性,後一個網頁能夠讀取它。

        父窗口先打開一個子窗口,載入一個不一樣源的網頁,該網頁將信息寫入window.name屬性。

        window.name = data;

        接着,子窗口跳回一個與主窗口同域的網址。

        location = ‘http://parent.url.com/xxx.html’;

        而後,主窗口就能夠讀取子窗口的window.name了。

        var data = document.getElementById(‘myFrame’).contentWindow.name;

        這種方法的優勢是,window.name容量很大,能夠放置很是長的字符串;缺點是必須監聽子窗口window.name屬性的變化,影響網頁性能。

    • postMessage

      • window.name和location.hash都屬於取巧的方式,利用了瀏覽器頁面的非數據屬性或者是窗口的固有屬性達到跨頁面的目的,可是在H5新增了postMessage API,實現跨tab跨窗口的數據通訊,容許窗口通訊,而且不管是否同源

總結:

經常使用的,仍是代理,還有CORS,或者是JSONP這三種方式,禁止跨域原本就是瀏覽器的一種安全策略,雖然有必定限制開發者的手腳,可是在一些特殊的網站或者安全性要求比較高的網站,網絡安全仍是不可忽視的

很是感謝:下面的文章給了我不少的幫助,感謝各位前行者的辛苦付出,能夠點擊查閱更多信息

前端常看法決跨域問題方案

9種常見的跨域解決方案

前端跨域問題

相關文章
相關標籤/搜索