10 種跨域解決方案(附終極方案)

寫在前面

嗯。又來了,又說到跨域了,這是一個老生常談的話題,之前我以爲這種基礎文章沒有什麼好寫的,會想着你去了解底層啊,不是很簡單嗎。可是最近在開發一個 「vscode 插件」 發現,當你剛入門同樣東西的時候,你不會想這麼多,由於你對他不熟悉,當你遇到不會的東西,你就是想先找到解決方案,而後經過這個解決方案再去深刻理解。就好比跨域,新人或者剛接觸的人對它並非那麼熟悉,因此說列出一些本身積累的方案,以及一些經常使用的場景來給他人帶來一些解決問題的思路,這件事是有意義的。(寫完以後還發現真香。之後忘了還能回來看看)javascript

其實如今的環境對於剛入門的前端來講,很是的不友好,一方面吧,不少剛新人沒有經歷過工具的變動時代,另外一方面框架的迭代更新速度很快。在之前你可能掌握幾種常見的手法就行了。可是如今 webpack-dev-servervue-cliparcel,這些腳手架都進行了一層封裝,對於熟悉的人可能很簡單,可是對於還未入門的人來講,簡直就是一個黑盒,雖然用起來很方便,可是某一天遇到了問題,你對它不熟悉,你就會不知道所錯。(可是別慌,主流 cli 的跨域方式我都會講到)css

講着講着有點偏離方向,可能我講的也並不必定是正確的。下面切入正題。html

本文會以 「「What-How-Why」」 的方式來進行講解。而在在 How (如何解決跨域,將會提供標題的 11 種方案。)前端

「重要的說明: 在文中,web 端地址爲 localhost:8000 服務端地址爲 localhost:8080,這一點但願你能記住,會貫穿全文,你也能夠把此處的兩端的地址代入你本身的地址。」vue

cors

如下全部代碼均在 https://github.com/hua1995116/node-demo/tree/master/node-corshtml5

image-20200413192431636

1、跨域是什麼?

1.同源策略

跨域問題其實就是瀏覽器的同源策略所致使的。java

「同源策略」是一個重要的安全策略,它用於限制一個origin的文檔或者它加載的腳本如何能與另外一個源的資源進行交互。它能幫助阻隔惡意文檔,減小可能被攻擊的媒介。node

--來源 MDNjquery

當跨域時會收到如下錯誤webpack

image-20200413205559124

2.同源示例

那麼如何纔算是同源呢?先來看看 url 的組成部分

http://www.example.com:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument

image-20200412171942421

只有當

「protocol(協議)、domain(域名)、port(端口)三者一致。」

「protocol(協議)、domain(域名)、port(端口)三者一致。」

「protocol(協議)、domain(域名)、port(端口)三者一致。」

纔是同源。

如下協議、域名、端口一致。

http://www.example.com:80/a.js

http://www.example.com:80/b.js

如下這種看上去再類似也沒有用,都不是同源。

http://www.example.com:8080

http://www2.example.com:80

在這裏注意一下啊,這裏是爲了突出端口的區別才寫上端口。在默認狀況下 http 能夠省略端口 80, https 省略 443。這別到時候鬧笑話了,你和我說 http://www.example.com:80 和 http://www.example.com 不是同源,他倆是一個東西。

http://www.example.com:80 === http://www.example.com

https://www.example.com:443 === https://www.example.com

唔,仍是要說明一下。

2、如何解決跨域?

1.CORS

跨域資源共享(CORS) 是一種機制,它使用額外的 HTTP 頭來告訴瀏覽器 讓運行在一個 origin (domain) 上的 Web 應用被准許訪問來自不一樣源服務器上的指定的資源。當一個資源從與該資源自己所在的服務器「不一樣的域、協議或端口」請求一個資源時,資源會發起一個「跨域 HTTP 請求」

而在 cors 中會有 簡單請求複雜請求的概念。

