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.

Dodaj komentarz

Tw贸j adres e-mail nie zostanie opublikowany. Wymagane pola s膮 oznaczone *