跨域问题解决方案
文章目录
一、JSONP
- 当浏览器不支持CORS时可以用该方式解决,该方式支持get请求,它通过动态创建script标加载包含回调函数的脚本,以实现跨域数据交换。
- 由于script标签不受浏览器同源政策的限制,所以可以用来从不同域名的服务器上请求数据。
通用JSONP函数
// 封装一个通用的JSONP函数
function jsonp(url, params = {}) {
// 创建一个唯一的回调函数名
const callbackName = `jsonp_callback_${Date.now()}`;
// 创建一个script元素
const script = document.createElement('script');
// 将回调函数名添加到请求参数中
params.callback = callbackName;
// 将请求参数拼接到URL中
const queryString = new URLSearchParams(params).toString();
url = `${url}?${queryString}`;
// 创建一个Promise对象来处理请求结果
const promise = new Promise((resolve, reject) => {
// 定义回调函数
window[callbackName] = (data) => {
// 清除回调函数
delete window[callbackName];
// 移除script元素
script.remove();
// 解析JSONP响应数据
resolve(data);
};
// 处理请求错误
script.onerror = (error) => {
// 移除script元素
script.remove();
// 拒绝Promise并返回错误信息
reject(error);
};
});
// 设置script元素的src属性
script.src = url;
// 将script元素添加到页面上
document.body.appendChild(script);
// 返回Promise对象
return promise;
}
使用
// 在mounted钩子中调用JSONP函数
mounted() {
jsonp('https://example.com/api/data', { param1: 'value1', param2: 'value2' })
.then(data => {
// 处理JSONP请求返回的数据
console.log(data);
})
.catch(error => {
// 处理请求错误
console.error(error);
});
}
二、CROSE
- 同源策略(Same-OriginPolicy):浏览器的同源策略是一种安全机制,限制了来自不同源的客户端脚本在不受信任的情况下对当前文档的操作。同源策略要求文档只能与同一域的资源进行交互,以防止恶意脚本窃取用户数据。
- CORS(跨域资源共享):CORS是一种机制,允许服务器在响应HTTP头中声明哪些源可以访问其资源。在服务器端配置响应头,指定允许访问的源,方法和标头。前端使用XMLHttpRequest或Fetch
API时,浏览器会自动执行预检请求(OPTIONS请求),以确定是否允许进行实际请求 - CORS解决方案:CORS是一种机制,通过在服务器的HTTP响应头中添加特定的CORS头,来告知浏览器当前文档是否允许跨域访问。当浏览器收到跨域请求时,会先发起一个预检请求(Preflight
Request),以确认是否允许真正的请求。 - CORS头信息:服务器通过在响应头中设置以下CORS头来控制跨域访问:
Access-Control-Allow-Origin:指定允许访问的域。可以是单个域名、多个域名或通配符(*)。
Access-Control-Allow-Methods:指定允许的HTTP方法。
Access-Control-Allow-Headers:指定允许的HTTP头信息。
Access-Control-Allow-Credentials:指定是否允许发送凭据(如Cookie)。
1.简单请求
满足条件
-
使用GET、HEAD或POST(请求头Content-Type:text/plain, multipart/form-data,
application/x-www-form-urlencoded)。 -
请求头包含特定字段(Accept、Accept-Language、Content-Language、Content-Type、DPR、Downlink、Save-Data、Viewport-width、Width)
代码如下(示例):
// 发起简单请求
fetch('http://example.com/api/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('请求失败:', error));
交互规范
- 请求头会自动添加Origin字段、Origin字段会告诉服务器、哪个源地址在跨域请求
- 服务器响应头中包含Access-Control-Allow-Origin、该字段的值可以是:*表示开放,什么人服务器这边都允许访问、具体的源:比如http://example.com/api/data、表示只允许该地址访问
2.预检请求
预检请求通常用于检查跨域请求的安全性。
-
浏览器发送预检请求(OPTIONS方法),包含实际请求的方法、自定义头部等信息。
-
服务器收到预检请求后,检查请求的来源、方法和头部,并在响应中设置相应的CORS头部,如:
Access-Control-Allow-Origin
Access-Control-Allow-Methods
Access-Control-Allow-Headers
Access-Control-Max-Age(指定预检请求结果的有效期) -
浏览器根据预检请求的响应决定是否发送实际请求。
代码如下(示例):
// 发起预检请求
fetch('http://example.com/api/data', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123'
}
});
3.复杂请求
不符合简单请求标准的请求,如使用其他HTTP方法或自定义头。
代码如下(示例):
// 发起简单请求
fetch('http://example.com/api/data', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123'
}
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('请求失败:', error));
三、代理
在服务器端设置代理,将客户端的请求发送到目标服务器,并返回响应。这种方法在前端代码中不需要任何更改,但需要额外的服务器资源来处理代理请求。
四、WebSocket
WebSocket是一种全双工通信协议,可在客户端和服务器之间建立持久连接。由于WebSocket是通过HTTP/HTTPS协议升级而来,因此不受同源策略的限制,可以实现跨域通信。
五、服务器端设置代理头
服务器端设置代理头:有些服务器可以配置代理头(如Nginx),将来自其他域的请求通过特定路径转发到目标服务器。这种方法使得客户端感知不到跨域的存在,但需要服务器端的额外配置。
下面是Nginx设置代理头的详细步骤:
1、编辑Nginx配置文件:
Nginx的配置文件通常位于/etc/nginx/nginx.conf或/etc/nginx/sites-available/目录下的特定站点配置文件中。你需要编辑相应的配置文件以设置代理头。
2、配置代理服务器:
在配置文件中,你需要设置一个代理服务器块,指定要代理的请求的路径和目标服务器的地址。
2、设置代理头:
使用proxy_set_header指令来设置或修改代理请求的HTTP头部。对于跨域问题,你通常需要设置Origin、Host和Referer等头部。
代码如下(示例):
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend-server; # 后端服务器的地址
proxy_set_header Host $host; # 传递原始的Host头部
proxy_set_header X-Real-IP $remote_addr; # 传递客户端的IP地址
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 传递经过的代理服务器列表
proxy_set_header X-Forwarded-Proto $scheme; # 传递使用的协议(HTTP/HTTPS)
# 解决跨域问题
add_header 'Access-Control-Allow-Origin' '*'; # 允许所有源
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS'; # 允许的HTTP方法
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; # 允许的HTTP头部
add_header 'Access-Control-Allow-Credentials' 'true'; # 是否发送Cookie
add_header 'Access-Control-Max-Age' '86400'; # 预检请求的缓存时间
# 如果是OPTIONS请求,直接返回204 No Content
if ($request_method = 'OPTIONS') {
return 204;
}
}
}
六、跨文档消息传递
通过window.postMessage()方法在不同窗口、标签页或iframe之间传递数据。使用这种方法可以在不同域之间进行安全的跨域通信。
代码如下(示例):
// postMessageHelper.js
class PostMessageHelper {
constructor(targetWindow, targetOrigin) {
this.targetWindow = targetWindow; // 目标窗口的引用
this.targetOrigin = targetOrigin; // 目标窗口的源
this.listeners = []; // 存储事件监听器以便之后清理
}
// 发送消息
sendMessage(message) {
this.targetWindow.postMessage(message, this.targetOrigin);
}
// 添加消息接收监听器
addMessageListener(callback) {
const listener = (event) => {
if (event.origin === this.targetOrigin) { // 安全检查
callback(event.data);
}
};
window.addEventListener('message', listener, false);
this.listeners.push(listener); // 存储监听器引用
}
// 移除所有监听器
removeListeners() {
this.listeners.forEach(listener => {
window.removeEventListener('message', listener, false);
});
this.listeners = [];
}
}
// 使用示例
const iframe = document.getElementById('myIframe');
const pmHelper = new PostMessageHelper(iframe.contentWindow, 'http://example.com');
// 发送消息
pmHelper.sendMessage('Hello from parent!');
// 添加接收消息的监听器
pmHelper.addMessageListener((data) => {
console.log('Received message:', data);
});
// 当不再需要通信时,可以清理监听器
// pmHelper.removeListeners();
七、使用反向代理
将跨域的请求转发到同一个域下,然后由服务器端发起真实请求。这种方法需要服务器端配置反向代理规则。
八、关闭浏览器跨域校验
··················
总结
待续··················