天天看点

终结同源策略!轻松实现跨域访问的9种方式!

作者:阿珊和她的猫
终结同源策略!轻松实现跨域访问的9种方式!

跨域指的是在Web浏览器中,一个站点的脚本试图向另一个站点的脚本发起HTTP请求,这种请求是由于浏览器遵守同源策略(Same-Origin Policy)而被拒绝的。为了解决这个问题,我们可以使用以下9种方式:

1. JSONP:通过动态创建<script>标签向其他域请求数据,并将数据作为回调函数参数传递回来。

当需要在一个网页中访问另一个域名下的数据时,由于浏览器的同源策略会阻止这种访问,因此就需要使用JSONP来实现跨域数据的访问。

JSONP(JSON with Padding)是一种实现跨域网络数据传输的技术,它利用了<script>标签的跨域访问特性。

使用JSONP的基本过程如下:
  1. 在客户端代码中定义一个全局的回调函数,用于接收服务器返回的数据。
  2. 构建一个动态创建

示例代码如下:

function jsonpRequest(url, callback) {
  // 创建<script>标签
  var script = document.createElement('script');
  // 构建URL地址,指定回调函数名称
  var callbackName = 'jsonp_callback_' + Math.floor(Math.random() * 100000);
  url += '&callback=' + callbackName;
  // 在window对象上定义回调函数
  window[callbackName] = function(data) {
    // 处理服务器返回数据
    callback(data);
    // 删除回调函数
    delete window[callbackName];
    // 移除<script>标签
    document.body.removeChild(script);
  };
  // 设置<script>标签的src属性
  script.src = url;
  // 添加<script>标签到<body>元素中
  document.body.appendChild(script);
}
           

通过调用这个jsonpRequest函数,我们可以跨域请求数据,并将数据传递给回调函数进行处理。

2. CORS:在服务器端设置响应头信息,允许跨域访问。

CORS(Cross-Origin Resource Sharing)是一种允许跨域访问资源的机制,它通过在服务器端设置响应头信息来允许跨域访问。

使用CORS的基本过程如下:
  1. 在服务器端设置响应头信息,允许指定的跨域请求访问资源。通过设置Access-Control-Allow-Origin头信息,指定允许访问的源,比如设置为"*"表示允许任意源访问。
  2. 浏览器在发送跨域请求时,会在请求头中添加Origin头信息,这个头信息指示了请求的源地址。
  3. 服务器在接收到请求后,会根据Origin头信息,在响应头中添加Access-Control-Allow-Origin头信息,以表明该源地址被允许访问。

示例代码如下:

// 使用Node.js的Express框架实现CORS跨域访问
const express = require('express');
const app = express();

// 设置允许跨域访问的资源
app.use((req, res, next) => {
  // 设置允许跨域的源
  res.setHeader('Access-Control-Allow-Origin', '*');
  // 设置允许跨域的请求头
  res.setHeader('Access-Control-Allow-Headers', 'origin, content-type, accept');
  // 设置允许跨域的请求方法
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
  // 继续处理请求
  next();
});

// 处理API请求
app.get('/api', (req, res) => {
  // 返回JSON数据
  res.json({
    code: 0,
    message: 'success',
    data: {
      name: 'JSONP',
      description: '使用JSONP实现跨域数据访问'
    }
  });
});

// 启动服务器
app.listen(3000, () => {
  console.log('Server is running on port 3000');
});
           

在这个示例代码中,我们使用了Node.js的Express框架实现了一个简单的API,该API允许跨域访问,通过设置响应头信息,指定了允许跨域请求访问的源、请求头和请求方法。客户端代码可以使用XMLHttpRequest或Fetch API来访问该API,并获得返回的JSON数据。

3. WebSocket:采用全双工通信方式,无需提前建立HTTP连接,可以跨域通讯。

