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) {
// ...
}
}
Kotlin
@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 дорівнює годині:

Java
@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) {
// ...
}
}
Kotlin
@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 як на рівні класу, так і на рівні методу, як це показано в наступному прикладі:

Java
@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) {
// ...
}
}
  1. Використання анотації @CrossOrigin на рівні класу.
  2. Використання анотації @CrossOrigin на рівні методу.
Kotlin
@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) {
// ...
}
}
  1. Використання анотації @CrossOrigin на рівні класу.
  2. Використання анотації @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, як показано в наступному прикладі:

Java

@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);
// Додаємо ще відображень...
}
}
Kotlin

@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 до його конструктора, як показано в наступному прикладі:

Java

@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);
}
Kotlin

@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)
}