SpringBoot中6种跨域请求解决方案

06-01 1937阅读

文章首发公众号【风象南】

一、跨域问题简介

在Web开发中,浏览器的同源策略(Same-Origin Policy)是一项重要的安全机制,它限制了一个源(Origin)中加载的文档或脚本如何与另一个源的资源进行交互。所谓同源,指的是协议、域名和端口号都相同。当前端应用试图请求与自身不同源的后端API时,就会遇到跨域问题。

例如,当 http://frontend.com 的前端应用尝试访问 http://backend.com/api 的后端服务时,浏览器会阻止这种请求,并在控制台报错:

Access to XMLHttpRequest at 'http://backend.com/api' from origin 'http://frontend.com' 
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

跨域资源共享(CORS,Cross-Origin Resource Sharing)是一种标准机制,允许服务器声明哪些源可以访问其资源。在SpringBoot应用中,有多种方式可以解决跨域问题,下面详细介绍6种常见的解决方案。

二、方案一:基于@CrossOrigin注解的方法级别控制

这是最简单直接的方式,通过在Controller类或特定方法上添加@CrossOrigin注解来允许跨域请求。

实现方式

// 在方法级别允许跨域
@RestController
@RequestMapping("/api")
public class UserController {
    
    @CrossOrigin(origins = "http://example.com")
    @GetMapping("/users")
    public List getUsers() {
        // 方法实现
        return userService.findAll();
    }
    
    @GetMapping("/roles")
    public List getRoles() {
        // 此方法不允许跨域
        return roleService.findAll();
    }
}
// 在类级别允许跨域
@CrossOrigin(origins = {"http://example.com", "http://localhost:3000"})
@RestController
@RequestMapping("/api/products")
public class ProductController {
    
    @GetMapping
    public List getAllProducts() {
        // 方法实现
        return productService.findAll();
    }
    
    @GetMapping("/{id}")
    public Product getProduct(@PathVariable Long id) {
        // 方法实现
        return productService.findById(id);
    }
}

