跨域问题

一、跨域问题原因

跨域问题源于浏览器的同源策略。同源策略是一种安全策略,它限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。所谓同源,是指域名、协议和端口号都相同。

例如,当一个网页的脚本试图访问另一个域名下的资源(如通过 XMLHttpRequestfetch 请求其他域名的接口)、不同协议(如 http 请求访问 https 资源)或者不同端口(如在 localhost:8080 下请求 localhost:3000 的资源)时,浏览器就会阻止这种跨域请求,以防止恶意网站窃取用户数据等安全问题。

二、跨域问题场景

前后端分离场景

当前端项目部署在一个域名(例如 www.frontend.com)下,后端接口部署在另一个域名(如 api.backend.com)下,前端通过 JavaScript 脚本请求后端接口时就会出现跨域问题。

不同子域名下资源访问场景

比如企业内部有 sub1.example.comsub2.example.com 两个子域名,分别对应不同的应用,当 sub1.example.com 下的脚本想访问 sub2.example.com 下的资源时,也会遇到跨域限制。

三、跨域问题解决方案

JSONP(JSON with Padding)

原理

它利用了 <script> 标签没有域跨限制的特性。其基本原理是动态创建一个 <script> 标签,然后将这个标签的 src 属性设置为要请求的跨域 URL,同时在 URL 中包含一个回调函数名的参数。服务器收到请求后,会将数据作为回调函数的参数返回,这样就可以在客户端执行这个回调函数,从而实现跨域数据获取。

场景

主要适用于跨域获取数据,并且只支持 GET 请求的场景,比如一些 API 接口只允许 GET 请求获取数据,并且可以接受 JSONP 格式的请求。

示例

前端代码:

ini 复制代码
function handleData(data) {
    console.log(data);
}
​
var script = document.createElement('script');
script.src = 'http://api.example.com/data?callback=handleData';
document.body.appendChild(script);

服务器端返回:

erlang 复制代码
handleData({"name": "John", "age": 30});

优点

实现相对简单,不需要服务器进行复杂的配置,只要服务器能够支持 JSONP 请求格式即可。

缺点

只支持 GET 请求,不能处理其他类型的 HTTP 请求方法,如 POSTPUT 等。并且对数据格式有一定的要求,需要服务器返回特定格式的 JSONP 响应。

CORS(Cross - Origin Resource Sharing)

原理

它是目前主流的解决跨域问题的方法。它通过服务器返回特定的 HTTP 头来告诉浏览器,允许某个外部域名的脚本访问资源。浏览器在发起跨域请求时,会先检查服务器返回的 CORS 相关头信息,如 Access - Control - Allow - Origin(指定允许哪些源访问资源)、Access - Control - Allow - Methods(指定允许的 HTTP 请求方法)等,如果符合要求,就会允许请求。

场景

适用于各种跨域场景,包括前后端分离、不同子域名下资源访问等,几乎可以满足所有正常的跨域请求需求。

示例

服务器返回的响应头:

yaml 复制代码
Access - Control - Allow - Origin: http://localhost:3000
Access - Control - Allow - Methods: GET, POST, PUT, DELETE
Access - Control - Allow - Headers: Content - Type

前端使用 fetch 发起请求:

javascript 复制代码
fetch('http://api.example.com/data', {
    method: 'GET',
    headers: {
        'Content - Type': 'application/json'
    }
})
.then(response => {
    return response.json();
})
.then(data => {
    console.log(data);
});

优点

功能强大,可以灵活地指定允许的源、方法、请求头等,而且可以支持各种类型的 HTTP 请求方法。它是一种标准化的跨域解决方案,得到了广泛的支持。

缺点

需要服务器端进行配置,对于一些没有权限修改服务器配置的情况可能无法使用。而且,在开发过程中,可能会因为一些复杂的 CORS 配置问题导致请求失败,需要仔细排查服务器返回的头信息。

代理服务器

原理

通过在同源服务器(通常是后端服务器)上设置一个代理,前端请求先发送到这个代理服务器,代理服务器再将请求转发到目标跨域服务器,并将返回的响应转发给前端。因为对于前端来说,请求是发送到了同源的代理服务器,所以不存在跨域问题。

场景

适用于比较复杂的跨域场景,尤其是在企业内部,当后端可以方便地搭建代理服务器时。或者在开发环境中,用于解决本地开发环境与后端服务器跨域的问题。

