Skip to content

【JS】跨域及其解决方法。 #26

@Dliling

Description

@Dliling

为什么会出现跨域呢?这个是由于浏览器的同源策略。同源策略限制是浏览器出于安全方面的考虑,只允许与本域下的接口交互。不同源的客户端脚本在没有明确授权的情况下,不能读写对方的资源。
同源是从三方面来看:

  1. 同协议:如都是http或https
  2. 同域名:如都是https://a.comm/listhttps://a.comm/detail
  3. 同端口:如都是80端口

那如果我想访问不同源的资源,如何做呢?跨域

解决方法
1. JSONP
JSONP有两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数。回调函数的名字一般是在请求中指定的。数据就是传入回调函数中的JSON数据。
我们知道script通过src属性可以引入其他域下的JS,如jquery。利用这个特性,我们可以动态创建一个script标签,访问后端地址,拿到数据后插入body中。这种方式需要后端同学配合。

function cb(data) {
     console.log('the response data is:' + data);
}
var srcipt = document.createElement('script');
script.src = 'https://example.com/data/?callback=cb';
document.body.insertBefore(script, document.body.firstChild);

// 通过jq
$.getJson('https://example.com/data/?callback=', function(data) {}
    // do something
);

缺点:

  1. 安全问题(请求代码总可能存在安全隐患)
  2. jsonp请求失败难以判断

2. CORS
全称是跨域资源共享,是一种ajax跨域请求资源的方式,支持现代浏览器,IE10以上。基本思想就是使用自定义的http头部让浏览器和服务器进行沟通,从而决定请求或响应式应该成功还是失败。
实现方法简单,使用XMLHttpRequest发送请求时,浏览器发现不符合同源策略时,会在请求头中加上origin。后端进行一系列处理,若确定接受请求则在响应头中加入Access-Control-Allow-Origin,浏览器判断该响应头中是否包含请求头中origin的值,若包含则响应数据,若不包含则驳回。
例如:
请求头:origin: https://example.com/
响应头:Access-Control-Allow-origin: https://example.com/,公共资源也可以是'*'。

详见阮一峰 CORS通信

这种方式对于前端开发是无感知的,主要是浏览器和后端同学在工作~

3. 图像Ping
就是域服务器金牛星简单、单向的跨域通信的一种方式。请求数据是通过查询字符串的形式发送的,而响应可以是任意内容,但通常是像素图或204响应。通过图像PIng,浏览器得不到任何具体的数据,但通过侦听load和error事件,能知道响应是什么时候接收到的。

var img = new Image();
img.onload = img.onerror = function () {
    // do something
};
img.src = 'https://example.com/name=test';

通常用于跟踪用户点击页面或动态广告曝光次数。
缺点:

  1. 只能发送get请求
  2. 无法访问服务器的响应文本,因此只能用于浏览器和服务器间的单向通信。

4. Web Sockets
是一种新浏览器API,目标是在一个单独的持久链接上提供全双工、双向通信。
在JS中创建了Web Socket之后,会有一个HTTP请求发送到浏览器发起连接。在取得服务器响应后,建立的连接会使用HTTP升级从HTTP协议交换为Web Socket协议。因此只有在支持Web Socket协议的浏览器上才能正常工作。
兼容性:Firefox 6+,Safari 5+, Chrome,IOS 4+版Safari。

// http => ws, https => wss
var socket = new WebSocket('https://example.com/server.php');
// 只能发送纯文本数据,对于复杂数据发送前压序列化
socket.send('hello socket');
socket.onmessage = function(event) {
    var data = event.data;
};

传入WebSocket的URL必须是绝对URL。同源策略对Web Sockets不适用,因此可以通过它打开任意站点的连接。

5. window.postMessage
详细介绍移步MDN
window.postMessage()可以安全地实现跨源通信。只要正确的使用,这种方法就很安全。从广义上讲,一个窗口可以获得对另一个窗口地引用(比如targetWindow = window.opener),然后在窗口上调用targetWinndow.postMessage()方法分发一个MessageEvent消息。接收消息的窗口可以根据需要自由处理事件。传递给window.postMessage()的参数将通过消息事件对象暴露给接收消息的窗口。

otherWindow.postMessage(message, targetOrigin)

  • otherWindow:接受消息的window对象

  • message:将要发送到其他window地数据,会被序列化

  • targetOrigin:接收消息的窗口origin,*表示任意。

window.addEventListener('message', receiveMessage, false);
function receiveMessage(event) {
    var origin = event.origin;
    if (origin === 'https://example.com:8080') {
        var data = event.data;
        event.source.postMessage('hi,' + event.origin + data);
    }
}

通过以上代码,其他window可以监听分发的message。message属性如下:

  • data:从其他window中传递过来的对象。

  • origin:调用postMessage时消息发送方窗口的origin。这个字符串由协议、'://'、域名、':端口号'拼接而成。

  • source:对发送消息的窗口对象的引用,可以使用此来在具有不通origin的两个窗口之间建立双向通信。

6. http-proxy-middleware
基于node.js的插件,如果项目是用vue-cli脚手架搭建的,这个插件是集成在脚手架里的。

const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
 
const app = express();
 
app.use('/api', createProxyMiddleware({ target: 'http://www.example.org', changeOrigin: true }));
app.listen(3000);

vue-cli中修改代理:config/index.js

module.exports = {
    dev: {
        proxyTable: {
            '/api': {
                target: url,
                changeOrigin: true
            }
        }
    }
}

7. axios和express结合使用
axios
express

const express = require('express');
const axios = require('axios');
app.get(url, (req, res) => {
    const url = 'http://www.example.org';
    axios.get(url, {
        params: req.query
    }).then(res => {
        console.log(res);
    });
});

8. 使用fetch请求

  • 当接收到一个代表错误的HTTP状态码时,从fetch返回的promise不会被标记为reject,即使响应的状态码是404。相反,它会将promise状态标记为resolve(但是会将resolve的返回值的OK属性设置为false),仅当网络故障时或请求被阻止时,才会被标记为reject。

  • 不会接受跨域cookies。也不能使用fetch()建立起跨域会话。

  • 不会发送cookies。除非你设置了credentials:include。默认是same-origin。

fetch('http://example.com/movies.json', {
    mode: 'cors'
})
  .then(function(response) {
      if (response.ok) {
           return response.json();   
      }
      throw new Error('network response was not ok');
  })
  .then(function(myJson) {
    console.log(myJson);
    });

我的总结就到这里了,在查阅资料的时候也看到几篇不错的总结,可以移步~

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions