跨域问题的解决方案

一、跨域问题的本质

1.1 同源策略的三要素

浏览器的同源策略(Same-Origin Policy)要求请求的 协议、域名、端口 完全一致,否则视为跨域:

  • 协议不同httphttps
  • 域名不同a.comb.com
  • 端口不同http://a.com:80http://a.com:8080

1.2 跨域请求的分类

  • 简单请求(Simple Request):
    • HTTP方法:GETPOSTHEAD
    • 请求头:仅允许 AcceptAccept-LanguageContent-Type(且 Content-Type 仅限 text/plainmultipart/form-dataapplication/x-www-form-urlencoded
  • 预检请求(Preflight Request):
    • 当请求包含自定义头(如 Authorization)或使用非简单方法(如 PUTDELETE)时,浏览器会先发送 OPTIONS 请求,询问服务器是否允许该请求。

二、解决方案一:CORS 标准实现

2.1 CORS 工作原理

CORS 通过 预检协商机制 解决跨域问题:

  1. 预检请求(OPTIONS)
    • 浏览器发送 OPTIONS 请求,携带请求方法(Access-Control-Request-Method)和头字段(Access-Control-Request-Headers)。
    • 服务端响应中必须包含允许的 originmethodsheaders,否则浏览器阻止后续请求。
  2. 实际请求
    • 若预检通过,浏览器发送真实请求,并携带 Origin 头。
    • 服务端在响应头中明确允许的 Access-Control-Allow-Origin,浏览器才会将数据返回给前端。
2.2.1 Spring Boot 实现
// 全局CORS配置(application.properties)
spring.mvc.cors.enabled=true
spring.mvc.cors.allow-origins=http://client.example.com
spring.mvc.cors.allow-methods=GET,POST,PUT,DELETE,OPTIONS
spring.mvc.cors.allow-headers=Authorization,Content-Type,X-Requested-With
spring.mvc.cors.exposed-headers=X-Total-Count
spring.mvc.cors.allow-credentials=true
spring.mvc.cors.max-age=3600// 或通过Java配置
@Configuration
public class CorsConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOrigins("http://client.example.com").allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS").allowedHeaders("Authorization", "Content-Type").exposedHeaders("X-Total-Count").allowCredentials(true).maxAge(3600);}
}
2.2.2 预检请求处理(关键点)

Spring Boot 默认会自动处理 OPTIONS 请求,但需确保:

  • allowedMethods 包含 OPTIONS
  • allowedHeaders 包含所有自定义头字段
