Spring Data Rest and Cors

Rozwijam aplikację Spring Boot z interfejsem Rest i frontem dart.

XMLHttpRequest wykonuje żądanie opcji, które jest obsługiwane całkowicie poprawnie. Po tym, ostateczne żądanie GET ("/products") jest wystawiane i nie powiodło się:

nie ma nagłówka 'Access-Control-Allow-Origin' na żądanym zasobie. Pochodzenie " http://localhost:63343 " nie jest zatem dozwolony dostęp.

Po pewnym debugowaniu znalazłem następujące: Na AbstractHandlerMapping.konfiguracja corsConfiguration jest wypełniana dla wszystkich podklas z wyjątkiem RepositoryRestHandlerMapping. W RepositoryRestHandlerMapping nie jest obecna / ustawiona konfiguracja corsConfiguration podczas tworzenia, więc nie zostanie rozpoznana jako ścieżka / zasób cors.
= > No Cors headers attached
Czy to może być problem? Jak mogę to ustawić?

Klasy konfiguracji:

@Configuration
public class RestConfiguration extends RepositoryRestMvcConfiguration {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**").allowCredentials(false).allowedOrigins("*").allowedMethods("PUT", "POST", "GET", "OPTIONS", "DELETE").exposedHeaders("Authorization", "Content-Type");
    }

   ...
}

Próbowałem nawet ustawić Kors na adnotację:

@CrossOrigin( methods = RequestMethod.GET, allowCredentials = "false")
public interface ProductRepository extends CrudRepository<Product, String> {


}

Raw request nagłówki:

