Spring Security – jak zacząć? Jak dodać do projektu? (+Video 📹)

Zabezpieczenie aplikacji przed nieuprawnionym dostępem to jedna z najważniejszych rzeczy do zrobienia przed wypuszczeniem programu do klientów. Nie możemy sobie pozwolić na to, by dowolny użytkownik aplikacji miał dostęp do wszystkich danych i funkcji systemu. Na szczęście w Springu możemy łatwo o to zadbać z pomocą projektu Spring Security, a w tym wpisie pokażę Ci jak to zrobić 🙂

Aby dodać Spring Security do projektu w Spring Boocie będziemy potrzebować trzech składników:

  1. odpowiednie zależności w projekcie,
  2. konfiguracja endpointów,
  3. konfiguracja użytkowników.

📹 Wolisz obejrzeć wideo? Na końcu wpisu znajdziesz link do webinaru z tym tematem.

Dodanie zależności

Tutaj sprawa jest prosta. Wystarczy dodać odpowiedni starter do zależności (tu przykład w Mavenie i pliku pom.xml) i voila – Security mamy dodane do projektu.

<dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
</dependencies>

Konfiguracja endpointów

W tym momencie cała Twoja aplikacja jest zabezpieczona przed nieuprawnionym dostępem, a jedyny sposób by się do niej dostać to skorzystanie z uwierzytelnienia Basic Auth i dwóch parametrów:

  1. nazwa użytkownika – domyślnie user
  2. hasło – wygenerowane na Twojej konsoli w formacie UUID, np. 63474724-1ceb-4a7d-bc0e-119fd728a915
    • szukaj wpisu Using generated security password: 63474724-1ceb-4a7d-bc0e-119fd728a915

Co jeśli nie chcesz, aby wszystko było zabezpieczone?

W tym celu musisz nadpisać klasę WebSecurityConfigurerAdapter, dodać ją do kontekstu Springa (np. adnotacją @Configuration) i zdefiniować własne reguły autoryzacji.

@Configuration
class BookaroSecurityConfiguration extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
      .authorizeRequests()
      .mvcMatchers(HttpMethod.GET, "/catalog/**").permitAll()
      .anyRequest().authenticated()
    .and()
      .httpBasic();
  }
}

Powyższy fragment oznacza:

  • requesty GET pod adres /catalog i jego podścieżki mają być dostępne dla wszystkich użytkowników – mvcMatchers(HttpMethod.GET, "/catalog/**").permitAll()
  • pozostałe żądania – anyRequest().authenticated() wymagają bycia uwierzytelnionym
  • dodatkowo pozwalamy na dostęp za pomocą Basic Auth – .and().httpBasic()

Gotowe. Teraz możesz sprawdzić, że nie wszystkie endpointy wymagają autoryzacji w Twojej aplikacji.

Konfiguracja użytkowników

Użytkowników można konfigurować na trzy sposoby:

  1. w pamięci aplikacji,
  2. w bazie danych aplikacji,
  3. z zewnętrznych dostawców.

W tym artykule skupimy się na pierwszym sposobie.

Aby skonfigurować użytkowników w pamięci wracamy do klasy konfiguracyjnej BookaroSecurityConfiguration i nadpisujemy kolejną metodę.

@Configuration
class BookaroSecurityConfiguration extends WebSecurityConfigurerAdapter {
  // ...
  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
      .withUser("marek@example.org")
      .password("{noop}xxx")
      .roles("USER")
    .and()
      .withUser("admin")
      .password("{noop}xxx")
      .roles("ADMIN");
  }
}       

Za pomocą obiektu AuthenticationManagerBuilder możemy zdefiniować jakich użytkowników z jakimi rolami chcemy mieć w swoim systemie.

W tym wypadku dodaję dwóch użytkowników:

  • marek@example.org z hasłem xxx i rolą USER,
  • admin z hasłem xxx i rolą ADMIN.

Zapis .password("{noop}xxx") oznacza, że nie chcę korzystać z żadnego (noop => no-operation) algorytmu szyfrującego hasła. Stąd przy próbie dostępu do endpointów w Basic Auth będę przekazywał właśnie xxx jako hasło.

Zabezpieczanie po roli

Skoro mamy już użytkowników z konkretnymi rolami, to możemy je wykorzystać do zabezpieczenia dostępu do konkretnych endpointów w aplikacji. Możemy to osiągnąć korzystając z adnotacji @Secured.

@RestController
@RequestMapping("/admin")
class AdminController {

  @Secured("ROLE_ADMIN")
  @PostMapping("/data")
  public void initialize() {
    // ...
  }
}
@RestController
@RequestMapping("/orders")
class OrdersController {

  @Secured({"ROLE_ADMIN", "ROLE_USER"})
  @GetMapping
  public ResponseEntity<Order> getOrders() {
    // ...
  }
}

Do endpointu /admin będzie miał dostęp tylko administrator, a do endpointu /orders zarówno administrator jak i zwykły, uwierzytelniony użytkownik. Osoba anonimowa nie będzie mogła uzyskać odpowiedzi z systemu.

Aby adnotacja @Secured zadziałała, potrzeba ją explicite włączyć w naszej aplikacji za pomocą @EnableGlobalMethodSecurity(securedEnabled = true).

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
class BookaroSecurityConfiguration extends WebSecurityConfigurerAdapter {
// ...
}

Gotowe!

Twoja aplikacja jest zabezpieczona, wybrane endpointy są schowane za Security, konta zdefiniowane i odpowiednie role przypisane do odpowiednich operacji w aplikacji. W tej chwili dostęp do systemu jest o wiele lepiej chroniony niż wcześniej.

Wersja wideo

Temat poruszaliśmy podczas webinaru w ramach Sztuka Kodu Live, którego nagranie możesz obejrzeć poniżej.

Author: Dariusz Mydlarz

Cześć, nazywam się Dariusz Mydlarz i od 2012 pracuję jako programista. Tworzę w ekosystemie Javy i uwielbiam systemy backendowe. Chcę w tym miejscu pomagać Ci stawać się lepszym programista. Jeśli mogę Ci jakoś pomoc, po prostu napisz do mnie.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *