Przejdź do treści

Użycie Spring Security w Spring Boot

Często przygotowując aplikację webową, chcemy posiadać sekcję, która będzie dostępna tylko dla zalogowanych użytkowników lub jakiś panel administracyjny, który pozwoli na zarządzanie stroną. Spring udostępnia gotowy mechanizm autentykacji i autoryzacji użytkowników, z którego możemy w łatwy sposób skorzystać. W poście postaram się pokazać jak w najprostszy sposób skonfigurować autentykację, czyli logowanie użytkownika do systemu. Kod, który używam, jest możliwie jak najprostszy. Pomijam tutaj używanie widoków i innych komponentów skupiając się tylko na tym, co jest niezbędne do rozpoczęcia pracy z security.

Przygotowanie

Zaczniemy od prostego projektu w Spring Boocie kompilowanego przy pomocy Mavena. Całość uruchomiona jest w Javie 8, jednak jeśli ktoś uprze się na 7, to powinno to przy minimalnym wysiłku lub nawet bez niego zadziałać.

Stwórzmy więc nowy projekt w Mavenie i dodajmy niezbędne zależności do uruchomienia projektu. Mój pom.xml wygląda następująco:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>pl.mloza.spring.boot</groupId>
<artifactId>security</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
view raw pom.xml hosted with ❤ by GitHub

Pom zawiera definicję projektu z parentem ustawionym na spring-boot-starter-paren oraz zależność do spring-boot-starter-web która umożliwi tworzenie aplikacji web.

Następnym krokiem jest utworzenie klasy startowej, która umożliwi uruchomienie aplikacji. Moja klasa wygląda następująco:

package pl.mloza.spring.boot.security;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@SpringBootApplication
@Controller
public class Main {
@RequestMapping("/")
@ResponseBody
public String mainAction() {
return "Hello World";
}
public static void main(String[] args) {
SpringApplication.run(Main.class);
}
}

view raw Main.java hosted with ❤ by GitHub

Klasa zawiera funkcję main, która uruchamia aplikację Spring Boota. Jak pisałem wcześniej, ograniczam ilość tworzonego kodu do minimum, klasę startującą połączyłem od razu z kontrolerem, który zwraca Hello World jako treść strony. Aktualnie powinniśmy być w stanie uruchomić projekt i po przejściu na stronę http://localhost:8080 powinien się nam ukazać napis Hello World.

Spring Boot Security

Jeśli mamy aplikację teraz przejdźmy do jej zabezpieczenia. Do naszego poma, dodajemy zależność spring-boot-starter-security:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
view raw dependency.xml hosted with ❤ by GitHub

Po odświeżeniu projektu Mavena, restarcie aplikacji i odświeżeniu strony, powinniśmy dostać zapytanie o użytkownika i hasło. Domyślna nazwa użytkownika to „user”, a hasło jest generowane losowo i wypisywane na konsolę:

2015-08-26 14:03:53.991  INFO 2652 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2015-08-26 14:03:53.991  INFO 2652 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 4092 ms
2015-08-26 14:03:55.781  INFO 2652 --- [ost-startStop-1] b.a.s.AuthenticationManagerConfiguration : 

Using default security password: 3931d77b-230d-4ef6-9be0-95bca8ab77ab

2015-08-26 14:03:55.891  INFO 2652 --- [ost-startStop-1] o.s.s.web.DefaultSecurityFilterChain     : Creating filter chain: Ant [pattern='/css/**'], []
2015-08-26 14:03:55.891  INFO 2652 --- [ost-startStop-1] o.s.s.web.DefaultSecurityFilterChain     : Creating filter chain: Ant [pattern='/js/**'], []

Wpisanie poprawnych danych pozwoli nam zobaczyć napis Hello World, w przypadku podania nieprawidłowych danych system poprosi o nie jeszcze raz i kolejny. Anulowanie spowoduje wyświetlenie domyślnej strony błędu.

Hasło można również ustawić w konfiguracji aplikacji za pomocą właściwości security.user.password.

Konfiguracja użytkownika i hasła

Jedną z możliwości jest podanie nazwy użytkownika i hasła bezpośrednio w kodzie. Nie jest to dobrą praktyką, ale dla bardzo prostych potrzeb jest to wystarczające. Dodatkowo posłuży to jako punkt wyjścia do kolejnych ulepszeń.

Zacznijmy od stworzenia klasy konfiguracyjnej dla security, gdzie dodamy kod tworzący użytkowników. Może to wyglądać następująco:

package pl.mloza.spring.boot.security.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("michal")
.password("password")
.roles("USER", "ADMIN");
}
}

