微服务下的跨域问题

在单体架构时代,跨域问题还不算突出;但进入微服务、前后端分离、多端统一时代,跨域几乎是每个项目必踩的坑。尤其在微服务架构下,网关、认证、分布式部署、多域名并存,让跨域变得更复杂、更隐蔽。

本文从浏览器同源策略讲起,结合微服务真实场景,把跨域原理、常见方案、网关统一处理、避坑要点一次性讲透,适合直接发布 CSDN 学习与收藏。


一、什么是跨域?为什么会出现跨域?

1.1 同源策略(Same-Origin Policy)

跨域限制,本质是浏览器的安全机制,服务器本身不存在跨域限制。

同源 = 协议 + 域名 + 端口 三者完全一致只要有一个不一样,浏览器就判定为 "跨域",会拦截响应,前端拿不到数据。

举例(以 http://a.com:8080 为基准):

表格

请求地址 是否跨域 原因
http://a.com:8080/api 完全同源
https://a.com:8080/api 协议不同(http/https)
http://api.a.com:8080 域名不同
http://a.com:9090/api 端口不同

1.2 为什么微服务更容易跨域?

微服务天生就是多应用、多域名、多端口、多实例部署:

  • 前端:http://localhost:8080
  • 网关:http://gateway:8888
  • 用户服务:http://user:8001
  • 订单服务:http://order:8002

只要前端直接 / 间接访问非同源地址,就会触发跨域。

典型报错:

plaintext

复制代码
Access to XMLHttpRequest at 'xxx' from origin 'xxx' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.

二、跨域核心知识点:简单请求 & 预检请求

2.1 简单请求

满足以下全部条件为简单请求:

  • 方法:GET / HEAD / POST
  • 请求头只包含:Accept、Accept-Language、Content-Type
  • Content-Type 仅限:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

特点:只发一次请求,直接携带跨域头。

2.2 预检请求(OPTIONS)

不满足简单请求,就会触发预检

  • 自定义请求头(如 token
  • Content-Type: application/json
  • 使用 PUT / DELETE / PATCH

浏览器会先发一次 OPTIONS 请求,询问服务器:

  • 允许哪些来源?
  • 允许哪些方法?
  • 允许哪些头?

服务器通过后,才发送真实请求。

微服务常见坑:后端只处理了真实请求,没处理 OPTIONS,导致跨域一直失败。


三、微服务架构下跨域的常见解决方案

方案 1:前端代理(开发环境专用)

Vue / React / Angular 都支持 devServer.proxy

原理:前端请求自己 → 代理转发到后端 → 浏览器认为同源

示例(vue.config.js):

js

复制代码
devServer: {
  proxy: {
    '/api': {
      target: 'http://localhost:8888',
      changeOrigin: true
    }
  }
}

适用:

  • 仅限开发环境
  • 生产环境不能用

方案 2:单个服务配置 CORS(不推荐微服务)

在每个微服务中单独配置跨域。

Spring Boot 示例:

java

运行

复制代码
@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("GET","POST","PUT","DELETE","OPTIONS")
                .allowCredentials(true)
                .maxAge(3600);
    }
}

缺点:

  • 每个服务都要写一遍
  • 网关、认证、服务之间不一致
  • 维护爆炸,不适合微服务

方案 3:网关统一处理跨域(微服务标准方案 ✅)

微服务最佳实践网关统一处理跨域,下游微服务关闭跨域配置。

常见网关:

  • Spring Cloud Gateway
  • GatewayNet / Ocelot(.NET)
  • Nginx
  • Kong / APISIX

Spring Cloud Gateway 配置跨域示例

yaml

复制代码
spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowedOrigins: "*"
            allowedMethods: "*"
            allowedHeaders: "*"
            allowCredentials: true
            maxAge: 3600

优点:

  • 统一配置,一处修改全局生效
  • 避免下游服务重复配置
  • 完美适配微服务

方案 4:Nginx 反向代理(生产常用)

前端 → Nginx → 网关 → 微服务

Nginx 配置:

plaintext

复制代码
location /api/ {
    proxy_pass http://gateway:8888/;
    add_header Access-Control-Allow-Origin *;
}

适合:

  • 对外正式环境
  • 多域名、多端统一接入

方案 5:JSONP(淘汰)

只支持 GET,不安全,微服务基本不用。

方案 6:前端与后端同域名部署(最彻底)

前端打包后放到后端静态资源目录,或同域名 Nginx 代理。同源自然无跨域。


四、微服务跨域高频坑点(重点!)

前端开启 withCredentials:

js

复制代码
axios.defaults.withCredentials = true

后端必须:

  • Access-Control-Allow-Origin 不能为 *
  • 必须配置具体域名
  • allowCredentials = true

4.2 OPTIONS 预检请求被拦截

  • 认证过滤器(JWT/Security)拦截 OPTIONS
  • 网关鉴权优先于跨域配置

解决:放行所有 OPTIONS 请求,不做认证

4.3 跨域头重复

网关配置了跨域 + 微服务也配置了跨域导致响应头重复,浏览器报错。

原则:网关处理跨域,下游服务禁止再配置 CORS。

4.4 开放 * 导致线上安全风险

测试可用 *,生产必须指定具体域名。


五、微服务跨域最佳实践总结

  1. 开发环境:前端代理
  2. 微服务架构:网关统一处理 CORS
  3. 生产环境:Nginx 反向代理 + 网关鉴权
  4. 认证场景:禁止 Origin=*,使用具体域名 + allowCredentials
  5. OPTIONS 请求:统一放行,不参与鉴权
  6. 避免重复配置:只在网关一层处理跨域

六、结语

跨域不是技术难题,而是架构规范问题。在微服务里,只要记住一句话:

跨域统一在网关处理,下游服务只负责业务,不关心跨域。

这套方案能适配:Spring Cloud、.NET 微服务、Go 微服务、K8s 集群、Service Mesh 等所有现代架构。

相关推荐
却话巴山夜雨时i2 小时前
Java面试实录:从Spring Boot到Kafka的技术探讨
spring boot·微服务·kafka·grafana·prometheus·java面试
Chuncheng's blog2 小时前
K8S二进制部署exec unable to upgrade connection: Unauthorized异常解决方案
云原生·容器·kubernetes
DoUfp0bgq3 小时前
Admin.NET开源版微服务改造记录
微服务·开源·.net
FJW0208143 小时前
HAProxy+Keepalived实现Kubernetes高可用集群部署
云原生·容器·kubernetes
倔强的胖蚂蚁3 小时前
云原生服务器存储规划与磁盘选型实施
运维·服务器·云原生
观无3 小时前
微服务架构核心技术知识全景总结
微服务·云原生·架构
那我懂你的意思啦3 小时前
微服务学习+商城
学习·微服务·架构
Mintopia3 小时前
技术人如何清晰表达:把复杂问题讲简单
架构