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 *