GET /products HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
authorization: Basic dXNlcjpwYXNzd29yZA==
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/43.0.2357.130 Chrome/43.0.2357.130 Safari/537.36
Content-Type: application/json
Accept: */*
Referer: http://localhost:63343/inventory-web/web/index.html
Accept-Encoding: gzip, deflate, sdch
Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4

Surowe nagłówki odpowiedzi:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/hal+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 30 Jul 2015 15:58:03 GMT

Używane wersje: Spring Boot 1.3.0M2 Wiosna 4.2.0.RC2

Czego mi brakuje?
Author: Andrew Tobilko, 2015-07-30

3 answers

Rzeczywiście, przed Spring Data REST 2.6 (Ingalls) tylko HandlerMapping instancje utworzone przez Spring MVC WebMvcConfigurationSupport i kontrolery oznaczone @CrossOrigin były świadome CORS.

Ale teraz, gdy DATAREST-573 został naprawiony, RepositoryRestConfiguration teraz wyświetla getCorsRegistry() dla konfiguracji globalnej i @CrossOrigin adnotacje w repozytoriach są również rozpoznawane, więc jest to zalecane podejście. Zobacz https://stackoverflow.com/a/42403956/1092077 odpowiedz na konkretne przykłady.

Dla ludzi, którzy muszą trzymać się Spring Data REST 2.5 (Hopper) lub poprzednie wersje, myślę, że najlepszym rozwiązaniem jest zastosowanie podejścia opartego na filtrach. Możesz oczywiście użyć Tomcat, Jetty lub ten , ale pamiętaj, że Spring Framework 4.2 zapewnia również CorsFilter które używają tej samej logiki przetwarzania Kors, co @CrossOrigin i addCorsMappings(CorsRegistry registry). Przechodząc przez UrlBasedCorsConfigurationSource instancja do parametru konstruktora CorsFilter, można łatwo uzyskać coś tak potężnego, jak natywne wsparcie Cors Spring global.

Jeśli używasz Spring Boot (który obsługuje Filter fasolki), może to być coś w stylu:

@Configuration
public class RestConfiguration {

    @Bean
    public FilterRegistrationBean corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration().applyPermitDefaultValues();
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(0);
        return bean;
    }
}
 87
Author: Sébastien Deleuze,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-22 08:24:43

Od Ingalls train został zrealizowany, wsparcie CORS w Spring Data jest teraz włączone. Istnieją dwa sposoby radzenia sobie z:

  1. Adnotacja @CrossOrigin z określeniem origins, methods, i allowedHeaders przez interfejs @RepositoryRestResource.

    @CrossOrigin(...)
    @RepositoryRestResource
    public interface PageRepository extends CrudRepository<Page, Long> { ... }
    
  2. Konfiguracja globalna z RepositoryRestConfiguration wewnątrz klasy @Configuration. Oznaczanie repozytoriów przez @CrossOrigin nie jest wtedy konieczne.

    @Configuration
    public class GlobalRepositoryRestConfigurer extends RepositoryRestConfigurerAdapter {
    
        @Override
        public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
            config.getCorsRegistry()
                      .addMapping(CORS_BASE_PATTERN)
                      .allowedOrigins(ALLOWED_ORIGINS)
                      .allowedHeaders(ALLOWED_HEADERS)
                      .allowedMethods(ALLOWED_METHODS);
         }
    
    }
    
 14
Author: Andrew Tobilko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-26 20:03:43

Z jakiegoś powodu podejście zaproponowane w zaakceptowanej odpowiedzi powyżej nie działało dla mnie po aktualizacji Z Spring Boot 1.5.2 do 1.5.6.

Jak również zauważył komentarz @ BigDong, wyjątek, który dostałem, to:

Beaninstantationexception: nie udało się utworzyć instancji [javax.servlet.Filter]: Fabryczna metoda 'springSecurityFilterChain' wyrzuciła wyjątek; zagnieżdżonym wyjątkiem jest org.springframework.fasola.fabryka.BeanNotOfRequiredTypeException: Bean named 'corsFilter' is miał być typu " org.springframework.www.filtr.CorsFilter", ale faktycznie był typu " org.springframework.but.www.servlet.FilterRegistrationBean

Oto, co wymyśliłem, aby uzyskać "globalną" konfigurację CORS dla wszystkich punktów końcowych w naszym REST API, niezależnie od tego, czy są one zaimplementowane przy użyciu Spring Data Rest czy Spring MVC, z wszystkimi punktami końcowymi chronionymi przez Spring Security.

Nie byłem w stanie podłączyć CorsFilter do potoku żądań w odpowiednim miejscu, więc zamiast tego skonfigurowałem SDR i MVC oddzielnie, jednak używając tej samej konfiguracji dla swoich CorsRegistry za pomocą tego helpera:

public static void applyFullCorsAllowedPolicy(CorsRegistry registry) {
    registry.addMapping("/**") //
            .allowedOrigins("*") //
            .allowedMethods("OPTIONS", "HEAD", "GET", "PUT", "POST", "DELETE", "PATCH") //
            .allowedHeaders("*") //
            .exposedHeaders("WWW-Authenticate") //
            .allowCredentials(true)
            .maxAge(TimeUnit.DAYS.toSeconds(1));
}

A następnie dla MVC:

@Configuration
@EnableWebSecurity(debug = true)
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class CustomWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // enables CORS as per
        // https://docs.spring.io/spring-security/site/docs/current/reference/html/cors.html#cors
        http.cors()
            .and() // ...
    }

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                applyFullCorsAllowedPolicy(registry);
            }
        };
    }
}

A następnie dla SDR:

public class CustomRepositoryRestMvcConfiguration extends RepositoryRestConfigurerAdapter {

@Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
    config.setReturnBodyOnCreate(true);
    config.setReturnBodyForPutAndPost(true);
    config.setReturnBodyOnUpdate(true);
    config.setMaxPageSize(250);
    config.setDefaultPageSize(50);
    config.setDefaultMediaType(MediaTypes.HAL_JSON);
    config.useHalAsDefaultJsonMediaType(true);

    CustomWebSecurityConfiguration.applyFullCorsAllowedPolicy(config.getCorsRegistry());
}

Oto kilka dalszych odniesień w temacie, które pomogły mi wymyślić tę odpowiedź:

 6
Author: Johannes Rudolph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-27 12:59:45