Spring Security 6: Login, Logout & Rollen
Spring Security ermöglicht Login und Logout für Web-Anwendungen in wenigen Konfigurationsschritten. Mit Spring Security sichern wir einzelne Webseiten oder API-Endpunkte so ab, dass nur authentifizierte Benutzer mit festgelegten Rollen zugreifen können. Wie funktioniert das? Lest weiter!
Hinweis: Für einen ersten Einblick zu Spring Security, schaut euch gerne meinen Einsteiger-Artikel an. Dort seht ihr wie Spring Security zum Spring Boot hinzugefügt wird und wie Benutzer mit Rollen definiert werden.
Abgesicherte und frei zugängliche Seiten
Die Spring Security Starter Dependency ist ohne weitere Konfiguration produktionsbereit, da sie alles absichert. Nur eingeloggte Benutzer haben Zugang. Um anonymen Benutzern bestimmte Seiten zugänglich zu machen, müssen wir die Authentifizierung und den Default Login anpassen.
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
@Bean
SecurityFilterChain authenticatedAndFreePagesWithLogin(
HttpSecurity http) throws Exception {
return http.authorizeHttpRequests()
.requestMatchers("/admin").authenticated()
.anyRequest().permitAll()
.and().formLogin()
.and().build();
}
...
- Eine eigene SecurityFilterChain Bean ermöglicht uns in die Security Filter-Kette von Spring einzugreifen und Default Einstellungen zu ändern. Diese Änderungen konfigurieren wir am HttpSecurity Objekt für Web-Anwendungen. Die build Methode des HttpSecurity Objektes erzeugt eine neue DefaultSecurityFilterChain Instanz. Die Bean Definition findet in einer mit @EnableWebSecurity und @Configuration annotierten Klasse statt.
- http.authorizeHttpRequests().requestMatchers("/admin").authenticated() legt fest, dass der Pfad /admin nur für authentifizierte Benutzer zugänglich ist.
Seit dem Update auf Spring Security 6 müssen die Methoden authorizeHttpRequests anstatt authorizeRequests und requestMatchers anstatt antMatchers verwendet werden. - .anyRequest().permitAll() gibt anonymen und eingeloggten Benutzer Zugang auf allen anderen URL-Pfaden. Ohne .requestMatchers("/admin").authenticated() wäre unsere komplette Web-Anwendung offen bzw. ohne Absicherung nutzbar.
- .and() gibt uns immer wieder das HttpSecurity Objekt zurück, so dass wir weitere Konfigurationen vornehmen können. Alternativ könnten wir statt .and() zu verwenden auch ein neues Statement mit dem HttpSecurity Objekt beginnen.
- .formLogin() aktiviert den Default Login von Spring Security. Wenn ein anonymer Benutzer eine mit .authenticated() abgesicherte URL (hier /admin) aufruft, findet ein Redirect zur Default Login Seite statt. Die Default URL der Login-Seite ist /login - Anpassungen sind natürlich möglich.
Logout anpassen - aber wie?
Mit dem Login bekommen wir automatisch einen Logout, den wir über den Pfad /logout erreichen. Im Browser sieht es wie folgt aus:
Bei Bedarf konfigurieren wir den Logout ebenfalls in der SecurityFilterChain Bean.
Beispielsweise legen wir fest, welche URL nach erfolgreichem Logout aufgerufen wird:
Beispielsweise legen wir fest, welche URL nach erfolgreichem Logout aufgerufen wird:
...
.and().formLogin()
.and().logout().logoutSuccessUrl("/index")
Zugriff nur mit autorisierter Rolle
Bis jetzt hat jeder eingeloggte Benutzer Zugriff auf die /admin Seite. Das ändern wir im nächsten Schritt, so dass nur noch Administratoren auf diese Seite zugreifen. Dazu definieren wir, dass nur die Rolle ADMIN für die /admin Seite berechtigt ist. Normale Benutzer haben hier nur die Rolle USER - ihnen ist der Zugriff verboten. Schauen wir uns zuerst die Benutzer und deren Rollen an.
@Bean
UserDetailsService users(@Autowired PasswordEncoder pwEnc) {
UserDetails user = User.builder()
.username("user")
.password(pwEnc.encode("top"))
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password(pwEnc.encode("secret"))
.roles("USER", "ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
- Die UserDetailsService Bean und ihre Alternativen erkläre ich im ersten Spring Security Artikel.
- Wichtig: In UserDetails Objekten legen wir sowohl Credentials als auch Rollen der Benutzer fest. Dabei kann ein Benutzer mehrere Rollen bekommen. Das sind dann die einzelnen String Parameter der Methode roles.
Jetzt sichern wir die /admin Seite durch die Rollenüberprüfung ab. Dazu konfigurieren wir in unserer SecurityFilterChain Bean die folgende fett markierte Stelle:
@Bean
SecurityFilterChain authenticatedAndFreePagesWithLogin(
HttpSecurity http) throws Exception {
return http.authorizeHttpRequests()
.requestMatchers("/admin").hasRole("ADMIN")
.anyRequest().permitAll()
.and().formLogin()
.and().build();
}
- Der Methodenaufruf von .authenticated() wurde durch .hasRole("ADMIN") ersetzt. Dabei wird die Rolle ADMIN wird als String Parameter übergeben. Der Methodenaufruf .requestMatchers("/admin") legt fest, an welchen URL-Pfaden die Rolle ADMIN überprüft wird.
- Logt ihr euch als Benutzer "user" ein und öffnet im Browser die Seite http://localhost:8080/admin, bekommt ihr den HTTP Status-Code 403 mit "Forbidden" Nachricht. Denn nur mit USER Rolle und ohne ADMIN Rolle habt ihr in diesem Testfall keine Zugriffsrechte.
- Hinweis: Der HTTP Status Code 403 befindet sich auf einer Whitelabel Error Page, da ich hier keine eigenen Fehlerseiten verwende.
Fazit
So leicht steigen wir mit Spring Security in die Themen Login, Logout und Rollenmanagement ein. Spring Security ist ein anpassbares Framework. Ihr könnt beispielsweise eigene Login und Logout Seiten hinterlegen oder andere Verfahren wie OpenId Connect einsetzen, siehe dazu auch OpenID Connect mit Spring Boot.
Spring Security hat eine sichere Default-Konfiguration. Damit kommen wir nach kurzer Adaption schnell zu einen sicheren Produktivsystem.
Das Update auf Spring Security 6 habe ich mit dem Spring Boot Update auf Version 3.0.0 gemacht. Da in Spring Security 6 deprecated Methoden (z. B. antMatchers) entfernt wurden, musste ich Code Anpassungen vornehmen. Den kompletten Code und die Spring Security 6 Code Anpassungen findet ihr in GitHub bzw. in der Git-Historie: https://github.com/elmar-brauch/spring-security-webapp.git
Kommentare