Java Developer? Przejdź na wyższy poziom wiedzy 🔥💪  Sprawdź

Team Leader? Podnieś efektywność swojego zespołu 👌 Sprawdź

Jak skonfigurować CORS w Spring?

utworzone przez 28 lipca 2020Java, Mikroserwisy i Integracje, Spring Framework

Być może było Ci się spotkać z tym, że zamieszone pliki JS albo CSS nie wczytują się, albo nie ma dostępu do Twojego publicznego RestAPI z frontendu. Dzieje się tak dzięki zabezpieczeniom wbudowanym w przeglądarki (SOP i CORS). W tym wpisie weźmiemy na tapetę konfigurację CORS w Spring. Pokażę Ci różne sposoby konfiguracji Springa, tak aby nasze RestAPI współpracowało z zapytaniami CORS.

Z tego artykułu dowiesz się

  • Jak skonfigurować CORS w Spring?
  • Globalne konfigurowanie CORS w Spring.
  • Konfigurowanie CORS na poziomie klasy i metody.

Przed przeczytaniem tego artykułu powinieneś wiedzieć:

  • Co to jest Same-Origin Policy (SOP).
  • Czym jest Origin.
  • Jak działa CORS.
  • Czym jest preflight request i response.

O czym w szczegółach dowiesz się czytając następujące, powiązane artykuły:

Same-Origin Policy (SOP) a bezpieczeństwo www

Cross-Origin Resource Sharing (CORS) a bezpieczeństwo www

Czym jest CORS?

CORS (czyli Cross-Origin Resource Sharing) to mechanizm bezpieczeństwa, który wykorzystuje dodatkowe nagłówki HTTP, aby poinformować przeglądarkę, czy ma udostępnić dane zwrócone klientowi (zapytanie wykona się poprawnie, więc odpowiedź i tak będzie). Serwer decyduje czy klient jest klientem zaufanym i na tej podstawie ustawia odpowiednie nagłówki, dzięki którym przeglądarka wie, czy ma udostępnić dane klientowi. W ten sposób przeglądarka zabezpiecza klienta przed m.in. atakami typu Cross-site Request Forgery. Atak ten polega na wysyłaniu w imieniu klienta żądań HTTP do złośliwych serwisów, wykorzystując dane klienta (np. sesje, ciasteczka, stan zalogowania itp.). W tym momencie nie będziemy się rozwodzić więcej na temat polityki SOP i CORS, ponieważ bardzo szczegółowo opisałem to w poprzednich artykułach CORS i SOP – do których przeczytania serdecznie zachęcam.

CORS w Spring

Spring, jak to zwykle bywa, bardzo ułatwia prace programistom, pozwalając rozwiązać popularne problemy za pomocą kilku adnotacji. Nie inaczej jest w tym przypadku. Obsługa CORS w Spring jest możliwa na kilka sposobów.

@CrossOrigin na poziomie metody

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

W kodzie powyżej włączyliśmy obsługę CORS tylko dla metody retrive(). Możemy zauważyć, że nie ustawiamy żadnych dodatkowych parametrów w naszej adnotacji @CrossOrigin, a więc:

  • Wszystie originy są akceptowane
  • Akceptowana jest tylko metoda HTTP GET (dlatego, że adnotacji użyliśmy nad metodą GET)
  • Czas życia preflight response jest utrzymywany w cache przez 30 minut (domyślna wartość parametru maxAge)

@CrossOrigin na poziomie kontrolera

@CrossOrigin(origins = "http://softwaresill.pl", 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) {...}
}

W tym przypadku dodaliśmy adnotację @CrossOrigin na poziomie kontrolera. W ten sposób obie metody retrieve() i remove() mają włączoną obsługę CORS. Możemy customizować zachowanie CORS poprzez dodanie parametrów do adnotacji @CorsOrigin tj.:

  • origins – określa akceptowalne originy
  • methods – akceptowalne metody HTTP
  • allowedHeaders – akceptowane nagłówki HTTP
  • exposedHeaders – szerzej opisane tutaj
  • allowCredentials – szerzej opisane tutaj
  • maxAge – czas życia cache przeglądarki dla preflight resonse

@CrossOrigin na poziomie kontrolera i metody

@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
 
    @CrossOrigin("https://softwareskill.pl")
    @RequestMapping(method = RequestMethod.GET, "/{id}")
    public Account retrieve(@PathVariable Long id) {...}

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

W powyższym przykładzie dodaliśmy adnotację @CrossOrigin na poziomie kontrolera i metody. Adnotacja na poziomie kontrolera ustawia parametr maxAge, a ta na poziomie metody ustawia akceptowany origin. W efekcie metoda retrive() jest skonfigurowana na obsługę origin https://softwareskill.pl i czas życia preflight cache na 3600s. Natomiast metoda remove() obsłuży dowolny origin (czas cache taki sam jak przy metodzie retrive())

Globalna konfiguracja CORS w Spring

Jeśli w projekcie mamy więcej endpointów, które wymagają podobnej konfiguracji, zalecana jest wówczas globalna konfiguracja Cross-Origin Resource Sharing (CORS). Jak to zwykle bywa, nie ma jednego rozwiązania. Poniżej prezentuje rozwiązanie, z którego sam z powodzeniem korzystam.

