Перейти к основному содержимому

CORS с весной

· 5 мин. чтения

1. Обзор

В любом современном браузере совместное использование ресурсов между источниками (CORS) является актуальной спецификацией с появлением клиентов HTML5 и JS, которые потребляют данные через REST API.

Часто хост, который обслуживает JS (например, example.com), отличается от хоста, который обслуживает данные (например, api.example.com). В таком случае CORS обеспечивает междоменное взаимодействие.

Spring обеспечивает первоклассную поддержку CORS, предлагая простой и эффективный способ его настройки в любом веб-приложении Spring или Spring Boot.

2. Конфигурация CORS метода контроллера

Включить CORS очень просто — просто добавьте аннотацию @CrossOrigin .

Мы можем реализовать это несколькими различными способами.

2.1. @CrossOrigin в методе аннотированного обработчика @RequestMapping-

@RestController
@RequestMapping("/account")
public class AccountController {

@CrossOrigin
@RequestMapping(method = RequestMethod.GET, path = "/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}

@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}

В приведенном выше примере мы включили CORS только для метода retrieve() . Мы видим, что мы не задавали никаких настроек для аннотации @CrossOrigin , поэтому используются значения по умолчанию:

  • Все происхождения разрешены.
  • Разрешены методы HTTP, указанные в аннотации @RequestMapping (GET, в данном примере).
  • Время кэширования ответа предварительной проверки ( maxAge ) составляет 30 минут.

2.2. @CrossOrigin на контроллере

@CrossOrigin(origins = "http://example.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {

@RequestMapping(method = RequestMethod.GET, path = "/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}

@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}

На этот раз мы добавили @CrossOrigin на уровне класса. Таким образом, оба метода retrieve() и remove() включены. Мы можем настроить конфигурацию, указав значение одного из атрибутов аннотации: origins , methods , allowHeaders , posedHeaders , allowCredentials или maxAge .

2.3. @CrossOrigin в контроллере и методе обработчика

@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {

@CrossOrigin("http://example.com")
@RequestMapping(method = RequestMethod.GET, "/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}

@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}

Spring объединит атрибуты из обеих аннотаций для создания объединенной конфигурации CORS.

Здесь оба метода будут иметь maxAge 3600 секунд, метод remove() разрешит все источники, а метод retrieve() разрешит только источники с http://example.com .

3. Глобальная конфигурация CORS

В качестве альтернативы мелкозернистой конфигурации на основе аннотаций Spring позволяет нам определить некоторую глобальную конфигурацию CORS из наших контроллеров. Это похоже на использование решения на основе фильтра , но может быть объявлено в Spring MVC и объединено с мелкозернистой конфигурацией @CrossOrigin .

По умолчанию разрешены все источники и методы GET, HEAD и POST.

3.1. JavaConfig

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}

В приведенном выше примере разрешаются запросы CORS из любого источника в любую конечную точку в приложении.

Чтобы еще больше зафиксировать это, метод register.addMapping возвращает объект CorsRegistration , который мы можем использовать для дополнительной настройки. Также есть метод allowOrigins , который позволяет указать массив разрешенных источников. Это может быть полезно, если нам нужно загрузить этот массив из внешнего источника во время выполнения.

Кроме того, есть также AllowMethods , AllowHeaders , visibleHeaders , maxAge и allowCredentials , которые мы можем использовать для установки заголовков ответа и параметров настройки.

3.2. Пространство имен XML

Эта минимальная конфигурация XML включает CORS в шаблоне пути /** с теми же свойствами по умолчанию, что и в JavaConfig:

<mvc:cors>
<mvc:mapping path="/**" />
</mvc:cors>

Также можно объявить несколько сопоставлений CORS с настраиваемыми свойствами:

<mvc:cors>

<mvc:mapping path="/api/**"
allowed-origins="http://domain1.com, http://domain2.com"
allowed-methods="GET, PUT"
allowed-headers="header1, header2, header3"
exposed-headers="header1, header2" allow-credentials="false"
max-age="123" />

<mvc:mapping path="/resources/**"
allowed-origins="http://domain1.com" />

</mvc:cors>

4. CORS с Spring Security

Если мы используем Spring Security в нашем проекте, мы должны сделать дополнительный шаг, чтобы убедиться, что он хорошо работает с CORS. Это связано с тем, что сначала необходимо обработать CORS. В противном случае Spring Security отклонит запрос до того, как он достигнет Spring MVC.

К счастью, Spring Security предоставляет готовое решение:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()...
}
}

В этой статье это объясняется более подробно.

5. Как это работает

Запросы CORS автоматически отправляются в различные зарегистрированные HandlerMappings . Они обрабатывают предварительные запросы CORS и перехватывают простые и фактические запросы CORS с помощью реализации CorsProcessor ( по умолчанию DefaultCorsProcessor ) для добавления соответствующих заголовков ответов CORS (таких как Access-Control-Allow-Origin ).

CorsConfiguration позволяет нам указать, как должны обрабатываться запросы CORS, включая разрешенные источники, заголовки и методы, среди прочего. Мы можем предоставить его различными способами:

  • AbstractHandlerMapping#setCorsConfiguration() позволяет нам указать Map с несколькими CorsConfiguration , сопоставленными с шаблонами пути, такими как /api/** .
  • Подклассы могут предоставлять свои собственные CorsConfiguration , переопределяя метод AbstractHandlerMapping#getCorsConfiguration(Object, HttpServletRequest) .
  • Обработчики могут реализовать интерфейс CorsConfigurationSource (как сейчас делает ResourceHttpRequestHandler ), чтобы предоставить CorsConfiguration для каждого запроса.

6. Заключение

В этой статье мы показали, как Spring обеспечивает поддержку включения CORS в нашем приложении.

Начали с настройки контроллера. Мы увидели, что нам нужно только добавить аннотацию @CrossOrigin , чтобы включить CORS либо для одного конкретного метода, либо для всего контроллера.

Кроме того, мы узнали, что для управления конфигурацией CORS вне контроллеров мы можем легко выполнять это в файлах конфигурации, используя либо JavaConfig, либо XML.

Полный исходный код примеров доступен на GitHub .