「瀏覽器支持狀況」

當你使用 IE<=9, Opera<12, or Firefox<3.5 或者更加老的瀏覽器,這個時候請使用 JSONP 。

a.簡單請求

不會觸發 CORS 預檢請求。這樣的請求爲「簡單請求」,請注意,該術語並不屬於 Fetch (其中定義了 CORS)規範。若請求知足全部下述條件,則該請求可視爲「簡單請求」:

狀況一: 使用如下方法(意思就是如下請求意外的都是非簡單請求)

  • GET
  • HEAD
  • POST

狀況二: 人爲設置如下集合外的請求頭

  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type (須要注意額外的限制)
  • DPR
  • Downlink
  • Save-Data
  • Viewport-Width
  • Width

狀況三:Content-Type的值僅限於下列三者之一:(例如 application/json 爲非簡單請求)

  • text/plain
  • multipart/form-data
  • application/x-www-form-urlencoded

狀況四:

請求中的任意XMLHttpRequestUpload 對象均沒有註冊任何事件監聽器;XMLHttpRequestUpload 對象可使用 XMLHttpRequest.upload 屬性訪問。

狀況五:

請求中沒有使用 ReadableStream 對象。

b.非簡單請求

除以上狀況外的。

c.Node 中的解決方案

原生方式

咱們來看下後端部分的解決方案。NodeCORS 的解決代碼.

app.use(async (ctx, next) => {
  ctx.set("Access-Control-Allow-Origin", ctx.headers.origin);
  ctx.set("Access-Control-Allow-Credentials"true);
  ctx.set("Access-Control-Request-Method""PUT,POST,GET,DELETE,OPTIONS");
  ctx.set(
    "Access-Control-Allow-Headers",
    "Origin, X-Requested-With, Content-Type, Accept, cc"
  );
  if (ctx.method === "OPTIONS") {
    ctx.status = 204;
    return;
  }
  await next();
});
第三方中間件

爲了方便也能夠直接使用中間件

const cors = require("koa-cors");

app.use(cors());
關於 cors 的 cookie 問題

想要傳遞 cookie 須要知足 3 個條件

1.web 請求設置withCredentials

這裏默認狀況下在跨域請求,瀏覽器是不帶 cookie 的。可是咱們能夠經過設置 withCredentials 來進行傳遞 cookie.

// 原生 xml 的設置方式
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
// axios 設置方式
axios.defaults.withCredentials = true;

2.Access-Control-Allow-Credentialstrue

3.Access-Control-Allow-Origin爲非 *

這裏請求的方式,在 chrome 中是能看到返回值的,可是隻要不知足以上其一,瀏覽器會報錯,獲取不到返回值。

image-20200412201424024
Access to XMLHttpRequest at 'http://127.0.0.1:8080/api/corslist' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
image-20200412201411481
Access to XMLHttpRequest at 'http://127.0.0.1:8080/api/corslist' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
image-20200412201530087

d.前端示例

分別演示一下前端部分 簡單請求非簡單請求

簡單請求
<script src="https://cdn.bootcss.com/axios/0.19.2/axios.min.js"></script>
<script>
  axios.get("http://127.0.0.1:8080/api/corslist");
</script>
非簡單請求

這裏咱們加入了一個非集合內的 headercc 來達到非簡單請求的目的。

<script src="https://cdn.bootcss.com/axios/0.19.2/axios.min.js"></script>
<script>
  axios.get("http://127.0.0.1:8080/api/corslist", { header: { cc"xxx" } });
</script>
image-20200412201158778
image-20200412195829232
小結

一、 在新版的 chrome 中,若是你發送了複雜請求,你卻看不到 options 請求。能夠在這裏設置 chrome://flags/#out-of-blink-cors 設置成 disbale ,重啓瀏覽器。對於非簡單請求就能看到 options 請求了。

