Spring MVC le permite manejar CORS (intercambio de recursos entre orígenes).

Por razones de seguridad, los navegadores prohíben las llamadas AJAX a recursos fuera del origen actual. Por ejemplo, es posible que tengas tu cuenta bancaria en una pestaña y evil.com en otra. Los scripts de evil.com no deberían poder realizar solicitudes AJAX a la API de su banco con sus credenciales; por ejemplo, ¡retirar dinero de su cuenta!

El intercambio de recursos entre orígenes (CORS) es especificación W3C implementada en la mayoría de los navegadores, lo que le permite determinar qué solicitudes entre dominios están permitidas, en lugar de utilizar soluciones alternativas menos seguras y menos potentes basadas en IFRAME o JSONP.

Procesamiento

La especificación CORS distingue entre solicitudes de verificación previa, simples y reales. Para comprender cómo funciona CORS, puede leer este artículo. otras cosas, o consulte la especificación para obtener más detalles.

Las implementaciones HandlerMapping de Spring WebFlux brindan soporte CORS nativo. Después de asignar con éxito una solicitud a un controlador, HandlerMapping verifica la configuración CORS para esa solicitud y controlador y toma medidas adicionales. Las solicitudes preliminares se procesan directamente, mientras que las solicitudes CORS simples y reales se interceptan, validan y se establecen los encabezados de respuesta CORS requeridos.

Para permitir solicitudes entre diferentes orígenes (es decir, el encabezado Origin está presente y es diferente del host de solicitud), debe proporcionar alguna configuración CORS declarada explícitamente. Si no se encuentra una configuración CORS adecuada, se rechazan las solicitudes de verificación previa. Las respuestas a solicitudes CORS simples y reales no tienen encabezados CORS agregados y, por lo tanto, los navegadores las rechazan.

Cada HandlerMapping puede ser configurado individualmente usando asignaciones CorsConfiguration basadas en patrones de URL. En la mayoría de los casos, las aplicaciones utilizan la configuración Java de WebFlux para declarar estas asignaciones, lo que da como resultado un único mapa global pasado a todas las implementaciones de HandlerMapping.

Puedes combinar la configuración CORS global en el nivel HandlerMapping con un ajuste CORS más detallado en el nivel del controlador. Por ejemplo, los controladores anotados pueden usar anotaciones @CrossOrigin a nivel de clase o método (otros controladores pueden implementar CorsConfigurationSource).

Las reglas para combinar reglas globales y la configuración local suelen ser aditivas; por ejemplo, todas las fuentes globales y todas las locales. Para atributos donde solo se puede aceptar un valor, como allowCredentials y maxAge, el valor local anula el global. Para obtener más información, consulte la sección sobre CorsConfiguration#combine(CorsConfiguration).

Para obtener más información de la fuente original o realizar una configuración avanzada, consulte:

  • CorsConfiguration

  • CorsProcessor y DefaultCorsProcessor

  • AbstractHandlerMapping

@CrossOrigin

Anotación @CrossOrigin permite consultas entre diferentes fuentes a métodos de controlador anotados, como se muestra en el siguiente ejemplo:

Java
@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) {
// ...
}
}

De forma predeterminada, @CrossOrigin permite:

  • Todas las fuentes.

  • Todos los encabezados.

  • Todos los métodos HTTP que utiliza el método asociado con el controlador.

allowCredentials no está habilitado de forma predeterminada porque establece el nivel de confianza en el que la información confidencial del usuario (como cookies y CSRF) tokens), por lo que sólo debe utilizarse en los casos en los que sea realmente necesario. Si está habilitado, entonces se debe configurar allowOrigins para uno o más dominios específicos (pero no el valor especial "*") o, alternativamente, la propiedad enableOriginPatterns se puede usar para asignaciones. a un conjunto dinámico de fuentes.

maxAge está establecido en 30 minutos.

@CrossOrigin es compatible tanto en la clase nivel y es heredado por todos los métodos. El siguiente ejemplo especifica un dominio específico y establece maxAge en una hora:

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) {
// ...
}
}

Puede utilizar la anotación @CrossOrigin tanto a nivel de clase como de método, como se muestra en el siguiente ejemplo:

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. Usando un anotación @CrossOrigin a nivel de clase.
  2. Usando la anotación @CrossOrigin a nivel de método.
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. Usando @CrossOrigin anotación a nivel de clase.
  2. Usando la anotación @CrossOrigin a nivel de método.

Configuración global

Además del ajuste fino a nivel del método del controlador, es probable que también necesite definir la configuración CORS global. Puede configurar asignaciones CorsConfiguration basadas en URL individualmente para cualquier HandlerMapping. Sin embargo, la mayoría de las aplicaciones utilizan la configuración Java de WebFlux para esto.

De forma predeterminada, la configuración global incluye lo siguiente:

  • Todas las fuentes.

  • Todos los encabezados.

  • Métodos GET, HEAD y POST.

allowCredentials no está habilitado de forma predeterminada porque establece el nivel de confianza en el que la información confidencial del usuario (como cookies y CSRF) ) son tokens revelados), por lo que solo debe usarse en casos donde sea realmente necesario. Si está habilitado, entonces allowOrigins debe configurarse para uno o más dominios específicos (pero no el valor especial "* "), o alternativamente puede usar la propiedad enableOriginPatterns para comparar con un conjunto dinámico de orígenes.

maxAge está establecido en 30 minutos.

Para habilitar CORS en la configuración de WebFlux Java, puede usar la devolución de llamada CorsRegistry, como se muestra en el siguiente ejemplo:

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);
// Agregar más pantallas...
}
}
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)
// Agregar más pantallas...
}
}

WebFilter para CORS

Puede aplicar compatibilidad con CORS a través del CorsWebFilter lo cual es excelente para puntos finales funcionales.

Si intenta utilizar CorsFilter con Spring Security, tenga en cuenta que Spring Security proporciona soporte integrado CORS.

Para configurar un filtro, puede declarar un bean CorsWebFilter y pasar CorsConfigurationSource a su constructor, como se muestra en el siguiente ejemplo:

Java

@Bean
CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
// Quizás...
// 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()
// Quizás...
// config.applyPermitDefaultValues()
config.allowCredentials = true
config.addAllowedOrigin("https://domain1.com")
config.addAllowedHeader("*")
config.addAllowedMethod("*")
val source = UrlBasedCorsConfigurationSource().apply {
registerCorsConfiguration("/**", config)
}
return CorsWebFilter(source)
}