SecurityConfig.java
package de.mirkosertic.powerstaff.config;
import de.mirkosertic.powerstaff.auth.MustChangePasswordFilter;
import de.mirkosertic.powerstaff.auth.PsAuthenticationSuccessHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import java.util.HashMap;
import java.util.Map;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final MustChangePasswordFilter mustChangePasswordFilter;
private final PsAuthenticationSuccessHandler authenticationSuccessHandler;
public SecurityConfig(final MustChangePasswordFilter mustChangePasswordFilter,
final PsAuthenticationSuccessHandler authenticationSuccessHandler) {
this.mustChangePasswordFilter = mustChangePasswordFilter;
this.authenticationSuccessHandler = authenticationSuccessHandler;
}
@Bean
public SecurityFilterChain securityFilterChain(final HttpSecurity http) {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/login", "/error", "/css/**", "/generated/**").permitAll()
.requestMatchers("/admin/historientypen/**", "/admin/positionsstatus/**", "/admin/tags/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.successHandler(authenticationSuccessHandler)
.failureUrl("/login?error")
.permitAll()
)
.logout(logout -> logout
.logoutSuccessUrl("/login?logout")
.permitAll()
)
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
)
.addFilterAfter(mustChangePasswordFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
/**
* DelegatingPasswordEncoder: Standard-Algorithmus ist bcrypt.
* Unterstützt {bcrypt} und {noop} Prefixe in der DB.
* Passwörter ohne Prefix werden ebenfalls mit BCrypt geprüft (Legacy-Kompatibilität).
* ADR: Neue Hashes werden immer mit {bcrypt} Prefix gespeichert.
*/
@Bean
@SuppressWarnings("deprecation")
public PasswordEncoder passwordEncoder() {
final Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put("bcrypt", new BCryptPasswordEncoder());
encoders.put("noop", NoOpPasswordEncoder.getInstance());
final DelegatingPasswordEncoder delegating = new DelegatingPasswordEncoder("bcrypt", encoders);
delegating.setDefaultPasswordEncoderForMatches(new BCryptPasswordEncoder());
return delegating;
}
}