二、 通常狀況下後端接口是不會開啓這個跨域頭的,除非是一些與用戶無關的不過重要的接口。

2.Node 正向代理

代理的思路爲,利用服務端請求不會跨域的特性,讓接口和當前站點同域。

「代理前」

image-20200412202320482

「代理後」

image-20200412202358759

這樣,全部的資源以及請求都在一個域名下了。

a.cli 工具中的代理

1) Webpack (4.x)

webpack中能夠配置proxy來快速得到接口代理的能力。

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: {
    index"./index.js"
  },
  output: {
    filename"bundle.js",
    path: path.resolve(__dirname, "dist")
  },
  devServer: {
    port8000,
    proxy: {
      "/api": {
        target"http://localhost:8080"
      }
    }
  },
  plugins: [
    new HtmlWebpackPlugin({
      filename"index.html",
      template"webpack.html"
    })
  ]
};

修改前端接口請求方式,改成不帶域名。(由於如今是同域請求了)

<button id="getlist">獲取列表</button>
<button id="login">登陸</button>
<script src="https://cdn.bootcss.com/axios/0.19.2/axios.min.js"></script>
<script>
  axios.defaults.withCredentials = true;
  getlist.onclick = () => {
    axios.get("/api/corslist").then(res => {
      console.log(res.data);
    });
  };
  login.onclick = () => {
    axios.post("/api/login");
  };
</script>
2) Vue-cli 2.x
// config/index.js

...
proxyTable: {
  '/api': {
     target'http://localhost:8080',
  }
},
...
3) Vue-cli 3.x
// vue.config.js 若是沒有就新建
module.exports = {
  devServer: {
    port8000,
    proxy: {
      "/api": {
        target"http://localhost:8080"
      }
    }
  }
};
4) Parcel (2.x)
// .proxyrc
{
"/api": {
"target": "http://localhost:8080"
}
}

看到這裏,內心一句 xxx 就會脫口而出,一會寫配置文件,一會 proxyTable ,一會 proxy,怎麼這麼多的形式?學不動了學不動了。。。不過也不用慌,仍是有方法的。以上全部配置都是有着共同的底層包 http-proxy-middleware .裏面須要用到的各類 websocketrewrite 等功能,直接看這個庫的配置就能夠了。關於 http-proxy-middleware 這個庫的原理能夠看我這篇文章 https://github.com/hua1995116/proxy 固然了。。。對於配置的位置入口仍是非統一的。教一個搜索的技巧吧,上面配置寫哪裏都不用記的,想要哪一個框架的 直接 google 搜索 xxx proxy 就好了。

例如 vue-cli 2 proxy 、 webpack proxy 等等....基本會搜到有官網的配置答案,通用且 nice。

b.使用本身的代理工具

cors-anywhere

「服務端」

// Listen on a specific host via the HOST environment variable
var host = process.env.HOST || "0.0.0.0";
// Listen on a specific port via the PORT environment variable
var port = process.env.PORT || 7777;

