瞧瞧别人家的跨域方案,那叫一个优雅!

开发 后端
很多小伙伴第一次遇到跨域问题,大概率会一脸懵逼:“我后端接口明明通了,Postman也能调,为啥浏览器就报红字?”

1 什么是跨域问题?

很多小伙伴第一次遇到跨域问题,大概率会一脸懵逼:“我后端接口明明通了,Postman也能调,为啥浏览器就报红字?”

图片图片

其实这事儿得怪浏览器的“同源策略”(Same-Origin Policy)。

简单说,浏览器觉得“不同源的请求都是耍流氓”。

比如你的前端跑在http://localhost:8080。

而后端在https://api.xxx.com:8000。

只要协议域名端口任何一个不同,就会被浏览器直接掐断。

举个栗子🌰:

// 前端代码(http://localhost:8080)
fetch('http://api.xxx.com:8000/user')
  .then(res => res.json())
  .then(data => console.log(data));  
// 浏览器控制台报错:  
// Access to fetch from 'http://localhost:8080' has been blocked by CORS policy...

这时候,你就需要“跨域解决方案”来帮浏览器松绑了!

那么,如何解决跨域问题呢?

2 解决跨域问题的方案

2.1 CORS(跨域资源共享)

适用场景:前后端分离项目、接口需要兼容多种客户端。

CORS是W3C标准,后端只需在响应头里加几个字段,告诉浏览器“这个接口我允许谁访问”。

后端代码示例(Spring Boot版):

// 方法1:直接怼注解(适合单个接口)
@CrossOrigin(origins = "http://localhost:8080")
@GetMapping("/user")
public User getUser() { ... }

// 方法2:全局配置(一劳永逸)
@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("http://localhost:8080")
                .allowedMethods("*")
                .allowedHeaders("*")
                .allowCredentials(true)
                .maxAge(3600);
    }
}

关键响应头:

  • Access-Control-Allow-Origin: http://localhost:8080(允许的源)
  • Access-Control-Allow-Methods: GET,POST(允许的方法)
  • Access-Control-Allow-Credentials: true(允许带Cookie)

注意坑点:

  • 如果用了allowCredentials(true)allowedOrigins不能为*(必须明确指定域名)。
  • 复杂请求(比如Content-Type是application/json)会先发一个OPTIONS预检请求,记得处理!

2.2 JSONP

适用场景:老项目兼容、只支持GET请求(比如调用第三方地图API)。

JSONP利用**<script>标签没有跨域限制**的特性,让后端返回一段JS代码。

前端代码:

function handleUserData(data) {
    console.log("收到数据:", data);
}

// 动态创建script标签
const script = document.createElement('script');
script.src = 'http://api.xxx.com:8000/user?callback=handleUserData';
document.body.appendChild(script);

后端代码:

@GetMapping("/user")
public String jsonp(@RequestParam String callback) {
    User user = new User("Tony", 30);
    // 把数据包进回调函数里
    return callback + "(" + new Gson().toJson(user) + ")";
}

输出结果:

handleUserData({"name":"Tony","age":30})

缺点:

  • 只支持GET(传参长度有限)。
  • 容易被XSS攻击(毕竟得信任第三方脚本)。

2.3 Nginx反向代理

适用场景:生产环境部署、微服务网关统一处理。

直接把跨域问题甩给Nginx,让浏览器以为所有请求都是同源的。

Nginx配置示例:

server {
    listen 80;
    server_name localhost;

    location /api {
        # 转发到真实后端
        proxy_pass http://api.xxx.com:8000;
        # 解决跨域
        add_header 'Access-Control-Allow-Origin' 'http://localhost:8080';
        add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'Content-Type';
    }
}

此时前端请求地址改成同源:

fetch('/api/user')  // 实际访问 http://localhost/api/user → 被Nginx转发

优点:

  • 前后端代码零侵入。
  • 能隐藏真实接口地址(安全加分)。

2.4 网关层统一处理

适用场景:Spring Cloud Gateway、Kong等API网关。

和Nginx思路类似,但更适合微服务场景,直接在网关层加CORS配置。

Spring Cloud Gateway配置:

spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowed-origins: "http://localhost:8080"
            allowed-methods: "*"
            allowed-headers: "*"
            allow-credentials: true

2.5 WebSocket

适用场景:实时通信需求(聊天室、股票行情)。

WebSocket协议没有跨域限制(因为握手阶段走HTTP,后续升级为长连接)。

前端代码:

const socket = new WebSocket('ws://api.xxx.com:8000/ws');
socket.onmessage = (event) => {
    console.log('收到消息:', event.data);
};

后端代码(Spring Boot):

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(new MyWebSocketHandler(), "/ws");
    }
}

2.6 PostMessage

适用场景:页面与iframe、弹窗之间的跨域通信。

通过window.postMessage实现不同窗口间的数据传递。

父页面(http://parent.com):

const childWindow = window.open('http://child.com');
childWindow.postMessage('我是你爹', 'http://child.com');

子页面(http://child.com):

window.addEventListener('message', (event) => {
    if (event.origin !== 'http://parent.com') return; // 验证来源
    console.log('收到爹的消息:', event.data);
});

总结

  • 简单粗暴:开发环境用CORS注解。
  • 生产环境:优先Nginx/网关统一处理,避免每个服务配一遍。
  • 老项目兼容:JSONP勉强能用,但别长期依赖。
  • 实时场景:直接上WebSocket,顺便解决通信问题。
  • 安全第一:Access-Control-Allow-Origin尽量别写*,白名单要用精确域名。

最后提醒温馨提醒一下:跨域问题本质是浏览器行为,和HTTP协议无关。

如果你用Postman,发送curl请求,测试没问题,但浏览器报错,别怀疑人生,这可能是前端的锅!


责任编辑:武晓燕 来源: 苏三说技术
相关推荐

2024-11-12 08:20:31

2024-10-24 08:21:33

2022-12-12 08:14:47

2024-12-02 00:59:30

Spring

2020-11-03 16:00:33

API接口微服务框架编程语言

2015-09-24 09:22:16

nodejs页面始末

2017-11-12 21:32:52

戴尔

2016-01-08 09:49:19

DockerDocker案例云应用开发

2020-11-17 09:34:31

API接口后端

2017-09-22 13:22:59

大数据南京大学宿舍

2023-12-30 20:04:51

MyBatis框架数据

2021-07-14 06:31:08

京东互联网加薪

2021-01-20 05:42:27

RabbitMQMQ vhost

2017-06-13 14:15:51

戴尔协同计算汽车神话

2019-06-11 09:35:50

戴尔

2017-12-25 11:24:57

程序员代码编程

2022-03-01 09:31:06

JWTSession跨域

2022-09-26 19:10:45

Kafka架构

2021-12-09 11:31:16

跨域后端开发

2024-05-20 09:28:44

Spring客户端浏览器
点赞
收藏

51CTO技术栈公众号