"打破束缚,跨越边界"——浅析JavaScript中的跨域
一、什么是跨域?
在Web前端开发中,跨域是指在浏览器中运行的JavaScript代码尝试访问不同域名、不同端口或不同协议的资源时所面临的安全限制。这种限制是浏览器出于安全考虑而引入的,以防止恶意脚本访问用户的敏感数据。
但是,这种出于安全的考虑也会给开发人员带来一定的麻烦。如图所示,在百度首页的页面下,打开控制台,分别输入以下两个命令去请求数据。
fetch("https://www.baidu.com")
和fetch("https://juejin.cn")
。
我们来看一下具体的报错信息。
Access to fetch at 'juejin.cn/' from origin 'www.baidu.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
翻译一下大概意思就是说:
来自'https://www.baidu.com'
的请求'https://juejin.cn/'
网站数据的行为已被CORS策略阻止:请求的资源上不存在Access Control Allow Origin
头部信息。如果不透明响应满足您的需求,请将请求的模式设置为“无cors
”,以在禁用cors的情况下获取资源。
二、为什么会出现跨域的问题?
上面那个报错是由于违反了浏览器的同源策略(Same-Origin Policy)而引起的,它是为了保护用户的安全而设立的一项安全措施。同源策略要求浏览器限制从一个源(origin)加载的网页或脚本与来自其他源的资源进行交互。
所谓"同源
"指的是"三个相同"。
- 1. 协议相同
- 2. 域名相同
- 3. 端口相同
同源的目的是为了保证用户信息的安全,防止恶意的网站窃取数据。
例如,www.test.com/dir/index.h… 这个网址,协议是http
,域名是www.test.com
, 端口是80
(默认端口可以省略)。它的同源情况如下。
www.test.com/dir2/index2… test.com/dir/index.h… www.test2.com/dir/index.h… www.test.com/dir2/index.… www.test.com:81/dir/index.h…
三、JSONP
JSONP(JSON with Padding)是一种跨域解决方案,它利用script
标签不受同源策略限制的特性来实现跨域数据获取。服务器将数据包装在一个JavaScript回调函数中,并以动态创建的script
标签的形式返回给客户端。
三个允许加载跨域资源的标签。
1. <img src="XXX" > //通常用于引入图片资源
2. <link href="XXX" > // 通常用于引入css文件
3. <script src="XXX" > // 通常用于引入js文件
前端代码index2.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>跨域</title>
</head>
<body>
<h1>我是前端的数据</h1>
<script>
function fn(data){
console.log('@', data);
}
</script>
<script src="http://localhost:3000?callback=fn"></script>
</body>
</html>
从?之后所写的东西表示客户端提供给服务器的额外参数,而这些参数是键值对形式的,用&进行分割。
上面那种写法也可替换成下面这种写法。
<h1>我是前端的数据</h1>
<script>
let script = document.createElement('script');
script.src = "http://localhost:3000?callback=fn"
document.body.appendChild(script);
function fn(data){
console.log('@', data);
}
</script>
后端代码index2.js:
const express = require('express');
const app = express();
app.get('/', (req, res) => {
var fn = req.query.callback;
console.log(req.query);
console.log(req.query.callback);
// 返回跨域请求的响应数据 相当于 fn('我是后端服务器的数据!') 调用执行html里提前定义的函数
res.send(fn + "('我是后端服务器的数据!')");
});
app.listen(3000, () => {
console.log('JSONP项目已在3000端口上启动!');
});
JSONP这种方法相当于是在html代码里提前声明了一个回调函数,但是这个回调函数不会立即执行,等到后端传来数据时再调用触发。
JSONP兼容性好,但有一定的安全风险。JSONP只支持GET
请求,并且需要前后端配合使用。
四、跨域资源共享(CORS)
CORS是一种现代化的跨域解决方案,它通过在服务器端设置响应头来允许跨域请求。服务器在响应中包含Access-Control-Allow-Origin头,指定允许访问资源的域名或通配符*。
CORS全称是"跨域资源共享"(Cross-origin resource sharing)。 它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
index.html文件如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<title>跨域</title>
</head>
<body>
<h1>我是前端的数据</h1>
<button id="btn">请求数据</button>
<script>
let btn = document.getElementById('btn')
btn.addEventListener("click", () => {
$.ajax({
url:'http://localhost:3000',
method: 'get',
headers: {
// 为了告诉后端,你返回的响应头的数据类型应该是xxx
'Content-Type': 'application/json; charset=utf-8'
},
success(res){
console.log('请求成功了!');
console.log(res);
}
})
})
</script>
</body>
</html>
index.js文件如下:
const express = require('express');
const cors = require('cors');
const app = express();
app.use(
cors({
origin: 'http://127.0.0.1:5500',
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']
}
)); // 使用cors中间件
app.get('/', (req, res) => {
// 返回跨域请求的响应数据
res.send('我是后端服务器的数据!');
});
app.listen(3000, () => {
console.log('cors项目已在3000端口上启动!');
});
origin: 'http://127.0.0.1:5500'
这行代码设置了Access-Control-Allow-Origin响应头,表示你只允许特定域名进行访问。如果你希望允许所有来源(即任意域名)的请求访问资源,可以用*
通配符表示允许任何域都能访问资源。
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']
这行代码设置了Access-Control-Allow-Methods响应头,指定允许的HTTP请求方法。在示例中,我们设置了常见的HTTP方法,包括GET、POST、OPTIONS、DELETE和PUT。这表示允许客户端使用这些方法进行跨域请求。
具体的CORS设置可能因服务器框架而异。示例中使用的是Node.js的Express框架,通过调用res.header()方法来设置响应头部。其他服务器框架或语言也有相应的设置方式,但语法和方法可能有所不同。
前端代码相同,使用koa
框架的写法如下。
const Koa = require('koa')
const cors = require('@koa/cors')
const app = new Koa()
app.use(
cors({
origin: 'http://127.0.0.1:5500'
})
)
const main = (ctx) => {
console.log(ctx.query);
console.log(ctx.query.name);
console.log(ctx.query.age);
ctx.body = "这里是后端的数据!!!";
}
app.use(main)
app.listen(3000, () => {
console.log('cors项目已经在3000端口上启动!');
})
五、代理服务器
浏览器有同源策略的限制,而服务器与服务器之间的访问是没有跨域问题的。
通过创建一个代理服务器,可以绕过浏览器的同源策略限制。客户端将请求发送到与自己同一域的服务器,然后由服务器转发请求到目标服务器,并将响应返回给客户端。这样,浏览器认为请求是发往同一域的,就绕过了浏览器的同源策略限制,从而实现跨域请求。有点像通过一个“中间人”
来互相通信。
1.示例一 原生JS实现代理服务器
目标服务器
const Koa = require('koa')
const app = new Koa()
const main = (ctx,next) => {
console.log(ctx.query.name);
ctx.body = '我是后端服务器的数据!'
}
app.use(main)
app.listen(3000, () => {
console.log('目标服务器已在3000端口启动');
})
const http = require('http')
// 创建代理服务器
const server = http.createServer((req,res) => {
// 开启 CORS,设置响应头
res.writeHead(200, {
// 允许跨域,配置同源白名单
"Access-Control-Allow-Origin": "*",
// 允许的请求方法
"Access-Control-Allow-Methods": "GET, POST, OPTIONS, PUT",
// // 允许的请求头部
"Access-Control-Allow-Headers": "Content-Type"
})
// 向其他后端发送请求获取数据
http.request({
host: '127.0.0.1', // 域名
port: '3000', // 端口号
path: '/', // 路径
method: 'GET'
}, proxyRes => {
// 接收代理服务器的响应数据
proxyRes.on('data', result => {
// 将响应数据返回给客户端
res.end(result.toString())
})
}).end()
})
// 代理服务器监听在3001端口
server.listen(3001, () => {
console.log('代理服务器已在3001端口启动');
})
2.示例二 在Vue项目中实现跨域
在Vue项目中实现跨域可以通过配置代理服务器来实现。以下是使用Vue CLI创建的Vue项目中如何跨域的步骤:
首先,安装 http-proxy-middleware 依赖:
npm install http-proxy-middleware --save
在项目根目录下创建一个 vue.config.js 文件,如果已经存在该文件则直接编辑它。
在 vue.config.js 中添加以下代码:
const proxyMiddleware = require('http-proxy-middleware');
module.exports = {
devServer: {
before: function(app) {
app.use(
'/api',
proxyMiddleware({
target: 'http://example.com', // 设置你的目标服务器地址
changeOrigin: true,
pathRewrite: {
'^/api': '' // 如果你的接口路径有特殊前缀,可以在这里进行替换
}
})
);
}
}
};
以上代码通过http-proxy-middleware
中间件在开发服务器启动之前,配置了一个代理规则。该代理规则指定了需要转发的请求路径(/api)以及转发的目标服务器地址http://example.com
。
在你的Vue组件中,可以直接使用相对路径 /api 发起请求。例如:
// 在你的Vue组件中
axios.get('/api/data')
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error(error);
});
通过上述配置,所有以 /api 开头的请求都会被转发到目标服务器地址,并获取返回的数据。
需要注意的是,在开发环境下,这种配置只会影响到开发服务器,而在生产环境下,你需要在后端服务器上进行相关跨域配置。
这样,通过配置代理服务器,就能在Vue项目中实现跨域请求了。代理服务器会帮助你将前端的请求转发到目标服务器,并将响应返回给前端,避免了浏览器的跨域限制。
六、最后的话
跨域大致共有九种左右解决方法,本文只介绍了三种,其他跨域方法,例如基于post message实现跨域处理
、基于iframe的跨域处理
、web-socket和nginx反向代理
,以后有机会再详述。
跨域是Web开发中常见的问题,但通过使用JSONP、CORS或代理服务器等方法,我们可以有效地解决跨域限制,并实现安全的跨域数据交互。根据具体的场景和需求,选择适合的跨域解决方案能够提升开发效率和用户体验。
能力一般,水平有限,本文可能存在纰漏或错误,如有问题欢迎指正(评论或私信),感谢你阅读这篇文章,如果你觉得写得还行的话,不要忘记点赞、评论、收藏哦!祝生活愉快!
转载自:https://juejin.cn/post/7235603140263313465