var cors_proxy = require("cors-anywhere");
cors_proxy
  .createServer({
    originWhitelist: [], // Allow all origins
    requireHeader: ["origin""x-requested-with"],
    removeHeaders: ["cookie""cookie2"]
  })
  .listen(port, host, function({
    console.log("Running CORS Anywhere on " + host + ":" + port);
  });

「前端代碼」

<script src="https://cdn.bootcss.com/axios/0.19.2/axios.min.js"></script>
<script>
  axios.defaults.withCredentials = true;
  getlist.onclick = () => {
    axios
      .get("http://127.0.0.1:7777/http://127.0.0.1:8080/api/corslist")
      .then(res => {
        console.log(res.data);
      });
  };
  login.onclick = () => {
    axios.post("http://127.0.0.1:7777/http://127.0.0.1:8080/api/login");
  };
</script>

「效果展現」

image-20200413161243734

「缺點」

沒法專遞 cookie,緣由是由於這個是一個代理庫,做者以爲中間傳遞 cookie 太不安全了。https://github.com/Rob--W/cors-anywhere/issues/208#issuecomment-575254153

c.charles

介紹

這是一個測試、開發的神器。介紹與使用

利用 charles 進行跨域,本質就是請求的攔截與代理。

tools/map remote 中設置代理

image-20200412232733437
image-20200412232724518
前端代碼
<button id="getlist">獲取列表</button>
<button id="login">登陸</button>
<script src="https://cdn.bootcss.com/axios/0.19.2/axios.min.js"></script>
<script>
  axios.defaults.withCredentials = true;
  getlist.onclick = () => {
    axios.get("/api/corslist").then(res => {
      console.log(res.data);
    });
  };
  login.onclick = () => {
    axios.post("/api/login");
  };
</script>
後端代碼
router.get("/api/corslist"async ctx => {
  ctx.body = {
    data: [{ name"秋風的筆記" }]
  };
});

router.post("/api/login"async ctx => {
  ctx.cookies.set("token", token, {
    expiresnew Date(+new Date() + 1000 * 60 * 60 * 24 * 7)
  });
  ctx.body = {
    msg"成功",
    code0
  };
});
效果

訪問 http://localhost:8000/charles

image-20200412232231554
image-20200412232752837

完美解決。

唔。這裏又有一個注意點。在 Mac mojave 10.14 中會出現 charles 抓不到本地包的狀況。這個時候須要自定義一個域名,而後配置hosts指定到127.0.0.1。而後修改訪問方式 http://localhost.charlesproxy.com:8000/charles

image-20200412233258107
image-20200412233317027

3.Nginx 反向代理

介紹

Nginx 則是經過反向代理的方式,(這裏也須要自定義一個域名)這裏就是保證我當前域,能獲取到靜態資源和接口,不關心是怎麼獲取的。nginx 安裝教程

image-20200412233536522

配置下 hosts

127.0.0.1 local.test

配置 nginx

server {
listen 80;
server_name local.test;
location /api {
proxy_pass http://localhost:8080;
}
location / {
proxy_pass http://localhost:8000;
}
}

啓動 nginx

sudo nginx

重啓 nginx

sudo nginx -s reload

實現

前端代碼

<script>
  axios.defaults.withCredentials = true;
  getlist.onclick = () => {
    axios.get("/api/corslist").then(res => {
      console.log(res.data);
    });
  };
  login.onclick = () => {
    axios.post("/api/login");
  };
</script>

後端代碼

router.get("/api/corslist"async ctx => {
  ctx.body = {
    data: [{ name"秋風的筆記" }]
  };
});

router.post("/api/login"async ctx => {
  ctx.cookies.set("token", token, {
    expiresnew Date(+new Date() + 1000 * 60 * 60 * 24 * 7)
  });
  ctx.body = {
    msg"成功",
    code0
  };
});
效果

訪問 http://local.test/charles

瀏覽器顯示

image-20200413000229326

4.JSONP

JSONP 主要就是利用了 script 標籤沒有跨域限制的這個特性來完成的。

「使用限制」

僅支持 GET 方法,若是想使用完整的 REST 接口,請使用 CORS 或者其餘代理方式。

「流程解析」

1.前端定義解析函數(例如 jsonpCallback=function(){....})

2.經過 params 形式包裝請求參數,而且聲明執行函數(例如 cb=jsonpCallback)

3.後端獲取前端聲明的執行函數(jsonpCallback),並以帶上參數並調用執行函數的方式傳遞給前端。

「使用示例」

後端實現

const Koa = require("koa");
const fs = require("fs");
const app = new Koa();

app.use(async (ctx, next) => {
  if (ctx.path === "/api/jsonp") {
    const { cb, msg } = ctx.query;
    ctx.body = `${cb}(${JSON.stringify({ msg })})`;
    return;
  }
});

app.listen(8080);

普通 js 示例

<script type="text/javascript">
  window.jsonpCallback = function(res{
    console.log(res);
  };
</script>
<script
  src="http://localhost:8080/api/jsonp?msg=hello&cb=jsonpCallback"
  type="text/javascript"
>
</script>

JQuery Ajax 示例

<script src="https://cdn.bootcss.com/jquery/3.5.0/jquery.min.js"></script>
<script>
  $.ajax({
    url"http://localhost:8080/api/jsonp",
    dataType"jsonp",
    type"get",
    data: {
      msg"hello"
    },
    jsonp"cb",
    successfunction(data{
      console.log(data);
    }
  });
</script>

「原理解析」

其實這就是 js 的魔法

咱們先來看最簡單的 js 調用。嗯,很天然的調用。

<script>
  window.jsonpCallback = function(res{
    console.log(res);
  };
</script>
<script>
  jsonpCallback({ a: 1 });
</script>

咱們稍稍改造一下,外鏈的形式。

<script>
  window.jsonpCallback = function(res{
    console.log(res);
  };
</script>
<script src="http://localhost:8080/api/a.js"></script>

// http://localhost:8080/api/a.js jsonpCallback({a:1});

咱們再改造一下,咱們把這個外鏈的 js 就當作是一個動態的接口,由於自己資源和接口同樣,是一個請求,也包含各類參數,也能夠動態化返回。

<script>
  window.jsonpCallback = function(res{
    console.log(res);
  };
</script>
<script src="http://localhost:8080/api/a.js?a=123&cb=sonpCallback"></script>

// http://localhost:8080/api/a.js jsonpCallback({a:123});

你仔細品,細細品,是否是 jsonp 有的優點就是 script 加載 js 的優點,加載的方式只不過換了一種說法。這也告訴咱們一個道理,不少東西並無那麼神奇,是在你所學的知識範圍內。就比如,桃樹和柳樹,若是你把他們當成很大跨度的東西去記憶理解,那麼世上這麼多樹,你真的要累死了,你把他們都當成是樹,哦吼?你會忽然發現,你對世界上全部的樹都有所瞭解,他們都會長葉子,光合做用....固然也有個例,可是你只須要去記憶這些細微的差異,抓住主幹。。。嗯,反正就這麼個道理。

5.Websocket

WebSocket 規範定義了一種 API,可在網絡瀏覽器和服務器之間創建「套接字」鏈接。簡單地說:客戶端和服務器之間存在持久的鏈接,並且雙方均可以隨時開始發送數據。詳細教程能夠看 https://www.html5rocks.com/zh/tutorials/websockets/basics/

這種方式本質沒有使用了 HTTP, 所以也沒有跨域的限制,沒有什麼過多的解釋直接上代碼吧。

前端部分

<script>
  let socket = new WebSocket("ws://localhost:8080");
  socket.onopen = function({
    socket.send("秋風的筆記");
  };
  socket.onmessage = function(e{
    console.log(e.data);
  };
</script>

後端部分

const WebSocket = require("ws");
const server = new WebSocket.Server({ port8080 });
server.on("connection"function(socket{
  socket.on("message"function(data{
    socket.send(data);
  });
});

6.window.postMessage

「window.postMessage()」 方法能夠安全地實現跨源通訊。一般,對於兩個不一樣頁面的腳本,只有當執行它們的頁面位於具備相同的協議(一般爲 https),端口號(443 爲 https 的默認值),以及主機 (兩個頁面的模數 Document.domain設置爲相同的值) 時,這兩個腳本才能相互通訊。「window.postMessage()」 方法提供了一種受控機制來規避此限制,只要正確的使用,這種方法就很安全。

用途

1.頁面和其打開的新窗口的數據傳遞

2.多窗口之間消息傳遞

3.頁面與嵌套的 iframe 消息傳遞

用法

詳細用法看 https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage

otherWindow.postMessage(message, targetOrigin, [transfer]);

  • otherWindow: 其餘窗口的一個引用,好比 iframe 的 contentWindow 屬性、執行window.open返回的窗口對象、或者是命名過或數值索引的window.frames。

  • message: 將要發送到其餘 window 的數據。

  • targetOrigin: 經過窗口的 origin 屬性來指定哪些窗口能接收到消息事件.

  • transfer(可選) : 是一串和 message 同時傳遞的 Transferable 對象. 這些對象的全部權將被轉移給消息的接收方,而發送一方將再也不保有全部權

示例

index.html

<iframe
  src="http://localhost:8080"
  frameborder="0"
  id="iframe"
  onload="load()"
>
</iframe>
<script>
  function load({
    iframe.contentWindow.postMessage("秋風的筆記""http://localhost:8080");
    window.onmessage = e => {
      console.log(e.data);
    };
  }
</script>

another.html

<div>hello</div>
<script>
  window.onmessage = e => {
    console.log(e.data); // 秋風的筆記
    e.source.postMessage(e.data, e.origin);
  };
</script>

7.document.domain + Iframe

從第 7 種到第 9 種方式,我以爲別人的寫的已經很好了,爲了完整性,我就拿別人的了。若有雷同....(不對,就是雷同....)不要說不出來。

「該方式只能用於二級域名相同的狀況下,好比 a.test.comb.test.com 適用於該方式」。只須要給頁面添加 document.domain ='test.com' 表示二級域名都相同就能夠實現跨域。

www.   baidu.  com     .
三級域  二級域   頂級域   根域
// a.test.com
<body>
  helloa
  <iframe
    src="http://b.test.com/b.html"
    frameborder="0"
    onload="load()"
    id="frame"
  >
</iframe>
  <script>
    document.domain = "test.com";
    function load({
      console.log(frame.contentWindow.a);
    }
  
</script>
</body>
// b.test.com
<body>
  hellob
  <script>
    document.domain = "test.com";
    var a = 100;
  
</script>
</body>

8.window.location.hash + Iframe

實現原理

原理就是經過 url 帶 hash ,經過一個非跨域的中間頁面來傳遞數據。

實現流程

一開始 a.html 給 c.html 傳一個 hash 值,而後 c.html 收到 hash 值後,再把 hash 值傳遞給 b.html,最後 b.html 將結果放到 a.html 的 hash 值中。一樣的,a.html 和 b.htm l 是同域的,都是 http://localhost:8000,而 c.html 是http://localhost:8080

// a.html
<iframe src="http://localhost:8080/hash/c.html#name1"></iframe>
<script>
  console.log(location.hash);
  window.onhashchange = function({
    console.log(location.hash);
  };
</script>
// b.html
<script>
  window.parent.parent.location.hash = location.hash;
</script>
// c.html
<body></body>
<script>
  console.log(location.hash);
  const iframe = document.createElement("iframe");
  iframe.src = "http://localhost:8000/hash/b.html#name2";
  document.body.appendChild(iframe);
</script>

9.window.name + Iframe

window 對象的 name 屬性是一個很特別的屬性,當該 window 的 location 變化,而後從新加載,它的 name 屬性能夠依然保持不變。

其中 a.html 和 b.html 是同域的,都是http://localhost:8000,而 c.html 是http://localhost:8080

// a.html
<iframe
  src="http://localhost:8080/name/c.html"
  frameborder="0"
  onload="load()"
  id="iframe"
>
</iframe>
<script>
  let first = true;
  // onload事件會觸發2次,第1次加載跨域頁,並留存數據於window.name
  function load({
    if (first) {
      // 第1次onload(跨域頁)成功後,切換到同域代理頁面
      iframe.src = "http://localhost:8000/name/b.html";
      first = false;
    } else {
      // 第2次onload(同域b.html頁)成功後,讀取同域window.name中數據
      console.log(iframe.contentWindow.name);
    }
  }
</script>

b.html 爲中間代理頁,與 a.html 同域,內容爲空。

// b.html
<div></div>
// c.html
<script>
  window.name = "秋風的筆記";
</script>

經過 iframe 的 src 屬性由外域轉向本地域,跨域數據即由 iframe 的 window.name 從外域傳遞到本地域。這個就巧妙地繞過了瀏覽器的跨域訪問限制,但同時它又是安全操做。

10.瀏覽器開啓跨域(終極方案)

其實講下其實跨域問題是瀏覽器策略,源頭是他,那麼可否能關閉這個功能呢?

答案是確定的。

「注意事項: 由於瀏覽器是衆多 web 頁面入口。咱們是否也能夠像客戶端那種,就是用一個單獨的專門宿主瀏覽器,來打開調試咱們的開發頁面。例如這裏以 chrome canary 爲例,這個是我專門調試頁面的瀏覽器,不會用它來訪問其餘 web url。所以它也相對於安全一些。固然這個方式,只限於當你真的被跨域折磨地崩潰的時候才建議使用如下。使用後,請以正常的方式將他打開,以避免你不當心用這個模式幹了其餘的事。」

Windows

找到你安裝的目錄
.\Google\Chrome\Application\chrome.exe --disable-web-security --user-data-dir=xxxx

Mac

~/Downloads/chrome-data 這個目錄能夠自定義.

/Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary  --disable-web-security --user-data-dir=~/Downloads/chrome-data

效果展現

image-20200413143102377

嗯,使用起來很香,可是再次提醒,通常狀況千萬別輕易使用這個方式,這個方式比如七傷拳,使用的好威力無比,使用很差,很容易傷到本身。

3、爲何須要跨域?

在最一開始,咱們知道了,跨域只存在於瀏覽器端。而瀏覽器爲 web 提供訪問入口。咱們在能夠瀏覽器內打開不少頁面。正是這樣的開放形態,因此咱們須要對他有所限制。就好比林子大了,什麼鳥都有,咱們須要有一個統一的規範來進行約定才能保障這個安全性。

1.限制不一樣源的請求

這裏仍是用最經常使用的方式來說解,例如用戶登陸 a 網站,同時新開 tab 打開了 b 網站,若是不限制同源, b 能夠像 a 網站發起任何請求,會讓不法分子有機可趁。

2.限制 dom 操做

我舉個例子吧, 你先登陸下 www.baidu.com ,而後訪問我這個網址。

https://zerolty.com/node-demo/index.html

image-20200413190413758

你會發現,這個和真實的百度如出一轍,若是再把域名搞的類似一些,是否是很容易被騙,若是能夠進行 dom 操做...那麼你們的信息在這種釣魚網站眼裏都是一顆顆小白菜,等着被收割。

能夠在 http 返回頭 添加X-Frame-Options: SAMEORIGIN 防止被別人添加至 iframe。

寫在最後

以上最經常使用的就是前 4 種方式,特別是第 2 種很是常見,我裏面也提到了多種示例,你們能夠慢慢消化一下。但願將來有更加安全的方式來限制 web ,解決跨域的頭疼,哈哈哈哈。

「有一個不成熟的想法,能夠搞這麼一個瀏覽器,只讓訪問內網/本地網絡,專門給開發者用來調試頁面用,對於靜態資源能夠配置白名單,這樣是否是就沒有跨域問題了,23333。上述若有錯誤,請第一時間指出,我會進行修改,以避免給你們來誤導。」

參考

https://stackoverflow.com/questions/12296910/so-jsonp-or-cors

https://juejin.im/post/5c23993de51d457b8c1f4ee1#heading-18

https://juejin.im/post/5a6320d56fb9a01cb64ee191

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS

https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy


本文分享自微信公衆號 - 牧碼的星星(gh_0d71d9e8b1c3)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索