什么是跨域问题
跨域问题是指在浏览器上运行的Web应用程序试图通过XMLHttpRequest或Fetch API等方式向不同源(域名、协议或端口)的服务器发送请求时,浏览器会根据同源策略阻止这种行为。同源策略是一种安全机制,用于限制来自不同源的页面对当前页面的访问。它可以防止恶意的网站通过跨域请求获取用户的个人信息或进行未授权操作。
同源策略要求请求的协议、域名和端口号必须完全相同才被认为是同源。也就是说只要协议、域名和端口号,任何一项与当前页面的协议、域名、端口号不一致,就会被浏览器阻止。在这种情况下,浏览器会抛出一个跨域错误,导致请求失败。
跨域是浏览器行为。实际上我们发出的请求已经到达服务器了,但是服务器返回数据时被浏览器限制了
如何解决
一、JSONP:利用 script 标签的跨域特性,通过动态创建 script 标签并设置其 src 属性为跨域的 URL,服务器端返回的响应数据需要用特定的格式包裹起来,并通过回调函数返回给客户端。
客户端代码:
js
function handleResponse(response) {
//处理服务器返回的数据
}
var script = document.createElement('script');
script.src = 'https://jsonp.com/data?callback=handleResponse';
document.body.appendChild(script);
服务端代码:
js
var data = { name: 'John', age: 30 };
var jsonpResponse = 'handleResponse(' + JSON.stringify(data) + ');';
res.send(jsonpResponse);
关于JSONP的补充
- JSONP只支持
GET
请求,不支持POST
等其他HTTP方法。 - 因为JSONP请求是通过动态创建
script
标签实现的,所以需要确保被请求的数据源返回的是JSONP格式的数据,而不是普通的JSON格式,否则在处理时会出现语法错误。 - 由于JSONP请求是通过
script
标签实现的,所以无法在请求时设置请求头(例如Content-Type),也无法获取响应头(例如Cookie),这是JSONP与XHR等其他跨域请求方式的一个区别。
二、CORS(跨域资源共享):在服务器端设置相应的响应头,允许跨域请求。通常在服务器端设置 Access-Control-Allow-Origin 头部,指定允许的来源域名,即可实现跨域请求的许可。CORS 支持各种 HTTP 请求方法,并且更加灵活和安全。
客户端代码:
js
fetch('http://CORS.com/api/data', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
mode: 'cors',
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error))
服务端代码:
js
const express = require('express');
const app = express();
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
next();
});
app.get('/api/data', (req, res) => {
const data = { message: 'Hello, World!' };
res.json(data);
});
app.listen(3000, () => {
console.log('Server listening on port 3000');
});
优点:
- 安全性高:CORS 是一种安全的跨域访问解决方案,通过限制允许跨域访问的源和方法,可以有效地防止恶意攻击。
- 灵活性强:CORS 支持不同类型的请求,包括 GET、POST、PUT、DELETE 等,同时也支持不同类型的数据传输格式,比如 JSON、XML 等。
- 使用方便:CORS 只需要在服务器端设置响应头,就可以实现跨域访问,使用方便。
不足:
- 兼容性问题:CORS 在某些旧版的浏览器中不支持,需要进行特殊处理。
- 跨域请求的额外消耗:在使用 CORS 解决跨域请求时,需要发送预检请求,这会增加请求的时间和带宽消耗。
- CSRF 攻击:虽然 CORS 是一种安全的跨域访问解决方案,但仍然可能存在 CSRF(Cross-Site Request Forgery)攻击,需要在使用时加以注意。可以使用 Cookie、Token 或者请求头中的特定信息来验证请求是否合法。
三、代理服务器:在同源策略限制下,可以通过在同域名下的服务器上设置一个代理服务器,将客户端请求转发到目标服务器,再将相应的结果返回给客户端。客户端只需要与代理服务器通信,而不是直接与目标服务器通信,间接实现了跨域请求。
案例一:
1、需求:使用axios接收server1.js的服务
2、实现:
App.vue
js
<template>
<div>
<button @click="getStudents">获取学生信息</button>
</div>
</template>
<script>
import axios from 'axios'
export default {
name:'App',
methods:{
getStudents(){
//注意:开启代理服务器后,get中的端口号要改为前端所在的端口号,即8080
axios.get('http://localhost:8080/students').then(
response => {
console.log('请求成功了',response.data)
},
error => {
console.log('请求失败了',error.message)
}
)
},
},
}
</script>
vue.config.js(这里要先关闭vue,写完后在重启,不然配置更改不会生效)
js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
// 关闭语法检查
lintOnSave: false,
// 开启代理服务器,注意:这里的端口号写后端的端口号(方式一)
devServer: {
proxy: 'http://localhost:5000'
},
})
案例二:
1、需求:使用axios接收server1.js和server2.js的服务
2、实现:
- App.vue
js
<template>
<div>
<button @click="getStudents">获取学生信息</button>
<button @click="getCars">获取汽车信息</button>
</div>
</template>
<script>
import axios from 'axios'
export default {
name:'App',
methods:{
getStudents(){
//注意:采用了写法二,要加上前缀 /atguigu
axios.get('http://localhost:8080/atguigu/students').then(
response => {
console.log('请求成功了',response.data)
},
error => {
console.log('请求失败了',error.message)
}
)
},
getCars(){
//注意:采用了写法二,要加上前缀 /demo
axios.get('http://localhost:8080/demo/cars').then(
response => {
console.log('请求成功了',response.data)
},
error => {
console.log('请求失败了',error.message)
}
)
},
},
}
</script>
- vue.config.js
js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
// 关闭语法检查
lintOnSave: false,
// 开启代理服务器,注意:这里的端口号写后端的端口号(方式一)
// devServer: {
// proxy: 'http://localhost:5000'
// },
// 开启代理服务器(方式二)
devServer: {
proxy: {
// /atguigu 是请求的前缀
'/atguigu': {
target: 'http://localhost:5000',
//重写路径,把所有路径中包含/atguigu的路径替换为空字符串
pathRewrite: {'^/atguigu':''},
// 用于支持websocket
ws: true,
// 用于控制请求头中的host值
changeOrigin: true
},
'/demo': {
target: 'http://localhost:5001',
//重写路径,把所有路径中包含/atguigu的路径替换为空字符串
pathRewrite: {'^/demo':''},
// 用于支持websocket
ws: true,
// 用于控制请求头中的host值
changeOrigin: true
},
}
}
})
四、WebSocket:WebSocket 是一种基于 TCP 协议的全双工的通信协议,它不受同源策略的限制,在建立连接后,客户端与服务器之间可以直接进行双向通信。因此,可以利用 WebSocket 实现跨域通信。
总结 跨域问题是在Web开发中常遇到的挑战之一。当页面上的JavaScript代码尝试通过XMLHttpRequest或Fetch API等方式向不同域名、协议或端口的服务器发送请求时,浏览器会根据同源策略(Same-Origin Policy)阻止这种行为。解决跨域问题有多种方法,常用的包括JSONP、CORS、代理服务器等。
选择不同的解决方案取决于项目需求和后端服务的支持情况。通过正确的解决方案,可以保证数据通信的安全性和稳定性,提高开发效率和用户体验。跨域问题是web开发中不可忽视的问题,了解解决方案对于前端开发者至关重要。