前端如何处理跨域

434 阅读2分钟

**这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战**w

跨域是什么?

1.同源策略

跨域问题其实就是浏览器的同源策略所导致的。

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

当跨域时会收到以下错误

2.同源示例

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

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

image-20200412171942421

只有当

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

才是同源。

以下协议、域名、端口一致。

www.qihh.com:80/a.js

www.qihh.com:80/b.js

以下这种看上去再相似也没有用,都不是同源。

www.qihh.com:8080

www2.qihh.com:80

在这里注意一下啊,这里是为了突出端口的区别才写上端口。在默认情况下 http 可以省略端口 80, https 省略 443。

如何解决跨域?

CORS

跨域资源共享CORS 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的 Web 应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器「不同的域、协议或端口」请求一个资源时,资源会发起一个「跨域 HTTP 请求」**。

「浏览器支持情况」

当你使用 IE<=9, Opera<12, or Firefox<3.5 或者更加老的浏览器,这个时候请使用 JSONP 。

Nginx 反向代理

Nginx 则是通过反向代理的方式。我们可以通过一个例子看一下nginx的配置:

server {

    listen 8033;//端口
    server_name bj01.reocar.ud_client.com;//代理的域名
    //日志
    access_log /tmp/udesk.imclient.nginx.access.log;
    error_log /tmp/udesk.imclient.nginx.error.log;

    location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
        expires 90d;
        add_header Vary "Accept-Encoding";
    }
    # location ^~ /im_client {proxy_pass http://reocar.ud.com/im_client/;}
    location ^~ /static_cmps {
        proxy_pass http://****-rd-bj-01.qihh.cn/static_cmps;
    }
    location ^~ /spa1 {
        proxy_pass http://****-rd-bj-01.qihh.cn/spa1;
    }
    location ^~ /im_client {
        proxy_pass http://localhost:3001/;
    }
    location ^~ /****_im {
        proxy_pass http://****-rd-bj-01.qihh.cn/****_im;
    }
}

我们可以看到代理的请求路径会将请求转移到我们代理的proxy_pass的服务器地址上。这样我们就实现了nginx的代理。

//nginx的一些操作,
//重启
nginx -s reload
//启动nginx
nginx start
//停止nginx
nginx stop

JSONP

JSONP 主要就是利用了 script 标签没有跨域限制的这个特性来完成的。JSONP的原理非常简单,就是HTML标签中,很多带src属性的标签都可以跨域请求内容,比如我们熟悉的img图片标签。同理,script标签也可以,可以利用script标签来执行跨域的javascript代码。通过这些代码,我们就能实现前端跨域请求数据。

使用限制

仅支持 GET 方法,如果想使用完整的 REST 接口,请使用 CORS 或者其他代理方式。

我们来看一下最简单的JSONP的例子: html代码,直接浏览器打开即可

<html>
  <body>
    <div>
      receive <span id="qwerty"> </span>
    </div>
  </body>
  <script>
    function callfun(data) {
      document.getElementById('qwerty').innerHTML = data;
    }
  </script>
  <script src="http://127.0.0.1:10010/js?call=callfun"></script>
</html>

后端使用的egg.js,核心代码只有ctx.body那一句

'use strict';

const Controller = require('egg').Controller;
class JsonpController extends Controller {
  async index() {
    const { ctx } = this;
    console.log(ctx.query);
    ctx.set('content-type', 'text/javascript');
    ctx.body = ctx.query.call + '("nihao")';
  }
}

为了让后端知道我们前端的回调函数的名字,我们在script的请求中加入了call=callfun参数,后端接收到ctx.query.call, 再和'("nihao")'合并,最后形成了字符串 callfun("nihao")这一句JS代码,传到前端。

这个"nihao"就是我们从后端跨域传输到前端的数据了。

callfun函数处理这个数据,显示到了屏幕中。

Websocket

WebSocket 规范定义了一种 API,可在网络浏览器和服务器之间建立“套接字”连接。简单地说:客户端和服务器之间存在持久的连接,而且双方都可以随时开始发送数据。

这种方式本质没有使用了 HTTP 的响应头, 因此也没有跨域的限制