Psst… Interesujący artykuł?

Jeżeli podoba Ci się ten artykuł i chcesz takich więcej – dołącz do newslettera. Nie ominą Cię materiały tego typu.

.

Plik CorsFilterConfiguration.java

@Configuration
@EnableConfigurationProperties(CorsFilterProperties.class)
@RequiredArgsConstructor
public class CorsFilterConfiguration {

    private final CorsFilterProperties properties;

    @Bean("corsFilter")
    public FilterRegistrationBean<CorsFilter> corsFilter() {
        final CorsConfiguration config = buildCorsConfiguration();
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return new FilterRegistrationBean(new CorsFilter(source));
    }

    private CorsConfiguration buildCorsConfiguration() {
        final CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);

        if (properties.getMaxAge() != null) {
            config.setMaxAge(properties.getMaxAge());
        }

        if (!CollectionUtils.isEmpty(properties.getAllowedMethods())) {
            config.setAllowedMethods(properties.getAllowedMethods());
        }

        if (!CollectionUtils.isEmpty(properties.getAllowedHeaders())) {
            config.setAllowedHeaders(properties.getAllowedHeaders());
        }

        if (!CollectionUtils.isEmpty(properties.getAllowedOrigins())) {
            config.setAllowedOrigins(properties.getAllowedOrigins());
        }

        return config;
    }
}

W tym przykładzie skorzystamy z konfiguracji CORS opartej na filtrze, przez który przechodzi każde zapytanie HTTP wysłane do aplikacji. W pierwszym kroku tworzymy beana CorsFilter, który ma za zadanie zarejestrować filtr w kontenerze serveltu. Wewnątrz metody tworzącej filtr FilterRegistrationBean odnosimy się do metody budującej konfigurację CORS, gdzie ustawiamy.:

  • maxAge
  • allowedMethods
  • allowedHeaders
  • allowedOrigins

Metoda budująca zwraca obiekt CorsConfiguration, który wykorzystujemy do stworzenia konfiguracji UrlBasedCorsConfigurationSource. W tej konfiguracji ustawiamy, aby pod dowolny url naszej aplikacji użyć wcześniej skonfigurowanego obiektu CorsConfiguration. W ostatnim kroku, na podstawie wcześniejszych konfiguracji tworzymy filtr, który zostanie zarejestrowany w kontenerze Serveltu.

Plik CorsFilterProperties.java reprezentuje konfigurację CORS, która będzie się znajdować w propertisach naszej aplikacji.

@Data
@Validated
@ConfigurationProperties(prefix = CorsFilterProperties.PREFIX)
class CorsFilterProperties {

    public static final String PREFIX = "web.filter.cors";

    private int order = 100;

    private List<String> urlPatterns;

    private List<String> allowedOrigins;

    private List<String> allowedMethods;

    private List<String> allowedHeaders;

    private Long maxAge;
}

Plik application.properties zawiera docelowe ustawienia CORS dla naszej aplikacji. Warto jest dodać, że pliki properties można podmieniać runtime. Czyli na pracującej aplikacji jesteśmy w stanie zmienić politykę CORS, co jest niezłą możliwością na środowiskach produkcyjnych, kiedy bez restartu aplikacji jesteśmy w stanie podmienić konfigurację.

web.filter.cors.allowed-methods=GET,POST,PUT,DELETE,ORIGIN
web.filter.cors.allowed-headers=Origin,Content-Type,Accept
web.filter.cors.allowed-origins=*

Podsumowanie

Wiesz już drogi czytelniku jak skonfigurować CORS w aplikacji Spring czy Spring Boot. Metod konfiguracji jest mnóstwo. Począwszy od konfiguracji na poziomie kontrolera, metody czy oby dwu. Innym rodzajem obsługi konfiguracji CORS jest ta globalna. Również tutaj jest wiele rozwiązań tego samego problemu. W artykule przedstawiłem rozwiązanie, z którego korzystam na co dzień. Umożliwia ono podmianę konfiguracji runtime, na środowisku produkcyjnym. Jeśli spodobał Ci się artykuł – podziel się nim ze swoimi znajomymi oraz koniecznie zapisz się do naszego newslettera – tak aby nie ominął Cię żaden mięsny artykuł 🙂

Podoba Ci się ten artykuł? Weź więcej.

Jeżeli uważasz ten materiał za wartościowy i chcesz więcej treści tego typu – nie przegap ich i otrzymuj je prosto na swoją skrzynkę. Nawiążmy kontakt.

.

Gdybyś potrzebował jeszcze więcej:

Jesteś Java Developerem?

Przejdź na wyższy poziom wiedzy 
„Droga do Seniora” 🔥💪

Jesteś Team Leaderem? Masz zespół?

Podnieś efektywność i wiedzę swojego zespołu 👌

Piguła wiedzy o najlepszych praktykach testowania w Java

Pobierz za darmo książkę 100 stron o technikach testowania w Java

Obrazek: Designed by macrovector / Freepik

Dyskusja