在我的 Spring Boot 应用程序中配置 CORS 设置以仅允许来自我的网站的请求后,我在 App Engine 上部署应用程序时遇到了意外行为。尽管本地测试成功,配置按预期工作,但部署后,API 仍然可以从任何来源访问。
下面是我的 Spring Security 配置类:
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
@Bean
public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(authorize -> authorize.anyRequest().permitAll()) // Allow all requests
.httpBasic(Customizer.withDefaults())
.cors(httpSecurityCorsConfigurer -> httpSecurityCorsConfigurer.configurationSource(corsConfigurationSource()))
.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("http://mywebsite.com","https://mywebsite.com"));
configuration.setAllowedHeaders(Arrays.asList("Access-Control-Allow-Origin", "Origin"));
configuration.setAllowedMethods(Arrays.asList("GET","POST","DELETE","PUT"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
这是我的控制器:
@RestController
@RequestMapping("/cars")
public class CarController {
private final CarService carService;
public CarController(CarService carService) {
this.carService = carService;
}
@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<Car> saveCar(@RequestBody Car car) {
return carService.saveCar(car);
}
@DeleteMapping("/{carId}")
public Mono<Void> deleteCar(@PathVariable String carId) {
return carService.deleteCar(carId);
}
@GetMapping
public Flux<Car> findAllCars() {
return carService.findALlCars();
}
@GetMapping("/city/{city}")
public Flux<Car> findCarsByCity(@PathVariable String city) {
return carService.findCarsByCity(city);
}
}
App Engine 用于部署 API 的 app.yml 文件:
runtime: java17
instance_class: F1
我发现我可以在 app.yml 文件中配置 CORS,但我更愿意保留这种关注,让 Spring Security 来处理它。
其他日志:
configuration.setAllowedHeaders(Arrays.asList("Access-Control-Allow-Origin", "Origin"));
您可能需要调整
configuration.setAllowedHeaders(...)
。 header "Access-Control-Allow-Origin"
是一个 response 标头,而不是请求标头。它不应该出现在允许的标头列表中。通常,您会允许诸如 "Content-Type"
、"Authorization"
、"Cache-Control"
等标头。
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("http://mywebsite.com","https://mywebsite.com"));
configuration.setAllowedHeaders(List.of("Content-Type", "Authorization", "Cache-Control"));
configuration.setAllowedMethods(List.of("GET","POST","DELETE","PUT"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
确保 Spring Boot 正在选取您的
SecurityConfiguration
类。有时,如果配置不在正确的包中或缺少某些注释,则不会使用它。您的 SecurityConfiguration
类应该位于与主应用程序类的包相同的包中,或者是主应用程序类的子包中。 主应用程序类是用 @SpringBootApplication
注释的类。
向
SecurityConfiguration
类添加日志记录以检查它是否正在实例化。您可以在构造函数或任何 @Bean
方法中添加日志语句:
@Slf4j // If you are using Lombok, or define a Logger manually
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
public SecurityConfiguration() {
log.info("SecurityConfiguration is being instantiated");
}
// rest of your configuration
}
如果您的项目中有 Spring Boot Actuator 依赖项,您可以使用
/beans
端点来获取已在 Spring 上下文中实例化的所有 bean 的列表,包括您的配置:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
// other dependencies
}
此外,应用程序引擎可能会覆盖您的 CORS 设置或提供自己的默认值。应用程序中的其他中间件可能会在将标头发送回客户端之前覆盖标头。检查 Google Cloud Console 中 App Engine 应用程序的日志,看看是否有任何消息指示响应标头的操作或与 CORS 相关的错误。
作为测试,您可以暂时禁用 Spring Security 配置并部署一个简单的控制器,在响应中手动设置 CORS 标头,以查看 App Engine 是否覆盖它们。
@GetMapping("/test-cors")
public ResponseEntity<String> testCors() {
HttpHeaders headers = new HttpHeaders();
headers.add("Access-Control-Allow-Origin", "http://mywebsite.com");
return new ResponseEntity<>("CORS test", headers, HttpStatus.OK);
}