什么是跨域
跨域:指的是在网页端,发起一个跨越不同域名(协议、端口号)的HTTP请求的过程。
例如:当一个页面的URL为http://a.com,通过Ajax向http://b.com发送请求,就是一个跨域请求。
跨域问题是由浏览器的同源策略
所引起的。同源策略限制了JS代码
只能读取与其来源页面具有相同域名的数据。具体而言,同源策略是一种浏览器安全策略,它通过检查来源URL(协议 + 域名 +端口)
是否与目标URL
来源是否相同来决定是否允许跨域访问。同源策略主要限制以下四种行为
:
Cookie、LocalStorage、IndexedDB
等存储性质的操作限制无法读取、写入非同源网站的Cookie
等数据。DOM
操作的各种API(如document.href等)
限制无法获取和修改非同源网站的DOM
对象。AJAX请求
限制无法向非同源网站地址
发送 AJAX 请求。window.open()
方法限制无法打开非同源网站访问页面。`
举例说明
假设有一个在本地localhost:8080
上的静态网站,并且它得到一个需要跨域请求的数据服务在www.server.com
后端运行。(1)请求方式一:使用JavaScript
内置XMLHttpRequest
对象
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://www.server.com/data.json', true);
xhr.onreadystatechange = function(){
if(xhr.readyState == 4 && xhr.status == 200){
console.log(xhr.responseText);
}
}
xhr.send();
出现如下错误:
XMLHttpRequest cannot load http://www.server.com/data.json. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access.
错误提示中有重要提示“没有'Access-Control-Allow-Origin' header”
,这是一个安全机制,它的作用是防止非同源的交互。因此,服务器需要设置 Access-Control-Allow-Credentials, Access-Control-Allow-Origin 和 Access-Control-Allow-Methods 以允许跨域请求
。
如何解决跨域
1. jsonp跨域
JSONP(JavaScript Object Notation with Padding)使用一个<script>标签发送一个回调函数
到服务端,服务端将一些用JSON表示的数据填入该回调函数中(也就是说服务端返回这个函数的调用,将服务daunt的数据以该函数的参数的形式发送给客户端)
,最后将字符串形式返回客户端。具体实现:
客户端
$.ajax({
url: 'http://www.server.com/data.json',
dataType: 'jsonp',
jsonpCallback: 'callback',
success: function(data){
console.log(data);
},
error: function(XMLHttpRequest, textStatus, errorThrown){
console.log('Error occurred: ' + textStatus);
}
})
服务端:
app.get('/data.jsonp', function(req, res) {
var data={'name':'Tom', 'age':'25'};
var jsonData = JSON.stringify(data);
var callback = req.query.callback; // callback=parseResponse
var resData = callback + '(' + jsonData + ')'; //parseResponse({"name":"Tom","age":"25"})
res.send(resData);
});
JSONP的原理:是利用\<script>标签不受同源策略约束的特点
,通过动态创建并插入\<script>标签来实现跨域请求
。
如下,用<script>标签链接了一个callback函数,服务器返回JSON数据并把数据作为函数的参数传递进去,页面执行callback函数将数据作为参数来处理。
<script type="text/javascript">
function callback(data){
// other code here
}
var myScript = document.createElement("script");
myScript.src = "http://www.server.com/data.json?jsonp=callback";
document.body.appendChild(myScript);
</script>
注意:jsonp只支持get方式发送请求
2. 跨文档消息传递(postMessage)
这个大家可以参考这篇文章postMessage解决iframe内嵌vue项目跨域问题
3. cors设置相应头
下面以CORS为例,详细说明如何实现跨域访问:
- 服务端设置响应头
在服务端的响应中设置以下响应头:
Access-Control-Allow-Origin
:
指定了允许跨域访问的源,可以是单个域名或者 *
代表允许所有域名访问。
Access-Control-Allow-Headers
:
表示允许跨域请求携带的自定义头信息。
Access-Control-Allow-Methods
:
表示允许跨域请求的请求方法。
Access-Control-Allow-Credentials
:
表示跨域请求是否允许携带cookie。
下面是一个允许http://localhost:8080
跨域访问的响应头设置:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:8080
Access-Control-Allow-Headers: X-Requested-With
Access-Control-Allow-Methods: GET,PUT,POST,DELETE,OPTIONS
Access-Control-Allow-Credentials: true
4. 服务端配置代理服务器实现跨域
配置代理服务器实现跨域的一般步骤:
-
创建代理服务器
代理服务器可以使用任何基于Node.js的框架来创建,例如Express.js、Koa.js或Hapi.js。在本例中,我们将使用Express.js。 -
安装跨域包
使用npm安装cors包,这是Express.js的一个中间件,它允许应用程序接受来自不同源的HTTP请求。 -
配置Express.js应用程序
在应用程序中,配置中间件以接受来自任意来源的请求,并将它们重定向到代理服务器。下面是Express.js应用程序的示例代码:
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
app.get('/api/data', (req, res) => {
// 这里是API逻辑,返回数据
});
app.listen(3000, () => {
console.log('服务器运行在端口3000');
});
- 配置代理服务器
代理服务器将请求重定向到另一台Web服务器。在本例中,我们将使用http-proxy-middleware包。安装它并使用以下代码配置代理服务器:
const express = require('express');
const cors = require('cors');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
app.use(cors());
app.use('/api', createProxyMiddleware({
target: 'http://localhost:3000',
changeOrigin: true,
}));
app.listen(4000, () => {
console.log('代理服务器运行在端口4000');
});
在上面的代码中,我们使用createProxyMiddleware
方法创建了一个代理中间件,并将所有以/api开头的请求都重定向到http://localhost:3000
。
现在我们的代理服务器已经配置好了。我们可以将所有来自客户端(例如一个Vue.js或React应用程序)的请求发送到http://localhost:4000/api/data
,代理服务器将它们重定向
到http://localhost:3000/api/data
,然后将响应返回给客户端。
具体来说,对于Vue.js
应用程序,可以在axios
库中设置baseURL
选项来指向代理服务器地址http://localhost:4000
:
// 配置axios库
import axios from 'axios';
axios.defaults.baseURL = 'http://localhost:4000';
// 在组件中使用axios
axios.get('/api/data')
.then(response => console.log(response.data))
.catch(error => console.error(error));
这样就可以在Vue.js应用程序中发送跨域请求了。其他框架的方式类似。
5. vue proxy配置代理跨域
在Vue
项目中,可以通过在vue.config.js
中配置proxy来实现跨域请求。
首先,创建vue.config.js
文件:
// vue.config.js
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000', // 要代理到哪个域名下
changeOrigin: true, // 允许跨域
pathRewrite: {
'^/api': '', // 将请求路径中的/api替换为空
},
},
},
},
};
上述代码表示,如果接口请求路径以/api
开头,那么就会被代理到http://localhost:3000
域名下,从而实现跨域访问。
例如,如果发起如下请求:
axios.get('/api/user')
实际上会被代理成:
axios.get('http://localhost:3000/user')
这样就能够解决跨域问题了。