WebSocket是一种基于TCP协议实现的全双工通信协议,它采用了一种类似HTTP的握手机制,在建立连接后,双方可以进行自由的数据传输,可以跨域通信,无需提前建立HTTP连接。

使用WebSocket的基本过程如下:
  1. 客户端通过WebSocket对象创建WebSocket连接,指定WebSocket服务端的地址。
  2. WebSocket服务端接收到连接请求后,进行握手处理,并建立起连接。在握手时,服务端会告知客户端是否支持WebSocket协议。
  3. 如果握手成功,客户端和服务端就可以进行双向数据传输,客户端和服务端均可发送和接收数据。

示例代码如下:

// 创建WebSocket连接
var socket = new WebSocket('ws://localhost:8080');

// 连接成功时,打印日志信息
socket.onopen = function() {
  console.log('WebSocket connection is opened.');
};

// 接收到消息时,打印消息内容
socket.onmessage = function(event) {
  console.log('WebSocket received message:', event.data);
};

// 连接关闭时,打印日志信息
socket.onclose = function() {
  console.log('WebSocket connection is closed.');
};

// 发送消息
socket.send('Hello WebSocket!');
           

在这个示例中,我们使用WebSocket对象创建了一个WebSocket连接,并指定了WebSocket服务端的地址,然后通过事件监听函数来处理连接状态、接收消息和发送消息。客户端和服务端之间可以发送和接收数据,实现实时通信的功能。由于WebSocket协议采用了类似HTTP的握手机制,所以WebSocket可以跨域通信,无需提前建立HTTP连接。

4.postMessage:HTML5提供了一个专门用于跨文档通信的API,可以跨域通信。

HTML5提供了一个专门用于跨文档通信的API——postMessage。postMessage方法可以在不同的窗口(或文档)之间传递数据,包括字符串和对象等数据类型,可以实现跨域通信。

使用postMessage的基本过程如下:
  1. 在发送消息的窗口中,使用postMessage方法向指定的窗口发送消息,指定窗口的origin属性(协议、域名、端口号)以便接收窗口验证消息是否来自信任的源。
  2. 在接收消息的窗口中,通过添加message事件监听函数来接受消息,事件对象包含了发送窗口的信息、消息数据等。

示例代码如下:

// 发送消息
var sendMessage = {
  type: 'text',
  content: 'Hello, postMessage!'
};
var targetWindow = window.opener;
var targetOrigin = '*';
targetWindow.postMessage(sendMessage, targetOrigin);

// 接收消息
window.addEventListener('message', function(event) {
  if (event.origin !== 'http://example.com') {
    return;
  }
  console.log(event.data);
}, false);
           

在这个示例中,我们在发送消息的窗口中,使用postMessage方法向指定的窗口发送消息,指定了接收窗口的origin,以保证消息来自可信任的源。在接收消息的窗口中,添加message事件监听函数来接收消息,通过验证消息来自可信任的源,然后使用事件对象来获取消息数据。通过这种方式实现了从一个窗口向另一个窗口发送跨域消息,并实现了跨域通信。

5. 代理服务器:通过在服务器端代理请求,将跨域请求转到同一服务器,再返回到请求端。

代理服务器是指在服务器端转发请求和响应的中间层服务器,它可以代理客户端向目标服务器发送请求,获取响应,从而实现跨域请求。

使用代理服务器实现跨域请求的基本过程如下:
  1. 客户端向代理服务器发送请求。
  2. 代理服务器接收到请求后,将请求转发给目标服务器,获取响应。
  3. 代理服务器将响应返回给客户端,完成跨域请求。

示例代码如下:

// 在Node.js中使用http-proxy中间件实现代理服务器
const http = require('http');
const httpProxy = require('http-proxy');

// 创建代理服务器
const proxy = httpProxy.createProxyServer({});

