feat!: initial prototype
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
package eu.bitfield.recipes.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.context.NoOpServerSecurityContextRepository;
|
||||
import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;
|
||||
|
||||
import static org.springframework.http.HttpMethod.*;
|
||||
|
||||
@Configuration
|
||||
@EnableWebFluxSecurity
|
||||
public class SecurityConfiguration {
|
||||
private SecurityWebFilterChain filterChainDefaults(ServerHttpSecurity http) {
|
||||
// disable session management
|
||||
// https://github.com/spring-projects/spring-security/issues/6552#issuecomment-519398510
|
||||
return http.csrf(ServerHttpSecurity.CsrfSpec::disable)
|
||||
.securityContextRepository(NoOpServerSecurityContextRepository.getInstance())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE) @Bean
|
||||
public SecurityWebFilterChain accountEndpointFilterChain(ServerHttpSecurity httpSecurity) {
|
||||
httpSecurity.securityMatcher(new PathPatternParserServerWebExchangeMatcher("/api/account/**"))
|
||||
.authorizeExchange(exchange -> exchange.anyExchange().permitAll());
|
||||
return filterChainDefaults(httpSecurity);
|
||||
}
|
||||
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE) @Bean
|
||||
public SecurityWebFilterChain recipeEndpointFilterChain(ServerHttpSecurity httpSecurity) {
|
||||
httpSecurity.securityMatcher(new PathPatternParserServerWebExchangeMatcher("/api/recipe/**"))
|
||||
.httpBasic(Customizer.withDefaults())
|
||||
.authorizeExchange(exchange -> {
|
||||
exchange.pathMatchers(GET, "/api/recipe/{recipeId:\\d+}").permitAll()
|
||||
.pathMatchers(GET, "/api/recipe/search").permitAll()
|
||||
.anyExchange().authenticated();
|
||||
});
|
||||
return filterChainDefaults(httpSecurity);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityWebFilterChain fallbackFilterChain(ServerHttpSecurity httpSecurity) {
|
||||
httpSecurity.authorizeExchange(exchange -> exchange.anyExchange().denyAll());
|
||||
return filterChainDefaults(httpSecurity);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id
|
||||
// accessed at 2025-04-22
|
||||
int saltLength = 16;
|
||||
int hashLength = 32;
|
||||
int parallelism = 1;
|
||||
int memory = 1 << 16; // in KiB = 64 MiB
|
||||
int iterations = 2;
|
||||
return new Argon2PasswordEncoder(saltLength, hashLength, parallelism, memory, iterations);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user