Cross-Origin Resource Sharing
CORS(Cross-Origin Resource Sharing,跨域资源共享)是一种基于 HTTP 头部的机制,它允许或限制来自 不同源(origin) 的网页请求访问你的服务器资源。
# 一、什么是“跨域”?
在 Web 中,“源(origin)”是由以下三部分组成的:
- 协议(如
http
或https
) - 域名(如
example.com
) - 端口(如
:80
、:443
或其他自定义端口)
如果两个 URL 的协议、域名或端口 任意一个不同,它们就属于 不同源,它们之间的交互就可能存在跨域问题。
# 举个例子:
https://example.com
和https://api.example.com
→ 不同源(域名不同)http://example.com
和https://example.com
→ 不同源(协议不同)http://example.com:80
和http://example.com:8080
→ 不同源(端口不同)
# 二、为什么需要 CORS?
出于 安全考虑,浏览器默认实施了一种策略,叫做 同源策略(Same-Origin Policy, SOP),它阻止一个网页(比如在 http://example.com
)直接通过 JavaScript 访问另一个源(比如 http://api.other.com
)下的资源(如 API 数据)。
但是,在实际开发中,前端页面和后端 API 往往是分开部署在不同服务器/域名下的,这就需要一种机制来 安全地允许跨域访问,这就是 CORS 的作用。
# 三、CORS 是怎么工作的?
当浏览器发现你的前端代码(比如 JavaScript 发起的 fetch
或 XMLHttpRequest
请求)试图访问 不同源 的资源时,它会:
先发送一个“预检请求”(Preflight Request,如果是非简单请求)
- 使用
OPTIONS
方法 - 询问服务器是否允许当前源进行某种跨域请求
- 使用
服务器响应中通过特定的 HTTP 头部来声明是否允许跨域
- 如果服务器允许,浏览器才会真正发送你想要的请求(比如
GET
/POST
数据请求) - 如果服务器不允许,浏览器就会阻止前端代码拿到响应数据,即使服务器实际返回了正确内容
- 如果服务器允许,浏览器才会真正发送你想要的请求(比如
# 四、常见的 CORS 相关 HTTP 头部
# 1. 由服务器返回,用来授权跨域访问:
头部字段 | 说明 |
---|---|
Access-Control-Allow-Origin | 表示允许哪些源访问资源,比如: - https://example.com - * (允许任意源,但不推荐用于有敏感信息的接口) |
Access-Control-Allow-Methods | 允许的 HTTP 方法,如 GET, POST, PUT, DELETE |
Access-Control-Allow-Headers | 允许的自定义请求头,比如 Content-Type, Authorization |
Access-Control-Allow-Credentials | 是否允许发送 cookies 等凭证信息(需与前端对应设置) |
Access-Control-Max-Age | 预检请求结果的缓存时间(单位:秒) |
# 2. 由浏览器自动发送(在预检请求中):
Origin
:表示请求来源的源,如https://example.com
Access-Control-Request-Method
:将要使用的 HTTP 方法Access-Control-Request-Headers
:将要使用的自定义请求头
# 五、简单请求 vs 非简单请求
CORS 请求分为两类:
# 1. 简单请求(Simple Request)
满足以下所有条件时,浏览器不会发送预检请求,而是直接发送请求,并检查响应头是否允许跨域:
- 请求方法为:
GET
、POST
或HEAD
- 请求头只包含安全的头部,比如:
Accept
Accept-Language
Content-Language
Content-Type
(但仅限:text/plain
、multipart/form-data
、application/x-www-form-urlencoded
)
- 不使用自定义请求头
# 2. 非简单请求(Preflight Request)
不满足上述条件的请求(比如用了 PUT
方法、自定义头如 Authorization
或 Content-Type: application/json
),浏览器会先发送一个 OPTIONS
预检请求,询问服务器是否允许该跨域请求。
# 六、如何启用 CORS?
# 1. 后端设置(推荐)
你需要在服务器端响应中添加合适的 CORS 头部,常见方式:
Node.js (Express) 可使用
cors
中间件:const express = require('express'); const cors = require('cors'); const app = express(); // 允许所有源访问 app.use(cors()); // 或者限制特定源 app.use(cors({ origin: 'https://example.com', methods: ['GET', 'POST'], allowedHeaders: ['Content-Type', 'Authorization'] }));
1
2
3
4
5
6
7
8
9
10
11
12
13在 Spring Boot 中,要配置允许所有源(即允许任意域名)的跨域请求(CORS),可以通过以下几种方式实现:
使用
@CrossOrigin
注解(适用于单个控制器或方法)全局 CORS 配置(推荐,统一管理)
通过
WebMvcConfigurer
实现(Spring Boot 2.x 推荐)import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class CorsConfig { @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") // 对所有接口都有效 .allowedOrigins("*") // 允许所有源(生产环境慎用!) .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的 HTTP 方法 .allowedHeaders("*") // 允许所有请求头 .allowCredentials(false) // 是否允许发送 cookie,若为 true,则 allowedOrigins 不能为 * .maxAge(3600); // 预检请求的缓存时间(秒) } }; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23安全提示:
allowedOrigins("*")
表示允许任何网站访问你的 API,这在开发阶段可以接受,但在生产环境中强烈不建议,应该明确指定允许的域名,如https://example.com
。- 如果你设置
allowCredentials(true)
(比如需要传递 cookies 或使用 JWT 等身份凭证),则 不能使用allowedOrigins("*")
,必须指定具体的域名,如allowedOrigins("https://yourdomain.com")
。
Spring Boot 3.x / Spring 6 推荐方式(基于
CorsConfigurationSource
)import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; @Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.addAllowedOriginPattern("*"); // Spring 6 推荐使用 allowedOriginPatterns 替代 allowedOrigins("*") config.setAllowCredentials(false); // 是否允许凭证 config.addAllowedMethod("*"); // 允许所有方法 config.addAllowedHeader("*"); // 允许所有请求头 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); // 对所有路径生效 return new CorsFilter(source); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23推荐使用
allowedOriginPatterns("*")
(Spring 6+ 支持),它是allowedOrigins("*")
的升级,更灵活且安全。如果你的项目使用了 Spring Security,那么你还需要在 Security 配置中允许 CORS,否则 Security 过滤器可能会拦截跨域请求。
import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.web.SecurityFilterChain; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import java.util.List; @Configuration public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .cors(cors -> cors.configurationSource(corsConfigurationSource())) // 启用自定义 CORS 配置 .csrf(csrf -> csrf.disable()) // 根据需求决定是否禁用 CSRF .authorizeHttpRequests(auth -> auth .anyRequest().permitAll() // 示例:允许所有请求,生产环境请按需配置 ); return http.build(); } @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOriginPatterns(List.of("*")); // 或指定域名 configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); configuration.setAllowedHeaders(List.of("*")); configuration.setAllowCredentials(false); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
其他语言/框架(如 Java Spring、Python Django、PHP、.NET 等)也都有相应的 CORS 配置方式,核心是设置上述 HTTP 头部。
# 2. 前端无法绕过 CORS
注意:CORS 是浏览器行为,不是前端代码能绕过的安全限制。 即使你用 fetch
或 axios
发请求,如果服务器没正确配置 CORS 头部,浏览器依然会阻止响应。
你不能通过前端代码“去掉”跨域限制,必须通过后端允许才行。
# 七、开发时如何绕过 CORS(仅限本地测试)
在本地开发时,你可能会遇到前端(如 http://localhost:3000
)访问后端 API(如 http://localhost:8000
或线上地址)时的跨域问题。常见临时解决方案:
- 后端设置允许来源(推荐)
- 使用代理服务器
- 比如在 Vue/React 项目中配置 devServer 的 proxy
- 或使用 Nginx 反向代理
- 浏览器禁用 CORS(仅测试用,不安全)
- 比如 Chrome 启动时加参数
--disable-web-security
(不推荐,仅临时调试用)
- 比如 Chrome 启动时加参数
- 使用 CORS 代理服务(如 crossorigin.me、cors-anywhere 等,仅用于测试)
# 八、总结
项目 | 说明 |
---|---|
CORS 是什么? | 跨域资源共享,是一种机制,允许浏览器安全地跨源访问资源 |
为什么需要它? | 因为浏览器默认禁止跨域访问,出于安全考虑 |
谁来配置? | 主要是后端服务,通过设置 HTTP 头部来授权跨域 |
前端能绕过吗? | 不能,CORS 是浏览器强制执行的机制 |
常见头部 | Access-Control-Allow-Origin 、Access-Control-Allow-Methods 等 |
预检请求 | 针对复杂请求,浏览器先发 OPTIONS 询问服务器是否允许 |
如果你有具体的 CORS 错误信息,比如:
Access to fetch at '...' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource
那通常说明你的后端没有正确配置 CORS 头部,需要后端开发人员添加对应的响应头。