Aby dodać Spring Security do projektu w Spring Boocie będziemy potrzebować trzech składników:
- odpowiednie zależności w projekcie,
- konfiguracja endpointów,
- konfiguracja użytkowników.
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:
- nazwa użytkownika - domyślnie
user
- 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
- szukaj wpisu
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:
- w pamięci aplikacji,
- w bazie danych aplikacji,
- 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łemxxx
i roląUSER
,admin
z hasłemxxx
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.