什么是跨域,后端工程师如何处理跨域

本文探讨了什么是跨域,以及同源策略的概念。详细解释了浏览器的同源策略限制,包括对非同源请求的限制。文章重点介绍了如何处理跨域问题,特别是针对 AJAX 请求,讲解了 CORS(跨域资源共享)机制的工作原理,包括简单请求和非简单请求的处理,并提供了服务端处理 CORS 的示例。最后,给出了调试跨域问题的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是跨域

前言

作为一名后端开发工程师,在给前端同事写接口的时候,经常碰到他们讲,你的接口跨域了,那么什么是跨域,这里来研究下。

什么是跨域

先来看下跨域的定义

跨域的广义定义:跨域是指一个域下的文档或脚本试图去请求另一个域下的资源。

我们经常遇到的跨域是由浏览器同源策略限制的一类请求场景。

栗如,下面的请求就发生了跨域,在京东的 H5 页面中请求淘宝的接口

上面栗子中跨域最终的罪魁祸首就是浏览器的同源策略。

1、因为上面的域名不相同,所以请求的接口被认为是非同源。

2、同时出于安全性,浏览器限制脚本内发起的跨源 HTTP 请求。 例如,XMLHttpRequest 和 Fetch API 遵循同源策略。所以上面的请求就报了跨域的错误。

同源策略

什么是同源策略

同源策略/SOP (Same origin policy)是一种约定,由 Netscape 公司1995年引入浏览器。

同源策略是指在 Web 浏览器中,允许某个网站脚本访问另一个网站的数据,但前提是这两个网站必须满足三个相同:

1、协议相同;

2、域名相同;

3、端口相同;

一旦两个网站满足上述条件,这两个网站就被认定为具有相同来源。

非同源的限制条件

如果两个网站非同源,将受到下面的几种限制

1、Cookie、LocalStorage 和 IndexDB 无法读取;

2、DOM 无法获得;

3、AJAX 请求不能发送。

同源策略的目的

同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。

栗如:Cookie 中存了一些用户的登陆信息,如果没有同源策略的限制,那么任何网站都能访问 Cookie,用户的信息就会泄露了,就能伪造用户信息登陆目标网站了,这显然是有很大的安全隐患的。

如何处理跨域

因为非同源有上面三种限制,这种能够规避一定的安全问题,但是有时候我们正常的使用也受到了影响,所以有时候我们需要想办法规避这种限制。

非同源限制中的

1、Cookie、LocalStorage 和 IndexDB 无法读取;

2、DOM 无法获得;

这里就不展开讨论了,这种只需要在前端页面中进行调整就行了,这里重点关注下有和后端交互的 3、AJAX 请求不能发送 的这种限制。

处理 AJAX 非同源的限制

同源政策规定,AJAX 请求只能发给同源的网址,否则就报错。

除了架设服务器代理(浏览器请求同源服务器,再由后者请求外部服务),有三种方法规避这个限制。

1、JSONP

2、WebSocket

3、CORS

关于 WebSocket 和 JSONP 的处理参见浏览器同源政策及其规避方法

作为服务端开发,对于 CORS 使用的比较多,这里展开讨论下

CORS

CORS 是一个 W3C 标准,全称是"跨域资源共享"(Cross-origin resource sharing)。

它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了 AJAX 只能同源使用的限制。

跨源资源共享 (CORS)(或通俗地译为跨域资源共享)是一种基于 HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其它 origin(域,协议和端口),使得浏览器允许这些 origin 访问加载自己的资源。跨源资源共享还通过一种机制来检查服务器是否会允许要发送的真实请求,该机制通过浏览器发起一个到服务器托管的跨源资源的"预检"请求。在预检中,浏览器发送的头中标示有 HTTP 方法和真实请求中会用到的头。

实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

CORS 的使用的关键在服务端,浏览器发送请求,服务端接收到客户端请求做一些判断(请求方是否在自己的“白名单”里?),如果没问题就返回数据,否则拒绝。

浏览器将 CORS 请求分成两类:

简单请求(simple request)

非简单请求(not-so-simple request)