Klasa powinna rozszerzać klasę WebSecurityConfigurerAdapter oraz posiadać adnotację @Configuration. W ten sposób zostanie automatycznie załadowana i uruchomiona. W metodzie configureGlobal otrzymujemy obiekt klasy AuthenticationManagerBuilder, dzięki której możemy dodać użytkowników. Jeśli potrzebujemy więcej niż jednego użytkownika, możemy zamienić kod metody na coś takiego:

InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> authenticationManagerBuilderInMemoryUserDetailsManagerConfigurer =
auth.inMemoryAuthentication();
authenticationManagerBuilderInMemoryUserDetailsManagerConfigurer
.withUser("michal")
.password("password")
.roles("USER", "ADMIN");
authenticationManagerBuilderInMemoryUserDetailsManagerConfigurer
.withUser("user2")
.password("passwd")
.roles("USER");

W ten sposób możemy dodawać kolejnych użytkowników. Następnie restartujemy aplikację i próbujemy się logować za pomocą podanych danych.

Zabezpieczanie tylko wybranych stron

W domyślnej konfiguracji zabezpieczamy wszystkie strony, jednak w zdecydowanej większości część stron powinna być dostępna dla użytkowników. Spróbujmy zmienić konfigurację tak, aby strona główna była dostępna dla wszystkich a zasób pod adresem /loggeduser wymagał zalogowania. Zacznijmy od dodania do kontrolera metody obsługującej zasób /loggeduser, do klasy Main dodajemy metodę z adnotacją:

@RequestMapping("/loggeduser")
@ResponseBody
public String loggedUserAction() {
return "Hello User";
}
view raw Controller.java hosted with ❤ by GitHub

Metoda ta nie robi nic innego, jak tylko zwraca napis Hello User. Następnie przejdźmy do konfiguracji security w klasie SecurityConfig i dodajmy metodę config z zawartością:

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/loggeduser").authenticated()
.and().formLogin();
}
view raw Auth.java hosted with ❤ by GitHub

Metoda ta definiuje sposób logowania oraz jakie zasoby mają być chronione a jakie dostępne. Metoda antMatchers może przyjmować adresy z gwiazdkami więc można w łatwy sposób zdefiniować, co ma być chronione. Po zdefiniowaniu adresów zasobów mówimy, co ma być zrobione, permitAll zezwala na dostęp, authenticated wymaga autentykacji. Na końcu mamy formLogin, mówi to, że dozwolone jest logowanie za pomocą formularza. Sam formularz jest generowany automatycznie, jak go zastąpić pokażę w innym poście. Jeśli zabraknie ostatniego wywołania w odpowiedzi na żądanie chronionego zasobu, otrzymamy błąd 403 bez możliwości zalogowania. Dodając ostatnie wywołanie, zostaniemy przekierowani na stronę z formularzem. Po restarcie aplikacji i wejściu na stronę główną powinniśmy zobaczyć napis Hello World bez potrzeby logowania. Wejście na adres http://localhost:8080/loggeduser przekieruje nas na stronę logowania, po podaniu danych logowania zostaniemy przeniesieni na docelową stronę i zobaczmy napis „Hello User”.

Podsumowanie

Post ten pokazuje podstawową konfigurację Spring Security. Dzięki temu jesteśmy w stanie zapewnić podstawową ochronę stron, udostępnić je tylko wybranym osobom. Biblioteka dostarcza możliwość autentykacji za pomocą między innymi JDBC, LDAPA lub pozwala na napisanie własnego sterownika autentykacji i autoryzacji. Jak tego dokonać postaram się pokazać w kolejnym poście.

3 komentarze do “Użycie Spring Security w Spring Boot”

  1. Spoko!

    Jeszcze bardziej gdyby był przykład z wyciąganiem userów z bazy danych mysql.

    Oraz zastąpienie tego generowanego formularza własnym.

    Ale ze ten wpis 10/10 🙂

    1. Dziękuje! Dodanie interakcji z bazą i formularza mam w planie, niestety ostatnio natłok innych obowiązków blokuje pisanie postów.

  2. A szkoda, że brak czasu, bo naprawdę prowadzisz bloga w świetny sposób. Już wcześniej skomentowałem, zrobiłem sobie przerwę na inne rzeczy od kodzenia, teraz wróciłem i znowu muszę Cię pochwalić – jesteś świetny w tworzeniu poradników w formie tekstowej. Naprawdę, jeśli stworzyłbyś bardziej kompletny, rozbudowany zbiór porad – to myślę, że to mogłaby być najlepsza polskojęzyczna strona dla osób, które chcą się nauczyć programować, ale nie są otrzaskane w środowisku informatycznym, nie poruszają się w nim zbyt sprawnie. Trzymam kciuki, zmieniasz Polskę!

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

%d