Введение
Иногда самые простые задачи в разработке могут превращаться в настоящую головоломку. Так случилось и с нами: наша команда столкнулась с неожиданной проблемой при интеграции API, написанного на Java с использованием Spring, и сайта-лендинга заказчика. Ошибка CORS (Cross-Origin Resource Sharing) стала барьером для нормального взаимодействия между сервисами. В этой статье я расскажу, как мы решили проблему и какие подходы лучше использовать в зависимости от конфигурации вашего проекта.
Проблема: CORS и как она возникает
CORS — это механизм, который ограничивает взаимодействие между различными доменами. Это важная функция безопасности, но она также может стать препятствием, если ваш фронтенд (лендинг) и API развернуты на разных серверах и доменах.
В нашем случае лендинг вызывал API, развернутое на другом сервере, и получил стандартное сообщение об ошибке CORS.
Решение с использованием аннотации @CrossOrigin
Первый интуитивный шаг — использование встроенной аннотации @CrossOrigin
. Вот пример:
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/some-amazing-api")
@Slf4j
public class UserController {
@CrossOrigin(origins = {"http://localhost:3000",
"https://landing-url-1",
"https://landing-url-2",
"https://landing-url-3"},
allowCredentials = "true")
@PostMapping("/some-amazing-api-url")
public ResponseEntity<?> amazingControllerMethod(@RequestBody @Valid AmazingRequestBody requestBody) {
return ResponseEntity.status(HttpStatus.OK)
.body("Hello from Tune-it!!!");
}
}
Эта аннотация указывает допустимые домены, с которых разрешено выполнять запросы к API. Однако, если ваш проект использует Spring Security, это решение работать не будет, так как Spring Security имеет собственные ограничения и правила обработки CORS.
Настройка CORS в проектах с Spring Security
Когда в проекте используется Spring Security, для настройки CORS необходимо вручную конфигурировать безопасность. Вот рабочее решение:
Подход для версии Spring Security до 5.7
@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().configurationSource(corsConfigurationSource())
.and()
// другие настройки безопасности
.authorizeRequests().anyRequest().authenticated();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
var corsConfig = new CorsConfiguration();
corsConfig.setAllowedOrigins(
List.of("http://localhost:3000",
"https://landing-url-1",
"https://landing-url-2",
"https://landing-url-3"));
corsConfig.setAllowCredentials(true);
corsConfig.setAllowedMethods(List.of("*"));
corsConfig.setAllowedHeaders(List.of("*"));
var urlBasedConfig = new UrlBasedCorsConfigurationSource();
urlBasedConfig.registerCorsConfiguration("/api/some-amazing-api/some-amazing-api-url", corsConfig);
urlBasedConfig.registerCorsConfiguration("/api/some-amazing-api/some-amazing-api-url-2", corsConfig);
return urlBasedConfig;
}
}
Подход для версии Spring Security 5.7 и выше
Если вы используете более свежую версию Spring Security, настройка выглядит немного проще:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public CorsConfigurationSource corsConfigurationSource() {
var corsConfig = new CorsConfiguration();
corsConfig.setAllowedOrigins(
List.of("http://localhost:3000",
"https://landing-url-1",
"https://landing-url-2",
"https://landing-url-3"));
corsConfig.setAllowCredentials(true);
corsConfig.setAllowedMethods(List.of("*"));
corsConfig.setAllowedHeaders(List.of("*"));
var urlBasedConfig = new UrlBasedCorsConfigurationSource();
urlBasedConfig.registerCorsConfiguration("/api/some-amazing-api/**", corsConfig);
return urlBasedConfig;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
// другие настройки
.authorizeHttpRequests().anyRequest().authenticated();
return http.build();
}
}
Заключение
Ошибка CORS может стать неприятной неожиданностью, особенно если вы сталкиваетесь с ней впервые. Однако, как мы видим, Spring предоставляет удобные инструменты для её решения. Ключевым моментом является правильная конфигурация в зависимости от используемых библиотек и версий.
Теперь, вооружённые этими знаниями, вы сможете без труда настроить взаимодействие между фронтендом и API, даже если они работают на разных серверах и доменах.