Einleitung
Mit den jüngsten Updates in Spring Security wurde die WebSecurityConfigurerAdapter als veraltet markiert. Diese Änderung ermutigt Entwickler, einen funktionalen Ansatz zur Sicherheitskonfiguration zu übernehmen. In diesem Artikel werden wir untersuchen, wie Sie Ihre Spring Security-Konfiguration aktualisieren können, indem Sie die Authentifizierung, HTTP-Sicherheit und Web-Sicherheit ohne die veraltete Adapterklasse konfigurieren. Wir werden auch Beispiele dafür geben, wie Sie verschiedene Endpunkte basierend auf Benutzerrollen wie anonym, Benutzer und Admin testen können.
Einrichtung Ihrer Spring Boot-Anwendung
Bevor wir in die Konfiguration eintauchen, stellen wir sicher, dass wir eine Spring Boot-Anwendung mit den erforderlichen Abhängigkeiten eingerichtet haben. Sie können mit den folgenden Abhängigkeiten in Ihrer pom.xml beginnen:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
Konfiguration der Authentifizierung
In Spring Security 5 und später können Sie Ihren Authentifizierungsmechanismus mit SecurityFilterChain definieren. Hier erfahren Sie, wie Sie die In-Memory-Authentifizierung mit Benutzerrollen konfigurieren.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/user/**").hasAnyRole("USER", "ADMIN")
.requestMatchers("/api/anonymous/**").permitAll()
.anyRequest().authenticated()
)
.httpBasic()
.and()
.csrf().disable();
return http.build();
}
@Bean
public AuthenticationManager authManager(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder =
http.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder
.inMemoryAuthentication()
.withUser("user").password(passwordEncoder().encode("password")).roles("USER")
.and()
.withUser("admin").password(passwordEncoder().encode("admin")).roles("ADMIN");
return authenticationManagerBuilder.build();
}
}
Erklärung
- Wir erstellen eine PasswordEncoder-Bean, um die Passwortcodierung zu behandeln. - Die SecurityFilterChain konfiguriert Autorisierungsregeln für verschiedene Endpunkte basierend auf Benutzerrollen. - Der AuthenticationManager wird für die In-Memory-Authentifizierung mit zwei Benutzern eingerichtet: einer mit der Rolle USER und einer anderen mit ADMIN.
Konfiguration der HTTP-Sicherheit
Neben der Authentifizierung ist die Konfiguration der HTTP-Sicherheit entscheidend für den Schutz der Endpunkte Ihrer Anwendung. Die vorherige Beispielkonfiguration enthält bereits HTTP-Sicherheitskonfigurationen, aber lassen Sie uns etwas ausführlicher darauf eingehen:
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/user/**").hasAnyRole("USER", "ADMIN")
.requestMatchers("/api/anonymous/**").permitAll()
.anyRequest().authenticated()
)
.httpBasic()
.and()
.csrf().disable();
- Die requestMatchers-Methode gibt an, welche Rollen Zugriff auf bestimmte Endpunkte haben. - permitAll() erlaubt anonymen Zugriff auf bestimmte Pfade, während authenticated() von Benutzern verlangt, eingeloggt zu sein. - Die Konfiguration deaktiviert den CSRF-Schutz, den Sie möglicherweise je nach Anforderungen Ihrer Anwendung überdenken möchten.
Konfiguration der Web-Sicherheit
Die Konfiguration der Web-Sicherheit kann auch zusätzliche Einstellungen wie CORS oder Sitzungsmanagement umfassen. Hier ist eine einfache Konfiguration:
http
.cors()
.and()
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
);
- Diese Konfiguration ermöglicht CORS-Anfragen und konfiguriert die Anwendung so, dass sie zustandslos ist, was bedeutet, dass keine Sitzungsinformationen auf der Serverseite gespeichert werden.
Endpunkte-Controller
Lassen Sie uns einen einfachen REST-Controller erstellen, um zu demonstrieren, wie unsere Sicherheitskonfigurationen auf verschiedene Endpunkte basierend auf Benutzerrollen angewendet werden.
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@GetMapping("/api/anonymous/info")
public String anonymousInfo() {
return "Dies sind öffentliche Informationen, die für jeden zugänglich sind.";
}
@GetMapping("/api/user/info")
public String userInfo() {
return "Dies sind Informationen, die für authentifizierte Benutzer zugänglich sind.";
}
@GetMapping("/api/admin/info")
public String adminInfo() {
return "Dies sind sensible Informationen, die nur für Admins zugänglich sind.";
}
}
Testen von Endpunkten
Sie können die Endpunkte mit Tools wie Postman oder cURL testen. Hier erfahren Sie, wie Sie die verschiedenen Rollen testen können:
1. Rolle: Anonymer Benutzer
Um den anonymen Endpunkt zu testen, führen Sie eine GET-Anfrage an /api/anonymous/info aus. Dies sollte eine öffentliche Nachricht ohne Authentifizierung zurückgeben.
2. Rolle: Benutzer
Um die Benutzerrolle zu testen, authentifizieren Sie sich mit dem Benutzernamen user und dem Passwort password. Führen Sie eine GET-Anfrage an /api/user/info aus. Sie sollten eine Nachricht erhalten, die darauf hinweist, dass die Informationen für authentifizierte Benutzer zugänglich sind.
3. Rolle: Admin
Für die Adminrolle authentifizieren Sie sich mit dem Benutzernamen admin und dem Passwort admin. Führen Sie eine GET-Anfrage an /api/admin/info aus. Sie sollten eine Nachricht sehen, die besagt, dass die Informationen nur für Admins zugänglich sind.
Umfassende Tests
Um sicherzustellen, dass unsere Sicherheitskonfigurationen wie erwartet funktionieren, können wir umfassende Tests für alle Rollen implementieren. Hier erfahren Sie, wie Sie diese Tests in Ihrer Anwendung implementieren können:
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
@WebMvcTest(UserController.class)
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@WithMockUser
@Test
public void testAnonymousUser() throws Exception {
mockMvc.perform(get("/api/anonymous/info"))
.andExpect(status().isOk())
.andExpect(content().string("Dies sind öffentliche Informationen, die für jeden zugänglich sind."));
}
@WithMockUser(username = "user", roles = {"USER"})
@Test
public void testUserRole() throws Exception {
mockMvc.perform(get("/api/user/info"))
.andExpect(status().isOk())
.andExpect(content().string("Dies sind Informationen, die für authentifizierte Benutzer zugänglich sind."));
}
@WithMockUser(username = "admin", roles = {"ADMIN"})
@Test
public void testAdminRole() throws Exception {
mockMvc.perform(get("/api/admin/info"))
.andExpect(status().isOk())
.andExpect(content().string("Dies sind sensible Informationen, die nur für Admins zugänglich sind."));
}
@WithMockUser(username = "user", roles = {"USER"})
@Test
public void testAdminRoleAccessDenied() throws Exception {
mockMvc.perform(get("/api/admin/info"))
.andExpect(status().isForbidden());
}
}
Erklärung der Tests
- Die testAnonymousUser-Methode testet den anonymen Endpunkt und erwartet einen 200 OK-Status sowie die richtige Antwortnachricht. - Die testUserRole-Methode testet den Zugriff der Benutzerrolle auf den Benutzer-Endpunkt und erwartet eine erfolgreiche Antwort. - Die testAdminRole-Methode überprüft, ob die Adminrolle auf den Admin-Endpunkt zugreifen kann. - Die testAdminRoleAccessDenied-Methode stellt sicher, dass ein Benutzer ohne Adminrechte nicht auf den Admin-Endpunkt zugreifen kann und erwartet eine 403 Forbidden-Antwort.
Fazit
Das Upgrade von der veralteten WebSecurityConfigurerAdapter zur neuen funktionalen Sicherheitskonfiguration in Spring Security verbessert die Klarheit und Wartbarkeit Ihrer Sicherheitskonfiguration. Durch die Definition von Sicherheitsregeln, Authentifizierungsmethoden und Zugriffsrechten für Endpunkte auf strukturierte Weise gewährleisten Sie eine robustere Sicherheitsarchitektur für Ihre Anwendung. Wenn Sie diese neuen Praktiken übernehmen, denken Sie daran, Ihre Abhängigkeiten auf dem neuesten Stand zu halten und die umfangreichen Funktionen zu erkunden, die Spring Security bietet.