2.3 安全加固
  • 动态验证来源
    @Bean
    public CorsWebFilter corsFilter() {return (serverWebExchange, chain) -> {String origin = serverWebExchange.getRequest().getHeaders().getOrigin();if (allowedOrigins.contains(origin)) {// 设置CORS头serverWebExchange.getResponse().getHeaders().add("Access-Control-Allow-Origin", origin);return chain.filter(serverWebExchange);}return Mono.error(new AccessDeniedException("Invalid origin"));};
    }
    

三、解决方案二:JSONP

3.1 JSONP 工作原理

JSONP 利用 <script> 标签的跨域特性,通过动态注入脚本实现数据回传:

  1. 前端动态注入
    • 创建 <script> 标签,src 指向服务端接口,附加 callback 参数。
  2. 服务端封装数据
    • 将数据包装在 callback 函数中,返回类似 handleResponse({data: "value"}) 的字符串。
  3. 前端执行回调
    • 浏览器解析脚本,执行 handleResponse 函数,获取数据。
3.2 后端代码
@RestController
public class JsonpController {@GetMapping("/jsonp")public String handleJsonp(@RequestParam String callback // 接收前端传递的回调函数名) {// 生成模拟数据Map<String, Object> data = new HashMap<>();data.put("name", "Alice");data.put("age", 25);// 防XSS攻击:校验callback参数格式if (!callback.matches("^[a-zA-Z0-9_]+$")) {throw new IllegalArgumentException("Invalid callback parameter");}// 将数据封装到回调函数中return callback + "(" + new Gson().toJson(data) + ")";}
}
3.3 优缺点对比
优点缺点
无需服务端配置CORS仅支持GET请求
兼容性好(支持旧浏览器)存在XSS风险(需严格校验callback)
实现简单不支持复杂认证(如JWT)

四、解决方案三:Nginx反向代理

4.1 反向代理原理

Nginx 作为反向代理,将客户端请求转发到后端服务,使浏览器认为请求与当前页面同源:

  1. 请求转发
    • 客户端请求 http://frontend.com/api/data
    • Nginx 将请求转发到 http://backend.com:3000/data
  2. 响应头伪造
    • Nginx 可修改响应头,如 Access-Control-Allow-Origin,使浏览器认为请求是同源的。
4.2 配置示例(支持WebSocket与HTTPS)
server {listen 443 ssl;server_name frontend.com;ssl_certificate /path/to/cert.pem;ssl_certificate_key /path/to/key.pem;location /api/ {# 反向代理到后端proxy_pass http://backend:3000;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;# CORS配置add_header Access-Control-Allow-Origin $http_origin;add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";add_header Access-Control-Allow-Headers "Authorization";add_header Access-Control-Allow-Credentials "true";# WebSocket支持proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";}# 处理OPTIONS预检location ~ ^/api/ {if ($request_method = 'OPTIONS') {add_header 'Access-Control-Allow-Origin' '$http_origin';add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';add_header 'Access-Control-Max-Age' 86400;return 204;}}
}
4.3 安全加固
  • 限制来源
    map $http_origin $allowed_origin {default '';~^(http://client\.example\.com|https://another\.domain\.com)$ $http_origin;
    }server {add_header Access-Control-Allow-Origin $allowed_origin always;if ($allowed_origin = '') {return 403;}
    }
    

五、解决方案四:API网关(微服务场景)

5.1 API网关核心原理

API网关作为微服务的统一入口,集中处理跨域、认证、限流等逻辑:

  1. 集中配置CORS
    • 在网关层统一设置 Access-Control-Allow-Origin,避免每个微服务重复配置。
  2. 路由与安全策略
    • 根据请求路径路由到对应服务,同时执行身份验证(如JWT校验)。
5.2 Spring Cloud Gateway 实现
// 配置全局CORS
@Bean
public CorsWebFilter corsFilter() {CorsConfiguration config = new CorsConfiguration();config.setAllowedOrigins(List.of("http://client.example.com"));config.setAllowedMethods(List.of("GET", "POST"));config.setAllowedHeaders(List.of("Authorization"));UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", config);return new CorsWebFilter(source);
}// 动态路由配置
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {return builder.routes().route("user-service", r -> r.path("/api/user/**").filters(f -> f.addRequestHeader("X-Forwarded-Proto", "https")).uri("lb://user-service")).build();
}
5.3 优势与适用场景
优点适用场景
集中式管理跨域与安全策略微服务架构、需要统一鉴权的系统
支持复杂路由与负载均衡高并发、多服务交互的场景

六、解决方案五:代理服务器(Node.js示例)

6.1 代理服务器原理

代理服务器(如Nginx、Node.js)接收客户端请求,转发到目标服务,并修改响应头以绕过跨域限制。

6.2 Node.js实现(http-proxy-middleware)
// proxy.config.js
module.exports = {'/api': {target: 'http://backend.example.com:3000',changeOrigin: true,onProxyReq: (proxyReq, req, res) => {// 动态修改请求头proxyReq.setHeader('X-Forwarded-For', req.ip);proxyReq.setHeader('X-Real-IP', req.ip);},onProxyRes: (proxyRes, req, res) => {// 添加CORS头proxyRes.headers['Access-Control-Allow-Origin'] = req.headers.origin;}}
};
6.3 安全建议
  • 限制来源
    const allowedOrigins = ['http://client.example.com'];
    if (!allowedOrigins.includes(req.headers.origin)) {return res.status(403).send('Forbidden');
    }
    

七、解决方案六:服务器端渲染(SSR)

7.1 SSR原理

在服务器端生成完整HTML页面,直接返回给浏览器,避免浏览器发起跨域AJAX请求。

7.2 Next.js 实现
// pages/index.js
export async function getServerSideProps() {const res = await fetch('http://api.example.com/data', {headers: {Authorization: 'Bearer YOUR_TOKEN'}});const data = await res.json();return { props: { data } };
}export default function Home({ data }) {return <div>{JSON.stringify(data)}</div>;
}
7.3 优势与局限
优点局限
首屏加载快、SEO友好仅适用于静态或半动态页面
无跨域问题开发复杂度较高

八、方案选择决策树

场景推荐方案原因技术栈
单页应用(SPA)开发Nginx反向代理 / 代理服务器开发与生产环境统一配置,避免前后端分离的复杂性Node.js, Nginx
微服务架构API网关统一处理集中式管理,支持动态路由与权限控制Spring Cloud Gateway, Kong
旧项目兼容第三方APIJSONP无需后端改造,快速集成Vanilla JS
需要严格安全控制CORS标准实现 + 白名单细粒度配置,支持所有HTTP方法Spring Boot, Express.js
WebSocket跨域Nginx反向代理 + WebSocket支持需要处理Upgrade头和Connection头Nginx
服务端渲染(SSR)服务器端直接请求避免浏览器发起跨域请求Next.js, Nuxt.js

九、常见问题与最佳实践

9.1 预检请求(OPTIONS)的深度处理

  • 问题:当请求包含自定义头或使用非简单方法(如PUT/DELETE)时,浏览器会先发送OPTIONS请求。
  • 解决方案
    • 在后端显式返回 Access-Control-Allow-MethodsAccess-Control-Allow-Headers
    • 对OPTIONS请求返回204 No Content状态码
9.1.1 Spring Boot示例
@Configuration
public class CorsConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS").allowedHeaders("Authorization", "Content-Type", "X-Requested-With");}
}

9.2 安全性建议

  • 避免使用 *allowCredentials 同时开启
    // 错误配置
    app.use(cors({ origin: '*', credentials: true }));
    
  • 限制 allowedOrigins 为可信域名列表
    allowedOrigins: ["http://client.example.com", "https://another-domain.com"]
    
  • 对敏感接口启用CSRF防护
    app.use(csrf());
    app.use((req, res, next) => {res.cookie('XSRF-TOKEN', req.csrfToken());next();
    });
    

十、扩展知识点

10.1 WebSocket跨域解决方案

通过Nginx配置支持WebSocket:

location /ws/ {proxy_pass http://backend-ws.example.com;proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";add_header Access-Control-Allow-Origin $http_origin;
}

10.2 跨域Cookie处理

  • 前端设置
    fetch('http://api.example.com', {credentials: 'include' // 允许携带Cookie
    });
    
  • 后端配置
    add_header Set-Cookie "SameSite=None; Secure"; // HTTPS下强制跨域Cookie
    

十一、总结

跨域问题的解决需要结合项目架构、安全需求与开发效率综合考量。CORS作为标准方案应优先采用,而Nginx、API网关等则适用于复杂场景。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/73685.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux 上使用 Docker 部署 Kafka 集群

在 Linux 上使用 Docker 部署 Kafka 集群的步骤如下 1. 准备工作 确保已安装&#xff1a; Docker Docker Compose 2. 创建 Docker Compose 文件 (docker-compose.yml) version: 3.8services:zookeeper:image: wurstmeister/zookeepercontainer_name: zookeeperports:- &quo…

【性能优化点滴】odygrd/quill 中一个简单的标记位作用--降低 IO 次数

在 StreamSink 类中&#xff0c;成员变量 _write_occurred 的作用是 跟踪自上次刷新&#xff08;Flush&#xff09;以来是否有写入操作发生&#xff0c;其核心目的是 优化 I/O 性能。以下是详细解析&#xff1a; _write_occurred 的作用 1. 避免不必要的刷新&#xff08;Flush…

Ubuntu Linux安装PyQt5并配置Qt Designer

一 安装 PyQt5 借助 apt 包管理器来安装 PyQt5 及其相关的开发工具&#xff1a; sudo apt install python3-pyqt5 pyqt5-dev-tools 假如报错&#xff0c; You might want to run apt --fix-broken install to correct these. 直接执行&#xff1a; sudo apt --fix-…

2025清华大学:DeepSeek教程全集(PDF+视频精讲,共10份).zip

一、资料列表 第一课&#xff1a;Deepseek基础入门 第二课&#xff1a;DeepSeek赋能职场 第三课&#xff1a;普通人如何抓住DeepSeek红利 第四课&#xff1a;让科研像聊天一样简单 第五课&#xff1a;DeepSeek与AI幻觉 第六课&#xff1a;基于DeepSeek的AI音乐词曲的创造法 第…

容器C++

string容器 string构造函数 #include<iostream> using namespace std; #include<string.h> void test01() {string s1;//默认构造const char* str "hello world";string s2(str);//传入char*cout << "s2" << s2 << endl;s…

【2.项目管理】2.4 Gannt图【甘特图】

甘特图&#xff08;Gantt&#xff09;深度解析与实践指南 &#x1f4ca; 一、甘特图基础模板 项目进度表示例 工作编号工作名称持续时间(月)项目进度&#xff08;周&#xff09;1需求分析3▓▓▓░░░░░░░2设计建模3░▓▓▓░░░░░░3编码开发3.5░░░▓▓▓▓░░…

C++List模拟实现|细节|难点|易错点|全面解析|类型转换|

目录 1.模拟代码全部 2.四大块代码理解 1.最底层&#xff1a;ListNode部分 2.第二层&#xff1a;ListIterator部分 3.第三层&#xff1a;ReserveListIterator部分 4最终层&#xff1a;List 1.模拟代码全部 using namespace std; template<class T> struct ListNode …

【深度学习与实战】2.1、线性回归模型与梯度下降法先导

import numpy as np# 数据准备 X np.array([1, 2, 3]) y np.array([3, 5, 7])# 参数初始化 w0, w1 0, 0 alpha 0.1 n len(X)# 迭代10次 for epoch in range(10):# 计算预测值y_pred w1 * X w0# 计算梯度grad_w0 (1/n) * np.sum(y_pred - y)grad_w1 (1/n) * np.sum((y_…

锐捷EWEB路由器 timeout.php任意文件上传漏洞代码审计(DVB-2025-9003)

免责声明 仅供网络安全研究与教育目的使用。任何人不得将本文提供的信息用于非法目的或未经授权的系统测试。作者不对任何由于使用本文信息而导致的直接或间接损害承担责任。如涉及侵权,请及时与我们联系,我们将尽快处理并删除相关内容。 一:产品介绍 锐捷EWEB路由器是锐…

flask开发中设置Flask SQLAlchemy 的 db.Column 只存储非负整数(即 0 或正整数)

如果你想控制一个 Flask SQLAlchemy 的 db.Column 只存储非负整数&#xff08;即 0 或正整数&#xff09;&#xff0c;你可以在模型中使用验证来确保这一点。一种常见的方法是使用模型的 validate 方法或者在执行插入或更新操作时进行检查。 以下是实现这一目标的几种方法&…

sqlmap 源码阅读与流程分析

0x01 前言 还是代码功底太差&#xff0c;所以想尝试阅读 sqlmap 源码一下&#xff0c;并且自己用 golang 重构&#xff0c;到后面会进行 ysoserial 的改写&#xff1b;以及 xray 的重构&#xff0c;当然那个应该会很多参考 cel-go 项目 0x02 环境准备 sqlmap 的项目地址&…

vscode连接服务器失败问题解决

文章目录 问题描述原因分析解决方法彻底删除VS Code重新安装较老的版本 问题描述 vscode链接服务器时提示了下面问题&#xff1a; 原因分析 这是说明VScode版本太高了。 https://code.visualstudio.com/docs/remote/faq#_can-i-run-vs-code-server-on-older-linux-distribu…

企业网站源码HTML成品网站与网页代码模板指南

在当今数字化时代&#xff0c;企业网站已成为展示品牌形象、吸引客户和提供在线服务的重要工具。对于许多企业来说&#xff0c;使用现成的HTML网站源码模板是快速搭建网站的高效方式。本文将详细介绍企业网站源码、HTML成品网站以及网页代码模板的相关内容&#xff0c;帮助你快…

计算机网络 - OSI 七层模型

OSI 七层模型 OSI&#xff08;Open System Interconnection&#xff0c;开放系统互联&#xff09;模型由 ISO&#xff08;国际标准化组织&#xff09; 制定&#xff0c;目的是为不同计算机网络系统之间的通信提供一个标准化的框架。它将网络通信划分为 七个层次&#xff0c;每…

flutter-实现瀑布流布局及下拉刷新上拉加载更多

文章目录 1. 效果预览2. 结构分析3. 完整代码4. 总结 1. 效果预览 在 Flutter 应用开发中&#xff0c;瀑布流布局常用于展示图片、商品列表等需要以不规则但整齐排列的内容。同时&#xff0c;下拉刷新和上拉加载更多功能&#xff0c;能够极大提升用户体验&#xff0c;让用户方…

在 Ubuntu 下通过 Docker 部署 Nginx 服务器

1. Docker 和 Nginx 简介以及实验环境 Docker 是一个开源的容器化平台&#xff0c;允许开发者将应用程序及其依赖项打包成一个轻量级的、可移植的容器。通过 Docker&#xff0c;开发者可以在任何支持 Docker 的环境中运行应用&#xff0c;从而实现一致的开发和生产环境。Docke…

IoT平台实时监测机器人状态的实现方案

通过IoT平台实时监测机器人状态的实现方案与可执行路径 一、整体架构设计 #mermaid-svg-6xMlDfFSZM4Wc8tA {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-6xMlDfFSZM4Wc8tA .error-icon{fill:#552222;}#mermaid-sv…

mybatis里in关键字拼接id问题

我们一般会把ids集合用StrUtil.join(‘,’)转成"1,2,3"这种形式 然后放入in中 我们会这么写: select id, nick_name, icon from tb_user where id in (#{ids}) order by FIELD(id, #{ids})结果发现sql执行是这样的: select id, nick_name, icon from tb_user where…

4.用 Excel 录入数据

一 用 Excel 录入数据的两种方式 用鼠标键盘录入数据和从网上爬取数据。 二 用鼠标键盘录入数据 1.录入数据的规范 横着录入数据&#xff08;横着一条条录入数据&#xff09;。 2.使用快捷键进行数据录入 tab 键和 enter 键。 tab 键&#xff1a;向右移动一个单元格。 tab 键…

C++类与对象-3.23笔记

今天学习了类的概述和写类的基本框架 在哔哩哔哩学习的这个老师的C面向对象高级语言程序设计教程&#xff08;118集全&#xff09;讲的很不错&#xff08;真的&#xff01;&#xff01;&#xff01;&#xff09;&#xff0c;C语言也是在这个老师的带领下学习的 #include<io…