美文网首页面试题
个人总结:浅谈浏览器跨域及解决办法?

个人总结:浅谈浏览器跨域及解决办法?

作者: 前端小帅 | 来源:发表于2020-05-26 20:15 被阅读0次

一、什么是跨域?

同源策略

同源策略:是一个重要的安全策略,它用于限制一个origin的文档,或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。

同源示例

那么如何才算是同源呢?先来看看 url 的组成部分?

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

在这里插入图片描述
只有当 【protocol(协议),domain(域名)port(端口)】三者一致,才是同源。

正确示例:
http://www.example.com:80/a.js
http://www.example.com:80/b.js
属于协议、域名、端口一致。

错误示例:
http://www.example.com:8080
http://www.example.com:80
没有三者一致。

二、如何解决跨域?

1.CORS

CORS(跨域资源共享):是一种机制,它使用额外的http头来告诉浏览器,让运行在(domain)上的web可以被允许访问不同资源服务器上的指定资源。
在cors中会有简单请求和非简单请求。

  • 简单请求
    不会触发cors预检请求,这样的请求为“简单请求”,
  1. 情况一:使用以下方法请求: GET、POST、HEAD
  2. 情况二:人为设置以下集合外的请求头:Accept、Accept-Language、Content-Language、Content-Type、DPR。
  3. 情况三:Content-type的值仅限:text/plain、multipart/form-data、application/x-www-form-urlencoded。
  4. 情况四:请求中的任意XMLHttpRequestUpload对象均没有注册任何事件监听器
  5. 请求中没有使用ReadableStream对象。
  • 非简单请求
    除以上情况。
  • node中的解决方案
  1. 原生方式
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();
});
  1. 第三方中间件
const cors = require("koa-cors");
app.use(cors());
  • CORS中的cookie问题
    要同时满足3个条件
  1. web 请求设置withCredentials
    这里默认情况下在跨域请求,浏览器是不带 cookie 的。但是我们可以通过设置 withCredentials 来进行传递 cookie.
// 原生 xml 的设置方式
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
// axios 设置方式
axios.defaults.withCredentials = true;
  1. Access-Control-Allow-Credentials 为 true
  2. Access-Control-Allow-Origin为非 *
  • 避免重复options请求
    Access-Control-Max-Age:(number)数值代表(预检请求)的返回结果可以被缓存多久,单位是秒。

2.Node 正向代理

代理的思路为,利用服务端请求不会跨域的特性,让接口和当前站点同域。

  • Webpack (4.x)
    在webpack中可以配置proxy来快速获得接口代理的能力。
  devServer: {
    port: 8000,
    proxy: {
      "/api": {
        target: "http://localhost:8080"
      }
    }
  }

原理:其实devServer是以express起的服务,起核心是用到http-proxy-middleware中的socket、rewrite 等功能。

  • charles
    利用 charles 进行跨域,本质就是请求的拦截与代理。、
    在 tools/map remote 中设置代理


    在这里插入图片描述

3.Nginx 反向代理

通过反向代理的方式能够进行跨域,前端通过nginx代理到后端接口。

  1. 配置下 hosts:127.0.0.1 local.test
  2. 配置 nginx
server {
        listen 80;
        server_name local.test;
        location /api {
            proxy_pass http://localhost:8080;
        }
        location / {
            proxy_pass http://localhost:8000;
        }
}
  1. 重启 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, {
    expires: new Date(+new Date() + 1000 * 60 * 60 * 24 * 7)
  });
  ctx.body = {
    msg: "成功",
    code: 0
  };
});

效果
访问 http://local.test/charles

在这里插入图片描述

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>

「原理解析」
1,最基本的js调用

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

2,在script中的src去外链 js代码

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

3,最终与后端接口进行联调,其实也是一个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});`
``

5.Websocket

WebSocket 规范定义了一种 API,可在网络浏览器和服务器之间建立“套接字”连接。简单地说:客户端和服务器之间存在持久的连接,而且双方都可以随时开始发送数据。
这种方式本质没有使用了 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({ port: 8080 });
server.on("connection", function(socket) {
  socket.on("message", function(data) {
    socket.send(data);
  });
});

6.window.postMessage

「window.postMessage()」 方法可以安全地实现跨源通信。

7.document.domain + Iframe

「该方式只能用于二级域名相同的情况下,比如 a.test.com 和 b.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
<iframe src="http://localhost:8080/hash/c.html#name1"></iframe>
<script>
  console.log(location.hash);
  window.onhashchange = function() {
    console.log(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>

// b.html
<script>
  window.parent.parent.location.hash = location.hash;
</script>

9.window.name + Iframe

三、为什么需要跨域?

1.限制不同源的请求
限制攻击者窃取请求数据
2.限制 dom 操作
限制钓鱼网站.操作dom节点

相关文章

  • 个人总结:浅谈浏览器跨域及解决办法?

    一、什么是跨域? 同源策略 同源策略:是一个重要的安全策略,它用于限制一个origin的文档,或者它加载的脚本如何...

  • HTML-获取iframe元素的正确方法

    跨域相关文章详解js跨域问题JavaScript跨域总结与解决办法 解释最清楚的jsonpWhat is JSON...

  • js如何解决跨域问题

    JavaScript跨域总结与解决办法 什么是跨域 1、document.domain+iframe的设置 2、动...

  • jsonp

    参考:轻松搞定JSONP跨域请求参考:JavaScript 跨域总结与解决办法要理解跨域,先要了解一下“同源策略”...

  • 产生跨域的原因

    产生跨域的原因 浏览器的限制 跨域 XHR(XMLHttpRequest)请求 1、浏览器的限制 2、跨域 跨域概...

  • 跨域与常用方案

    本文源自一次内部关于跨域的讨论分享的总结 理解跨域的重点在于:了解跨域产生的场景、原理 跨域问题只在浏览器客户端环...

  • 2020-11-10 浏览器跨域

    谷歌浏览器(chrome)允许跨域设置的方法 什么是跨域? 跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器...

  • Ajax的跨域问题

    什么是跨域及来源 跨域问题来源于浏览器的同源策略,JavaScript只能访问和操作自己域下的资源,不能访问和操作...

  • 浏览器跨域问题,教你手写实现jsonp跨域

    跨域概述为什么会有跨域跨域解决办法:1、jsonp;2、后台代理手写实现jsonp跨域(包括服务器端代码) 跨域问...

  • Vue cli 3.x 版本跨域问题

    前言 首先简单的说一下什么是跨域 跨域首先是出现在浏览器里的,也就是浏览器拦截了跨域请求。 跨域所跨的域(doma...

网友评论

    本文标题:个人总结:浅谈浏览器跨域及解决办法?

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