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

一、什么是跨域?

同源策略

同源策略:是一个重要的安全策略,它用于限制一个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
    代码展示
    前端代码:

后端代码:

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 示例



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



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



3,最终与后端接口进行联调,其实也是一个js函数
``

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

5.Websocket

WebSocket 规范定义了一种 API,可在网络浏览器和服务器之间建立“套接字”连接。简单地说:客户端和服务器之间存在持久的连接,而且双方都可以随时开始发送数据。
这种方式本质没有使用了 HTTP, 因此也没有跨域的限制。
前端部分


后端部分

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

  helloa
  
  

// b.test.com

  hellob
  

8.window.location.hash + Iframe

实现原理
原理就是通过 url 带 hash ,通过一个非跨域的中间页面来传递数据。
实现流程

// a.html


// c.html



// b.html

9.window.name + Iframe

三、为什么需要跨域?

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

你可能感兴趣的:(个人总结:浅谈浏览器跨域及解决办法?)