优点

  • 实现简单直观
  • 可以精确控制到方法级别
  • 可以针对不同的API设置不同的CORS规则

    缺点

    • 代码重复,需要在多个地方添加注解
    • 维护成本高,当CORS策略变更时,需要修改多处代码
    • 不适合大型项目中统一管理CORS策略

      三、方案二:全局CORS配置(WebMvcConfigurer)

      通过实现WebMvcConfigurer接口并重写addCorsMappings方法,可以在全局范围内配置CORS规则。

      实现方式

      @Configuration
      public class WebConfig implements WebMvcConfigurer {
          
          @Override
          public void addCorsMappings(CorsRegistry registry) {
              registry.addMapping("/api/**")
                      .allowedOrigins("http://example.com", "http://localhost:3000")
                      .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                      .allowedHeaders("*")
                      .allowCredentials(true)
                      .maxAge(3600); // 1小时内不需要再预检(发OPTIONS请求)
          }
      }
      

      优点

      • 可以方便集中管理所有API的CORS配置
      • 配置灵活,可以针对不同的URL模式设置不同的规则
      • 代码简洁,易于维护

        缺点

        • 在某些场景下可能需要与其他安全配置结合使用

          四、方案三:使用CorsFilter

          通过定义CorsFilter作为一个Bean,可以在过滤器级别处理跨域请求,这种方式比WebMvcConfigurer的优先级更高。

          实现方式

          @Configuration
          public class CorsConfig {
              
              @Bean
              public CorsFilter corsFilter() {
                  UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
                  CorsConfiguration config = new CorsConfiguration();
                  
                  // 允许的源
                  config.addAllowedOrigin("http://example.com");
                  config.addAllowedOrigin("http://localhost:3000");
                  
                  // 允许的HTTP方法
                  config.addAllowedMethod("*");
                  
                  // 允许的头信息
                  config.addAllowedHeader("*");
                  
                  // 允许携带认证信息(Cookie等)
                  config.setAllowCredentials(true);
                  
                  // 预检请求的有效期,单位为秒
                  config.setMaxAge(3600L);
                  
                  // 对所有URL应用这些配置
                  source.registerCorsConfiguration("/**", config);
                  
                  return new CorsFilter(source);
              }
          }
          

          优点

          • 在过滤器级别处理,可以拦截所有请求
          • 优先级高于方案二
          • 可以与其他过滤器组合使用
          • 适合在不修改已有Controller的情况下添加CORS支持

            缺点

            • 无法精确到方法级别控制
            • 对于复杂的规则可能不够灵活

              五、方案四:Spring Security中的CORS配置

              如果项目使用了Spring Security,需要在Security配置中允许CORS,否则Security可能会拦截跨域请求。

              实现方式

              @Configuration
              @EnableWebSecurity
              public class SecurityConfig {
                  
                  @Bean
                  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
                      http
                          .cors(Customizer.withDefaults()) // 使用CorsConfigurationSource的默认配置
                          .csrf().disable()
                          .authorizeHttpRequests(authorize -> authorize
                              .requestMatchers("/api/**").authenticated()
                              .anyRequest().permitAll()
                          )
                          .httpBasic(Customizer.withDefaults());
                      
                      return http.build();
                  }
                  
                  @Bean
                  public CorsConfigurationSource corsConfigurationSource() {
                      CorsConfiguration configuration = new CorsConfiguration();
                      configuration.setAllowedOrigins(Arrays.asList("http://example.com", "http://localhost:3000"));
                      configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
                      configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type"));
                      configuration.setAllowCredentials(true);
                      configuration.setMaxAge(3600L);
                      
                      UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
                      source.registerCorsConfiguration("/**", configuration);
                      return source;
                  }
              }
              

              优点

              • 与Spring Security无缝集成
              • 可以结合认证和授权规则一起配置
              • 适合需要安全控制的REST API

                缺点

                • 依赖Spring Security
                • 对于不需要安全控制的简单应用可能略显复杂

                  六、方案五:网关层面解决跨域(Spring Cloud Gateway)

                  在微服务架构中,可以在API网关层统一处理跨域问题,这样后端微服务就不需要各自配置CORS了。

                  实现方式

                  // Spring Cloud Gateway配置
                  @Configuration
                  public class GatewayConfig {
                      
                      @Bean
                      public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
                          return builder.routes()
                                  .route("user_service_route", r -> r.path("/api/users/**")
                                          .uri("lb://user-service"))
                                  .route("product_service_route", r -> r.path("/api/products/**")
                                          .uri("lb://product-service"))
                                  .build();
                      }
                      
                      @Bean
                      public WebFilter corsFilter() {
                          return (ServerWebExchange ctx, WebFilterChain chain) -> {
                              ServerHttpRequest request = ctx.getRequest();
                              if (CorsUtils.isCorsRequest(request)) {
                                  ServerHttpResponse response = ctx.getResponse();
                                  HttpHeaders headers = response.getHeaders();
                                  headers.add("Access-Control-Allow-Origin", "*");
                                  headers.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
                                  headers.add("Access-Control-Allow-Headers", "Authorization, Content-Type");
                                  headers.add("Access-Control-Allow-Credentials", "true");
                                  headers.add("Access-Control-Max-Age", "3600");
                                  if (request.getMethod() == HttpMethod.OPTIONS) {
                                      response.setStatusCode(HttpStatus.OK);
                                      return Mono.empty();
                                  }
                              }
                              return chain.filter(ctx);
                          };
                      }
                  }
                  

                  优点

                  • 集中处理所有微服务的跨域问题
                  • 后端服务无需关心跨域配置
                  • 便于统一管理和维护
                  • 适合微服务架构

                    缺点

                    • 依赖Spring Cloud Gateway
                    • 配置相对复杂
                    • 对于单体应用可能过于重量级

                      七、方案六:使用代理服务器

                      通过配置前端开发服务器代理 (开发环境) 或使用Nginx (生产环境) 等反向代理服务器,可以间接解决跨域问题。这种方式实际上是绕过了浏览器的同源策略,而不是直接在后端解决CORS。

                      前端开发服务器代理配置(以Vue CLI为例)

                      // vue.config.js
                      module.exports = {
                        devServer: {
                          proxy: {
                            '/api': {
                              target: 'http://localhost:8080',
                              changeOrigin: true,
                              pathRewrite: {
                                '^/api': '/api'
                              }
                            }
                          }
                        }
                      }
                      

                      Nginx反向代理配置

                      server {
                          listen 80;
                          server_name frontend.example.com;
                          
                          location / {
                              root /usr/share/nginx/html;
                              index index.html;
                              try_files $uri $uri/ /index.html;
                          }
                          
                          location /api/ {
                              proxy_pass http://backend.example.com:8080/api/;
                              proxy_set_header Host $host;
                              proxy_set_header X-Real-IP $remote_addr;
                          }
                      }
                      

                      优点

                      • 完全绕过浏览器的同源策略限制
                      • 后端无需任何CORS配置

                        缺点

                        • 需要额外的代理配置
                        • 增加了系统复杂性
                        • 可能引入额外的网络延迟

                          八、方案比较与选择建议

                          方案实现难度灵活性维护成本适用场景
                          @CrossOrigin注解小型项目,特定API需要跨域
                          WebMvcConfigurer大多数Spring Boot应用
                          CorsFilter需要优先级高的CORS处理
                          Spring Security有安全需求的应用
                          网关层面解决微服务架构
                          代理服务器生产环境,严格的安全要求

                          九、最佳实践与注意事项

                          1. 安全考虑

                          • 不要盲目设置Access-Control-Allow-Origin: *,应该明确指定允许的源
                          • 谨慎处理带有凭证的请求(如Cookie),确保只允许受信任的源
                          • 对于敏感操作,考虑使用CSRF令牌进行保护

                            2. 性能优化

                            • 合理设置Access-Control-Max-Age以减少预检请求
                            • 避免在每个请求中都解析和构建CORS头
                            • 在网关层处理CORS可以减轻后端服务的负担

                              3. 开发与调试

                              • 在开发环境可以适当放宽CORS限制,但在生产环境一定要收紧
                              • 使用浏览器开发者工具的Network面板调试CORS问题

                                十、总结

                                跨域请求是前后端分离开发中不可避免的问题,Spring Boot提供了多种解决方案。从简单的@CrossOrigin注解到复杂的网关配置,我们可以根据项目规模和需求选择合适的方案。在实际开发中,建议综合考虑安全性、灵活性和维护成本,选择最适合项目的CORS解决方案。

                                对于大多数Spring Boot应用,推荐使用全局CORS配置(WebMvcConfigurer)方案,它提供了良好的平衡性;而对于微服务架构,则推荐在网关层统一处理CORS问题,以减少后端服务的配置负担。

                                无论选择哪种方案,都应该遵循"最小权限原则" ,只允许必要的源访问必要的资源,确保系统的安全性。

                                SpringBoot中6种跨域请求解决方案
                                (图片来源网络,侵删)
                                SpringBoot中6种跨域请求解决方案
                                (图片来源网络,侵删)
                                SpringBoot中6种跨域请求解决方案
                                (图片来源网络,侵删)
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码