// 处理请求
const server = http.createServer((req, res) => {
  console.log('Proxy request:', req.url);
  // 设置响应头,允许跨域访问
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Headers', 'origin, content-type, accept');
  // 如果是API请求,将请求转发到目标服务器
  if (req.url.startsWith('/api')) {
    proxy.web(req, res, {
      target: 'http://localhost:3000'
    });
  } else {
    res.end();
  }
});

// 监听端口,启动服务器
server.listen(8000, () => {
  console.log('Server is running on port 8000');
});
           

在这个示例中,我们使用Node.js自带的http模块创建了一个HTTP服务器,通过设置响应头信息,实现了允许跨域访问。在处理请求时,我们通过判断请求URL,将API请求转发到目标服务器,使用http-proxy中间件实现了代理服务器的功能,将请求转发到同一服务器上,再将响应返回给客户端,从而实现了跨域请求的目的。

6. nginx反向代理:利用nginx的反向代理特性来实现跨域。

Nginx是一款高性能的Web服务器和反向代理服务器,通过配置nginx的反向代理功能,可以解决跨域问题。

使用nginx反向代理实现跨域请求的基本步骤如下:
  1. 安装nginx,并编辑配置文件。
  2. 在nginx配置文件中,使用location指令匹配需要代理的URL,并设置代理服务器的地址和端口号。
  3. 重启nginx,使配置文件生效。

示例代码如下:

# 将请求 /api/ 转发到 http://localhost:3000/
location /api/ {
  # 设置允许跨域访问
  add_header 'Access-Control-Allow-Origin' '*';
  
  # 设置需要代理的目标服务器
  proxy_pass http://localhost:3000/;
}
           

在这个示例中,我们使用nginx反向代理功能,将请求URL为/api/的请求转发到http://localhost:3000/服务器,实现了跨域请求。同时,我们在nginx配置文件中设置了Access-Control-Allow-Origin头信息,允许跨域访问,从而实现了跨域通信的目的。

7. iframe嵌套:利用iframe的跨域通信特性,在不同的iframe之间实现数据交换。

iframe是HTML中的一个重要元素,可以在一个网页中嵌入另一个网页或文档。iframe具有访问不同域名网页的能力,因此可以在不同iframe之间实现跨域通信。

使用iframe嵌套实现跨域通信的基本过程如下:
  1. 在主页面中定义一个iframe元素,并设置src属性为跨域页面的URL。
  2. 在跨域页面中,通过window.parent来获取主页面的window对象,并向主页面发送消息。
  3. 在主页面中,通过监听message事件来接收来自iframe的消息。

示例代码如下:

在主页面嵌入iframe:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>主页面</title>
</head>
<body>
  <h1>主页面</h1>
  <iframe src="http://example.com/iframe.html"></iframe>
  <script>
    // 监听消息事件,接收来自iframe的消息
    window.addEventListener('message', function(event) {
      console.log(event.data);
    }, false);
  </script>
</body>
</html>
           

在跨域页面发送消息:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>跨域页面</title>
  <script>
    // 发送消息给主页面
    window.parent.postMessage('Hello, parent window!', '*');
  </script>
</head>
<body>
  <h1>跨域页面</h1>
</body>
</html>
           

在这个示例中,我们在主页面中嵌入了一个跨域的iframe,并监听message事件来接收来自iframe的消息。在跨域的iframe中,通过window.parent.postMessage()方法发送消息给主页面,'*'表示不限制发送消息的域名,接收哪个窗口也是如此。

使用iframe嵌套实现跨域通信时,需要注意的是,需要确保主页面和iframe页面的信任关系,避免信息泄露和CSRF攻击。

8. Document.domain:在同一个父域名下的不同子域名之间,通过设置document.domain来实现跨域访问。

在同一个父域名下的不同子域名之间进行跨域操作时,由于浏览器的同源策略,会限制JS代码的访问权限,为了解决这个问题,可以在父域名相同的情况下,通过设置document.domain来实现跨域访问。

