package com.talentcard.server.config; import java.util.Arrays; import java.util.List; import org.springframework.beans.factory.annotation.Value; 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 ApiCorsConfig { @Bean public CorsFilter apiCorsFilter( @Value("${app.cors.allowed-origins:}") String allowedOrigins, @Value("${app.cors.allowed-origin-patterns:}") String allowedOriginPatterns) { CorsConfiguration cors = new CorsConfiguration(); cors.setAllowedOrigins(split(allowedOrigins)); // 本地开发工具的端口是随机的(微信开发者工具每次启动都换一个),写死端口必然漏。 // 用 origin 通配覆盖本机各种调试端口,真实域名仍走上面的精确白名单。 cors.setAllowedOriginPatterns(split(allowedOriginPatterns)); cors.setAllowedMethods(List.of("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")); cors.setAllowedHeaders(List.of("Content-Type", "Authorization", "X-Request-Id", "X-Client-Platform", "X-Client-Version", "X-Merchant-Store-Id", "X-Idempotency-Key")); cors.setMaxAge(3600L); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/api/**", cors); // Complete browser preflight before MVC authentication interceptors run. return new CorsFilter(source); } private static List split(String values) { return Arrays.stream(values.split(",")).map(String::trim).filter(value -> !value.isEmpty()).toList(); } }