Cross-Origin Resource Sharing

2025/8/12

CORS(Cross-Origin Resource Sharing,跨域资源共享)是一种基于 HTTP 头部的机制,它允许或限制来自 不同源(origin) 的网页请求访问你的服务器资源。


# 一、什么是“跨域”?

在 Web 中,“源(origin)”是由以下三部分组成的:

  • 协议(如 httphttps
  • 域名(如 example.com
  • 端口(如 :80:443 或其他自定义端口)

如果两个 URL 的协议、域名或端口 任意一个不同,它们就属于 不同源,它们之间的交互就可能存在跨域问题。

# 举个例子:

  • https://example.comhttps://api.example.com不同源(域名不同)
  • http://example.comhttps://example.com不同源(协议不同)
  • http://example.com:80http://example.com:8080不同源(端口不同)

# 二、为什么需要 CORS?

出于 安全考虑,浏览器默认实施了一种策略,叫做 同源策略(Same-Origin Policy, SOP),它阻止一个网页(比如在 http://example.com)直接通过 JavaScript 访问另一个源(比如 http://api.other.com)下的资源(如 API 数据)。

但是,在实际开发中,前端页面和后端 API 往往是分开部署在不同服务器/域名下的,这就需要一种机制来 安全地允许跨域访问,这就是 CORS 的作用


# 三、CORS 是怎么工作的?

当浏览器发现你的前端代码(比如 JavaScript 发起的 fetchXMLHttpRequest 请求)试图访问 不同源 的资源时,它会:

  1. 先发送一个“预检请求”(Preflight Request,如果是非简单请求)

    • 使用 OPTIONS 方法
    • 询问服务器是否允许当前源进行某种跨域请求
  2. 服务器响应中通过特定的 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)

满足以下所有条件时,浏览器不会发送预检请求,而是直接发送请求,并检查响应头是否允许跨域:

  • 请求方法为:GETPOSTHEAD
  • 请求头只包含安全的头部,比如:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type(但仅限:text/plainmultipart/form-dataapplication/x-www-form-urlencoded
  • 不使用自定义请求头

# 2. 非简单请求(Preflight Request)

不满足上述条件的请求(比如用了 PUT 方法、自定义头如 AuthorizationContent-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 是浏览器行为,不是前端代码能绕过的安全限制。 即使你用 fetchaxios 发请求,如果服务器没正确配置 CORS 头部,浏览器依然会阻止响应。

你不能通过前端代码“去掉”跨域限制,必须通过后端允许才行。


# 七、开发时如何绕过 CORS(仅限本地测试)

在本地开发时,你可能会遇到前端(如 http://localhost:3000)访问后端 API(如 http://localhost:8000 或线上地址)时的跨域问题。常见临时解决方案:

  1. 后端设置允许来源(推荐)
  2. 使用代理服务器
    • 比如在 Vue/React 项目中配置 devServer 的 proxy
    • 或使用 Nginx 反向代理
  3. 浏览器禁用 CORS(仅测试用,不安全)
    • 比如 Chrome 启动时加参数 --disable-web-security(不推荐,仅临时调试用)
  4. 使用 CORS 代理服务(如 crossorigin.me、cors-anywhere 等,仅用于测试)

# 八、总结

项目 说明
CORS 是什么? 跨域资源共享,是一种机制,允许浏览器安全地跨源访问资源
为什么需要它? 因为浏览器默认禁止跨域访问,出于安全考虑
谁来配置? 主要是后端服务,通过设置 HTTP 头部来授权跨域
前端能绕过吗? 不能,CORS 是浏览器强制执行的机制
常见头部 Access-Control-Allow-OriginAccess-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 头部,需要后端开发人员添加对应的响应头。