具体实现方式是,在不同的子域名中,将document.domain设置为相同的父域名,如将子域名A.domain.com和子域名B.domain.com中的document.domain都设置为domain.com。这样,这两个子域名就可以通过JS代码来共享cookie、iframe等资源了。

需要注意的是,document.domain只能设置为包含它的域,例如,它可以设置为domain.com或sub.domain.com,但不能设置为otherdomain.com。并且,不同子域名之间的跨域操作仅适用于协议、端口号相同的情况。

下面是一个简单的JS代码示例:

假设当前页面的URL是https://sub1.example.com/index.html,想要在同域下的https://sub2.example.com/other.html页面中,访问当前页面的cookie。

  1. 在当前页面(https://sub1.example.com/index.html)中,设置document.domain:
document.domain = 'example.com';
           
  1. 在同域下的另一个页面(https://sub2.example.com/other.html)中,通过JS代码来访问当前页面的cookie:
// 获取当前域下的cookie
var cookies = document.cookie;
console.log(cookies);
           

通过将两个页面的document.domain设置为相同的父域名,使得它们处于同一个域中,从而实现了跨域访问。

9. 跨域资源共享(CORS)标准:CORS机制通过一些特定的HTTP头部来告诉浏览器,哪些跨域请求是可以被允许的,从而实现跨域数据访问。

CORS(Cross-origin Resource Sharing)是一个W3C标准,用于解决通过HTTP进行跨域访问的问题。

CORS机制允许服务器向浏览器发送附加HTTP头,告诉浏览器哪些跨域请求是被允许的。

具体来说,当浏览器向跨域服务器请求数据时,服务器可以在响应头部中加入Access-Control-Allow-Origin字段。例如,如果A域名的页面向B域名的服务器请求资源,B服务器可以在响应头中添加以下字段:

Access-Control-Allow-Origin: https://A-domain.com
           

这个字段告诉浏览器A域名下的页面可以跨域访问B域名服务器的资源。如果没有设置这个字段,跨域请求将会被浏览器阻止。

除了Access-Control-Allow-Origin字段,CORS机制还可以通过其他一些HTTP头部实现更复杂的跨域请求。例如,Access-Control-Allow-Methods字段指定了服务器支持的HTTP方法,Access-Control-Allow-Headers指定了服务器接受的请求头,Access-Control-Allow-Credentials指定了请求是否需要使用凭证等等。

下面是一个简单的CORS示例代码,在服务器端需要添加Access-Control-Allow-Origin头部。

// 定义CORS允许访问的域名
const ALLOW_ORIGIN = 'https://example.com'

const http = require('http');

http.createServer(function(req, res) {
    console.log('request method:', req.method);
    console.log('request url:', req.url);
    console.log('request headers:', req.headers);
    if (req.method === 'OPTIONS') {
        // 处理预检请求
        res.writeHead(200, {
            'Access-Control-Allow-Origin': ALLOW_ORIGIN,//此处指定了允许访问的域名
            'Access-Control-Allow-Methods': 'POST,GET,OPTIONS',
            'Access-Control-Allow-Headers': 'X-Requested-With,Content-Type',
            'Access-Control-Allow-Credentials': 'true',
            'Content-Length': 0
        });
        res.end();
    } else {
        // 处理CORS请求
        res.writeHead(200, {
            'Access-Control-Allow-Origin': ALLOW_ORIGIN,//此处指定了允许访问的域名
            'Access-Control-Allow-Credentials': 'true',
            'Content-Type': 'application/json'
        });
        res.write(JSON.stringify({'msg': 'Hello CORS!'}));
        res.end();
    }
}).listen(3000, function() {
    console.log('Listening on port 3000');
});
           

这段代码创建了一个HTTP服务器,当浏览器向该服务器发送CORS请求时,服务器会在响应头中添加Access-Control-Allow-Origin和Access-Control-Allow-Credentials字段,从而允许跨域访问。

继续阅读