<think>好的,我现在需要帮助用户了解如何使用SseEmitter实现服务器发送事件(SSE)并接收数据的示例。首先,我得回忆一下SSE的基本概念和SseEmitter的作用。SSE允许服务器主动向客户端推送数据,常用于实时更新,比如股票行情或实时通知。而SseEmitter是Spring框架中用于处理SSE的一个类。 接下来,用户需要一个具体的示例,所以我应该分步骤来展示后端的实现和前端的使用。首先,后端部分需要创建一个Controller,使用@GetMapping映射到一个路径,并返回SseEmitter对象。这里需要处理问题,可能需要添加@CrossOrigin注解。然后,在方法内部,创建一个SseEmitter实例,设置超时时间,比如设置为0表示永超时。 然后,需要在一个单独的线程中发送数据,模拟实时推送。这里可以用ExecutorService来调度任务,每隔一段时间发送数据。发送数据时,使用emitter.send()方法,并处理可能的IOException。完成后调用emitter.complete()。 在前端部分,需要使用EventSource对象连接到后端端点,监听message事件来接收数据,并处理错误和完成事件。这里要注意问题,如果前端和后端同源,需要正确配置CORS。 另外,需要注意的几个点:确保Spring Boot版本支持SseEmitter,一般是2.0以上;超时设置要根据实际需求调整;在生产环境中可能需要更完善的错误处理和资源管理,比如关闭线程池。 用户可能还会关心如何传递具体的数据格式,比如发送JSON数据,可以在send方法中传递对象,Spring会自动转换为JSON字符串。此外,异常处理部分也很重要,比如客户端断开连接后,后端需要捕获异常并释放资源。 最后,可能需要生成相关问题,比如SSE与WebSocket的区别、如何保证消息顺序、如何处理大规模并发连接等,这有助于用户进一步学习。 现在需要把这些思路整理成结构化的步骤,确保代码示例正确,并且注释清晰。同时,引用用户提供的参考内容,虽然看起来和SSE关系大,但可能涉及异步支持,所以可以提到AbstractDispatcherServletInitializer的异步配置,确保DispatcherServlet的异步支持已启用,因为SseEmitter依赖于此。</think>以下是通过Spring Boot的SseEmitter实现SSE的详细步骤和示例: ### 一、后端实现 ```java @RestController @CrossOrigin // 解决问题 public class SseController { private final ExecutorService executor = Executors.newCachedThreadPool(); @GetMapping("/sse-stream") public SseEmitter streamData() { SseEmitter emitter = new SseEmitter(0L); // 设置永超时 executor.execute(() -> { try { for (int i = 1; i <= 10; i++) { // 构建带时间戳的消息 String jsonPayload = String.format( "{\"count\": %d, \"timestamp\": \"%s\"}", i, LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) ); emitter.send( SseEmitter.event() .id(String.valueOf(i)) .name("progress") .data(jsonPayload) ); Thread.sleep(1000); // 每秒发送一次 } emitter.complete(); } catch (IOException | InterruptedException e) { emitter.completeWithError(e); } }); // 客户端断开连接处理 emitter.onCompletion(() -> System.out.println("SSE连接已完成")); emitter.onTimeout(() -> System.out.println("SSE连接超时")); emitter.onError((ex) -> System.out.println("SSE发生错误: " + ex.getMessage())); return emitter; } } ``` 关键配置说明: 1. 异步支持需在DispatcherServlet中启用,Spring Boot默认开启异步处理[^1] 2. 使用`@CrossOrigin`解决问题 3. 通过`SseEmitter.event()`构建结构化事件 ### 二、前端实现 ```javascript const eventSource = new EventSource('http://localhost:8080/sse-stream'); eventSource.addEventListener('progress', (event) => { const data = JSON.parse(event.data); console.log(`收到进度更新: 序号: ${event.lastEventId} 计数: ${data.count} 时间: ${data.timestamp}`); }); eventSource.onerror = (err) => { console.error('SSE连接异常:', err); eventSource.close(); }; eventSource.onopen = () => { console.log('SSE连接已建立'); }; ``` ### 三、运行效果 ``` SSE连接已建立 收到进度更新: 序号:1 计数:1 时间:2023-03-15T14:30:01.123 收到进度更新: 序号:2 计数:2 时间:2023-03-15T14:30:02.456 ... 收到进度更新: 序号:10 计数:10 时间:2023-03-15T14:30:10.789 SSE连接已完成 ``` ### 四、进阶配置建议 1. **安全增强**:添加JWT认证头 ```javascript const eventSource = new EventSource('/sse-stream', { withCredentials: true }); ``` 2. **心跳机制**:防止连接超时 ```java emitter.send(SseEmitter.event().comment("heartbeat")); ``` 3. **异常恢复**:实现断线重连 ```javascript function connect() { const es = new EventSource(...); es.onerror = () => { setTimeout(connect, 5000); // 5秒后重连 } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值