示例

假设前端在 http://localhost:8080,后端接口在 http://api.example.com,在 http://localhost:8080 上搭建一个代理服务器。

使用 Node.js 的 http - proxy - middleware 搭建代理:

php 复制代码
const { createProxyMiddleware } = require('http - proxy - middleware');
module.exports = function (app) {
    app.use(
        '/api',
        createProxyMiddleware({
            target: 'http://api.example.com',
            changeOrigin: true,
            pathRewrite: {
                '^/api': ''
            }
        })
    );
};

前端请求:

ini 复制代码
fetch('/api/data')
.then(response => {
    return response.json();
})
.then(data => {
    console.log(data);
});

优点

可以完全避免跨域问题,因为请求看起来都是同源的。并且可以对请求进行一些额外的处理,如添加请求头、修改响应内容等。

缺点

增加了系统的复杂性,需要搭建和维护代理服务器。而且可能会带来性能开销,因为增加了请求的中间转发环节。

document.domain 方式(适用于不同子域名下的跨域通信)

原理

在浏览器中,可以通过设置 document.domain 属性来改变文档的域。一般来说,这个属性默认是完整的域名,但如果两个页面属于同一个父域名,例如 sub1.example.comsub2.example.com,我们可以通过设置 document.domain = 'example.com' 来使它们看起来属于同一个域,从而实现一定程度的跨域通信。

场景

主要适用于不同子域名下的页面之间的通信,比如在一个企业网站的不同子域名下的页面之间共享一些简单的数据。

示例

sub1.example.com 页面中:

ini 复制代码
document.domain = 'example.com';
window.parent.postMessage('Hello from sub1', 'http://sub2.example.com');

sub2.example.com 页面中:

javascript 复制代码
document.domain = 'example.com';
window.addEventListener('message', function (event) {
    // 只接受来自 example.com 域的消息
    if (event.origin !== 'http://sub1.example.com') return;
    console.log(event.data);
}, false);

优点

实现相对简单,对于子域名之间的通信比较有效,并且可以直接操作 DOM 等元素。

缺点

只能用于同父域名下不同子域名的通信,并且只能对 document.domain 进行设置,设置后的域必须是父域名,而且会带来一定的安全风险,因为两个子域名下的页面可以互相访问。

四、跨域解决方案总结

解决方案 适用场景 优点 缺点
JSONP 跨域获取数据,仅支持 GET 请求 实现简单,无需复杂服务器配置 仅支持 GET 请求,要求服务器返回特定格式的 JSONP 响应
CORS 各种跨域场景 功能强大,支持多种 HTTP 方法,标准化解决方案 需要服务器端配置,配置错误可能导致请求失败
代理服务器 复杂跨域场景,企业内部或开发环境 完全避免跨域问题,可额外处理请求和响应 增加系统复杂性和性能开销,需搭建和维护代理服务器
document.domain 同父域名下不同子域名通信 实现简单,可直接操作 DOM 仅适用于同父域名下不同子域名,存在安全风险
相关推荐
harrain1 小时前
什么!vue3.4开始,v-model不能用在prop上
前端·javascript·vue.js
fanruitian6 小时前
uniapp android开发 测试板本与发行版本
前端·javascript·uni-app
rayufo6 小时前
【工具】列出指定文件夹下所有的目录和文件
开发语言·前端·python
RANCE_atttackkk7 小时前
[Java]实现使用邮箱找回密码的功能
java·开发语言·前端·spring boot·intellij-idea·idea
2501_944525548 小时前
Flutter for OpenHarmony 个人理财管理App实战 - 支出分析页面
android·开发语言·前端·javascript·flutter
李白你好9 小时前
Burp Suite插件用于自动检测Web应用程序中的未授权访问漏洞
前端
刘一说10 小时前
Vue 组件不必要的重新渲染问题解析:为什么子组件总在“无故”刷新?
前端·javascript·vue.js
徐同保11 小时前
React useRef 完全指南:在异步回调中访问最新的 props/state引言
前端·javascript·react.js
刘一说11 小时前
Vue 导航守卫未生效问题解析:为什么路由守卫不执行或逻辑失效?
前端·javascript·vue.js
一周七喜h12 小时前
在Vue3和TypeScripts中使用pinia
前端·javascript·vue.js