Spring MVC позволяет обрабатывать CORS (совместное использование ресурсов между разными источниками).
Из соображений безопасности браузеры запрещают AJAX-обращения к ресурсам за пределами текущего происхождения. Например, у вас может быть свой банковский счет на одной вкладке, а evil.com - на другой. Скрипты с сайта evil.com не должны иметь возможности осуществлять AJAX-запросы к API вашего банка с вашими учетными данными - например, снимать деньги с вашего счета!
Совместное использование ресурсов между разными источниками (CORS) - это спецификация W3C, реализованная в большинстве браузеров, которая позволяет определять, какие междоменные запросы разрешены, вместо того, чтобы использовать менее безопасные и менее мощные обходные пути, основанные на IFRAME или JSONP.
Обработка
Спецификация CORS различает предварительные, простые и фактические запросы. Чтобы понять, как работает CORS, можете ознакомиться с этой статьей, помимо прочего, или обратиться за подробной информацией к спецификации.
Реализации HandlerMapping
из Spring WebFlux обеспечивают встроенную поддержку CORS. После успешного отображения запроса на обработчик, HandlerMapping
проверяет конфигурацию CORS для данного запроса и обработчика и предпринимает дальнейшие действия. Предварительные запросы обрабатываются напрямую, а простые и фактические CORS-запросы перехватываются, валидируются, и для них устанавливаются необходимые заголовки CORS-ответа.
Для того чтобы разрешить запросы между разными источниками (то есть заголовок Origin
присутствует и отличается от хоста запроса), вам необходимо обеспечить некоторую явно объявленную конфигурацию CORS. Если подходящей конфигурации CORS не найдена, то предварительные запросы отклоняются. В ответы на простые и фактические CORS-запросы не добавляются CORS-заголовки, и, следовательно, браузеры их отклоняют.
Каждый HandlerMapping
может быть сконфигурирован индивидуально с помощью отображений CorsConfiguration
на основе URL-шаблонов. В большинстве случаев приложения используют Java-конфигурацию WebFlux для объявления таких отображений, что приводит к созданию единой глобальной Map, передаваемой всем реализациям HandlerMapping
.
Можно сочетать глобальную конфигурацию CORS на уровне HandlerMapping
с более тонкой настройкой CORS на уровне обработчиков. Например, аннотированные контроллеры могут использовать аннотации @CrossOrigin
на уровне классов или методов (другие обработчики могут реализовать CorsConfigurationSource
).
Правила комбинирования глобальной и локальной конфигурации обычно аддитивны - например, все глобальный и все локальные источники. В случае тех атрибутов, где может быть принято только одно значение, например, allowCredentials
и maxAge
, локальное значение переопределяет глобальное. Более подробную информацию см. в разделе, посвященном CorsConfiguration#combine(CorsConfiguration)
.
Чтобы узнать больше из исходного источника или выполнить расширенную настройку, см.:
-
CorsConfiguration
-
CorsProcessor
иDefaultCorsProcessor
-
AbstractHandlerMapping
@CrossOrigin
Аннотация @CrossOrigin
позволяет выполнять запросы между разными источниками к аннотированным методам контроллера, как показано в следующем примере:
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin
@GetMapping("/{id}")
public Mono<Account> retrieve(@PathVariable Long id) {
// ...
}
@DeleteMapping("/{id}")
public Mono<Void> remove(@PathVariable Long id) {
// ...
}
}
@RestController
@RequestMapping("/account")
class AccountController {
@CrossOrigin
@GetMapping("/{id}")
suspend fun retrieve(@PathVariable id: Long): Account {
// ...
}
@DeleteMapping("/{id}")
suspend fun remove(@PathVariable id: Long) {
// ...
}
}
По умолчанию @CrossOrigin
разрешает:
-
Все источники.
-
Все заголовки.
-
Все HTTP-методы, с которыми сопоставлен метод контроллера.
allowCredentials
не активирован по умолчанию, так как это устанавливает уровень доверия, при котором раскрывается конфиденциальная информация о пользователе (такая как файлы cookie и CSRF-токены), поэтому его следует использовать только в тех случаях, когда это действительно необходимо. Если он активирован, то либо allowOrigins
должен быть установлен для одного или нескольких определенных доменов (но не специальное значение "*"
), либо, как вариант, можно использовать свойство allowOriginPatterns для сопоставления с динамическим набором источников.
maxAge
is set to 30 minutes.
@CrossOrigin
поддерживается и на уровне класса и наследуется всеми методами. В следующем примере указан определенный домен и задано значение maxAge
равное часу:
@CrossOrigin(origins = "https://domain2.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@GetMapping("/{id}")
public Mono<Account> retrieve(@PathVariable Long id) {
// ...
}
@DeleteMapping("/{id}")
public Mono<Void> remove(@PathVariable Long id) {
// ...
}
}
@CrossOrigin("https://domain2.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
class AccountController {
@GetMapping("/{id}")
suspend fun retrieve(@PathVariable id: Long): Account {
// ...
}
@DeleteMapping("/{id}")
suspend fun remove(@PathVariable id: Long) {
// ...
}
}
Вы можете использовать аннотацию @CrossOrigin
как на уровне класса, так и на уровне метода, как это показано в следующем примере:
@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin("https://domain2.com")
@GetMapping("/{id}")
public Mono<Account> retrieve(@PathVariable Long id) {
// ...
}
@DeleteMapping("/{id}")
public Mono<Void> remove(@PathVariable Long id) {
// ...
}
}
- Использование аннотации
@CrossOrigin
на уровне класса. - Использование аннотации
@CrossOrigin
на уровне метода.
@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
class AccountController {
@CrossOrigin("https://domain2.com")
@GetMapping("/{id}")
suspend fun retrieve(@PathVariable id: Long): Account {
// ...
}
@DeleteMapping("/{id}")
suspend fun remove(@PathVariable id: Long) {
// ...
}
}
- Использование аннотации
@CrossOrigin
на уровне класса. - Использование аннотации
@CrossOrigin
на уровне метода.
Глобальная конфигурация
В дополнение к тонкой настройке на уровне методов контроллера, вам, вероятно, понадобится определить и глобальную конфигурацию CORS. Вы можете настроить отображения CorsConfiguration
на основе URL-адресов индивидуально для любого HandlerMapping
. Однако большинство приложений используют для этого Java-конфигурацию WebFlux.
По умолчанию глобальная конфигурация включает следующее:
-
Все источники.
-
Все заголовки.
-
Методы
GET
,HEAD
иPOST
.
allowCredentials
не активирован по умолчанию, так как это устанавливает уровень доверия, при котором раскрывается конфиденциальная информация о пользователе (такая как файлы cookie и CSRF-токены), поэтому его следует использовать только в тех случаях, когда это действительно необходимо. Если он активирован, то либо allowOrigins
должен быть установлен для одного или нескольких определенных доменов (но не специальное значение "*"
), либо, как вариант, можно использовать свойство allowOriginPatterns для сопоставления с динамическим набором источников.
maxAge
is set to 30 minutes.
Чтобы активировать CORS в Java-конфигурации WebFlux, можно использовать обратный вызов CorsRegistry
, как показано в следующем примере:
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://domain2.com")
.allowedMethods("PUT", "DELETE")
.allowedHeaders("header1", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(true).maxAge(3600);
// Добавляем еще отображений...
}
}
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun addCorsMappings(registry: CorsRegistry) {
registry.addMapping("/api/**")
.allowedOrigins("https://domain2.com")
.allowedMethods("PUT", "DELETE")
.allowedHeaders("header1", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(true).maxAge(3600)
// Добавляем еще отображений...
}
}
WebFilter
для CORS
Вы можете применить средства поддержки CORS через встроенный CorsWebFilter
, который отлично подходит для функциональных конечных точек.
CorsFilter
со Spring Security, имейте в виду, что Spring Security предусматривает встроенную поддержку CORS.Для настройки фильтра можно объявить бин CorsWebFilter
и передать CorsConfigurationSource
в его конструктор, как показано в следующем примере:
@Bean
CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
// Возможно...
// config.applyPermitDefaultValues()
config.setAllowCredentials(true);
config.addAllowedOrigin("https://domain1.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
@Bean
fun corsFilter(): CorsWebFilter {
val config = CorsConfiguration()
// Возможно...
// config.applyPermitDefaultValues()
config.allowCredentials = true
config.addAllowedOrigin("https://domain1.com")
config.addAllowedHeader("*")
config.addAllowedMethod("*")
val source = UrlBasedCorsConfigurationSource().apply {
registerCorsConfiguration("/**", config)
}
return CorsWebFilter(source)
}
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