Hier entwickeln wir Microservices und IT-Systemen für die Cloud mit Java oder Kotlin. Für die einfache Nutzung von Datenbanken (z.B. MongoDB), Security- oder Frontend-Technologien stelle ich das Spring Framework mit dem Spring Boot Projekt vor. Mit Clean Code Prinzipen und Test-Automatisierungstechniken zeige ich, wie Wartbarkeit und Qualität sichergestellt wird.
Authentifizierung in Web-Anwendungen mit Spring Security 6
Link abrufen
Facebook
X
Pinterest
E-Mail
Andere Apps
Spring Security hilft uns beim Authentifizieren und Autorisieren von Benutzern in Java Web-Anwendungen. Da Spring Security ein mächtiges und komplexes Framework ist, zeige ich in diesem Einstiegsartikel, wie wir die von Spring Boot vorkonfigurierte Spring Security Authentifizierung nutzen und anpassen können.
In diesem Artikel demonstriere ich Spring Security 6 passend zum aktuellen Spring Framework in Version 6. Das Demo Projekt wurde mit der neuen Spring Boot Version 3 erstellt.
Spring Security
Spring Security ist eins mächtiges und flexibel anpassbares Authentifizierungs- und Zugangskontroll-Framework. In der Praxis ist es der Standard zum Absichern von Spring basierten Web-Anwendungen.
Mit Spring Boot kann man Spring Security einfach beim Aufsetzen eines neuen Projektes hinzufügen. Das funktioniert so, wie ich es schon in diesem Artikel gezeigt habe: microservices-mit-spring-boot-erstellen.html Allerdings kann man Spring Security auch einfach als Maven Dependency zu existierenden Spring oder Spring Boot Projekten hinzufügen, bei Spring Boot Projekten sieht das so aus:
Authentifizierung mit dem Spring Security Standard Benutzer
Nach dem Hinzufügen der Spring Security Dependency ist die Spring Boot Web-Anwendung standardmäßig mit Benutzername und Passwort abgesichert. Das sieht man dann in den Log-Einträgen beim Starten der Anwendung:
...Initializing Spring embedded WebApplicationContext
...Root WebApplicationContext: initialization completed in 909 ms
.s.s.UserDetailsServiceAutoConfiguration :
Using generated security password: 0139...
...Adding welcome page template: index
o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [org.spring...security...WebAsyncManagerIntegrationFilter@e6cffb4, org.spring...security...DefaultLoginPageGeneratingFilter@6507a4d2, ...
In den Logs steht das automatisch, generierte Passwort für den Standard Benutzer mit dem Benutzernamen "user": "Using generated security password: 0139...". Bei jedem Start der Anwendung wird ein neues Passwort generiert. Außerdem sehen wir noch die Log-Meldung das jeder beliebige Request mit einer Liste von diversen Spring Security Filtern abgesichert wird: "DefaultSecurityFilterChain: Will secure any request with...".
Rufen wir nun im Browser die Web-Application auf (http://localhost:8080), werden wir von den Spring Security Filtern automatisch zur Spring Security Login-Seite weitergeleitet. Der Login-Dialog wird im folgenden Screenshot gezeigt.
Nach erfolgreichem Login als Benutzer "user" wird der Request wie gewohnt von unseren Spring Controllern verarbeitet, wie das funktioniert ist z.B. hier gezeigt: spring-mvc-thymeleaf.html
Spring Security Filter Chain
Vereinfacht gesagt, ist Spring Security eine zusätzlich Schicht zwischen dem Client und der Spring Anwendung. Die Spring Security Schicht prüft, ob die Requests des Clients den Security Regeln der Anwendung entsprichen. Security Regeln definieren wir in Form von Filtern. Die Filter bilden eine Kette (Filter Chain). Jeder eingehende Request durchläuft die Filter der Kette entsprechend ihrer Reihenfolge. Dabei prüft jeder Filter, ob dem Request Zugriff auf die Anwendung gewährt wird oder nicht.
Die Logausgabe listet z.B. den Filter DefaultLoginPageGeneratingFilter auf. Wenn wir diese Klasse in unserer IDE öffnen, sehen wir, dass die Superklasse das Filter Interface implementiert. In DefaultLoginPageGeneratingFilter gibt es daher auch die Implementierung der Methode doFilter. In doFilter wird entschieden, ob die Login Seite angezeigt wird oder nicht. Wenn ihr einen Breakpoint in doFilter setzt und die Anwendung im Debug-Modus startet, seht ihr, dass dieser Filterbei jedem Request benutzt wird.
Standard Benutzer konfigurieren
Wenn unsere Web-Anwendungen mit einem Benutzer auskommt, können wir den Standard Benutzer auch per Konfiguration anpassen. Dazu können wir in der application.properties Datei den Benutzernamen und das Passwort selbst definieren:
# Replace Spring Security default username "user" with:
spring.security.user.name=admin-user
# Replace Spring Securtiy randomly generated password with:
spring.security.user.password=geheim123
Nach einem Neustart können wir uns nun als Benutzer "admin-user" mit dem Passwort "geheim123" einloggen. Spring Boot bietet neben der application.properties Datei noch einige andere Möglichkeiten, um Konfigurationen vorzunehmen. So können wir das Passwort auch an einer anderen Stelle definieren - weitere Infos dazu findet ihr hier: https://docs.spring.io/spring-boot/docs/1.0.1.RELEASE/reference/html/boot-features-external-config.html
Mehrere Benutzer und Rollen definieren
Spring Security unterstützt beliebig viele, verschiedene Benutzer. Diese können sich in einer Datenbank, in einem LDAP-Server oder anderen Identity Provider befinden - es werden alle gängigen Lösungen unterstützt. Alternativ kann man Spring Security auch wie benötigt anpassen. Um diesen Einstiegsartikel möglichst einfach zu halten, stelle ich hier nur eine einfache Benutzer-Verwaltung im Speicher vor. Alles was wir dazu brauchen ist eine einfache Spring Bean, die das Interface UserDetailsService implementiert.
@Configuration
public class SecurityConfiguration {
@Bean
public 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);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Die UserDetailsService Bean habe ich per @Bean Annotation in einer Spring Konfigurationsklasse (@Configuration) erstellt. Dieses Vorgehen ist Standard in Spring, siehe dazu auch kernkonzepte-von-spring.html
Konkret wird dieUserDetailsService Bean in Form einer Instanz von InMemoryUserDetailsManager bereitgestellt. Dem Konstruktor-Aufruf kann man dabei beliebig viele Benutzer Objekte übergeben (hier im Beispiel sind es nur 2). Alternativen zur Benutzerverwaltung im Speicher mittels InMemoryUserDetailsManager sind zum Beispiel JdbcUserDetailsManager oder CachingUserDetailsService.
Die beiden Benutzer wurden hier einfach im Java-Code erstellt. Benutzername und Password sind hier im Beispiel hardcoded - in der Praxis sollte man Credentials auch beim Verwalten der Benutzer im Speicher aus der Spring Konfiguration lesen. Die Benutzer (Instanz von UserDetails) werden mittels UserBuilder (User.builder()) erstellt. Das Setzen von username, password und roles (bzw. der Aufruf der gleichnamigen Builder-Methoden) ist dabei Pflicht - alle anderen UserDetails Attribute sind optional. Rollen (roles) werde ich in einem künftigen Blog-Artikel vorstellen, den Demo-Code dazu findet ihr aber jetzt schon im GitHub-Repository zu diesem Artikel.
Das Passwort muss encodiert im UserBuilder gesetzt werden. Das mache ich mit der Bean PasswordEncoder, die eine einfache Instanz der BCryptPasswordEncoder Implementierungsklasse ist.
Nach einem Neustart der Spring Boot Applikation könnt ihr den Login mit beiden Benutzern testen, indem ihr die URL http://localhost:8080 jeweils in einem neuen Incognito Browser-Fenster öffnet. Da wir hier noch keine Logout-Funktion implementiert haben, empfehle ich zum Testen den Incognito Modus des Browsers.
Fazit & nächste Schritte
In diesem Blog-Artikel habe ich gezeigt, wie man Spring Security zu einem Spring Boot Projekt hinzufügen kann und was die Standard-Konfiguration von Spring Security mitbringt. In einem kleinen nächsten Schritt haben wir weitere Benutzer für unsere Spring Security abgesicherte Web-Anwendung erstellt.
Möchtet ihr tiefer in das Thema einsteigen? Dann schaut euch noch diese Artikel an:
Spring Security ermöglicht das Anpassen der Authentifizierung und Zugangskontrolle, indem man vorhandene Module konfiguriert oder eigene schreibt und sie in die Filter-Chain aufnimmt. Hier eine Liste vorhandener Module: https://docs.spring.io/spring-security/site/docs/5.4.6/reference/html5/#modules
Alternativ könnte man die Spring Web-Anwendung in ein privates Netzwerk packen und dann den Zugriff über eine DMZ ermöglichen: https://de.wikipedia.org/wiki/Demilitarisierte_Zone_%28Informatik%29
SecuRemote kenne ich nicht, aber eine dieser beiden Varianten sollte sicher damit funktionieren.
Authentifizierung mit OpenID Connect geht einfach dank Spring Boot. Wir bauen den Login Deiner Web-Anwendung mit dem Autorisierungsserver Deiner Firma. Wie wir dazu OpenID Connect mit Spring Boot 3 konfigurieren, zeige ich in diesem Blog-Artikel. OpenID Connect - Authorization Code Prozess OpenID Connect ist ein Single Sign-On Login-Verfahren. Zur Umsetzung komplexer Anwendungsfälle verwenden größere Firmen meist verteilte Systeme. Damit der Benutzer z. B. beim Online-Shopping den Systemwechsel von Produktseiten zum Einkaufswagen und zur Kasse nicht wahrnimmt, loggt er sich mittels Single Sign-On nur einmal ein. Die beteiligten Systeme authentifizieren den eingeloggten Benutzer anhand seiner Single Sign-On Session. Detaillierte Informationen über OpenID Connect und Single Sign-On findet ihr hier . In diesem Artikel fokussiere ich mich auf das Anwendungs-System, welches zur Benutzer-Authentifizierung den firmeneigenen OpenID Identity Provider verwendet. Bevor wir uns die Implementierung
Mit Spring können zeitgesteuerte Aufgaben in Java Code integriert werden. CronJobs wie wir sie in Linux kennen, definieren wir mit Spring einfach per Annotation. In diesem Artikel zeige ich wie das geht und wie Spring die CronJobs entsprechend unserer Definition ausführt. Was ist ein CronJob? Unter CronJob verstehen wir die zeitlich gesteuerte Ausführung eines Kommandos zur Erledigung einer Aufgabe bzw. eines Jobs. Das Kommando wird durch einen bestimmten Zeitpunkt oder eine zeitliche Bedingung angestoßen. Typische Beispiele für durch CronJobs gestartete Aufgaben sind: Regelmäßiges Aufräumen der Datenbank - z. B. um veraltete Daten zu löschen oder DSGVO konform persönliche Daten nach einer definierten Zeit zu löschen. Wöchentlicher Versand von Newslettern oder Werbung per Email Nächtliche Datenbank-Backups Monatliches Erstellen von Rechnungen (z.B. Telefon-Rechnung) Das Betriebssystem Linux bietet crontab zum Erstellen von CronJobs an. Mit crontab könnten wir eine Spring Anwendung star
In diesem Blog-Post besprechen wir die Basics von Spring: Spring Beans und wie man diese miteinander vernetzt bzw. referenziert (Dependency Injection). Den kompletten Code zu diesem Blog-Post findet ihr in GitHub: https://github.com/elmar-brauch/beans Spring Bean Spring Beans sind Java Objekte, die durch den Spring IoC Container instanziiert und verwaltet werden. Der IoC (Inversion of Control) Container erstellt Beans anhand einer Bean Definition, die der Entwickler in Form von Annotationen oder xml Konfiguration bereitstellt. IoC ist ein Umsetzungsparadigma und bedeutet Steuerungsumkehr. Im Kontext des Spring Frameworks versteht man darunter, dass das Framework die Erstellung des Objektnetzes (Beans) anstelle des Entwicklers übernimmt. In vorherigen Blog-Artikel haben wir bereits Projekte mit Spring Boot aufgesetzt, siehe z.B. microservices-mit-spring-boot-erstellen.html . In diesen Projekten verwendeten wir die Annotation @SpringBootApplication , welche eine Aggregation diverser
Kommentare
indem man vorhandene Module konfiguriert oder eigene schreibt und sie in die Filter-Chain aufnimmt.
Hier eine Liste vorhandener Module: https://docs.spring.io/spring-security/site/docs/5.4.6/reference/html5/#modules
Alternativ könnte man die Spring Web-Anwendung in ein privates Netzwerk packen
und dann den Zugriff über eine DMZ ermöglichen: https://de.wikipedia.org/wiki/Demilitarisierte_Zone_%28Informatik%29
SecuRemote kenne ich nicht, aber eine dieser beiden Varianten sollte sicher damit funktionieren.