WebSecurity.java
package se.jobtechdev.personaldatagateway.api.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter;
import org.springframework.security.web.header.HeaderWriterFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import se.jobtechdev.personaldatagateway.api.security.RequestHeaderAuthenticationProvider;
import se.jobtechdev.personaldatagateway.api.security.RequestMethodPattern;
import java.util.Collections;
import java.util.function.Predicate;
@Configuration
@EnableWebSecurity
public class WebSecurity {
private static final Logger logger = LoggerFactory.getLogger(WebSecurity.class);
private final RequestHeaderAuthenticationProvider requestHeaderAuthenticationProvider;
private final SecurityConfig securityConfig;
@Autowired
public WebSecurity(
RequestHeaderAuthenticationProvider requestHeaderAuthenticationProvider,
SecurityConfig securityConfig) {
this.requestHeaderAuthenticationProvider = requestHeaderAuthenticationProvider;
this.securityConfig = securityConfig;
}
public static Predicate<RequestMethodPattern> isRolePopulated() {
return methodPattern -> !methodPattern.roles().isEmpty();
}
public Customizer<
AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry>
getAuthorizeHttpRequestsCustomizer() {
return registry -> {
final var methodPatterns = securityConfig.getMethodPatterns();
for (final var methodPattern : methodPatterns) {
final var method = methodPattern.method();
final var pattern = methodPattern.pattern();
final var roles = methodPattern.roles();
if (!roles.isEmpty()) {
registry.requestMatchers(method, pattern).hasAnyAuthority(roles.toArray(String[]::new));
} else {
registry.requestMatchers(method, pattern).permitAll();
}
}
};
}
public static AuthenticationEntryPoint getAuthenticationEntryPoint() {
return (request, response, exception) -> {
logger.info("Authentication failed", exception);
response.setStatus(HttpStatus.UNAUTHORIZED.value());
};
}
public static Customizer<ExceptionHandlingConfigurer<HttpSecurity>> getExceptionHandler(
AuthenticationEntryPoint authenticationEntryPoint) {
return exceptions -> exceptions.authenticationEntryPoint(authenticationEntryPoint);
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http.cors(Customizer.withDefaults())
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(getSessionManagementConfigurerCustomizer())
.addFilterBefore(requestHeaderAuthenticationFilter(), HeaderWriterFilter.class)
.authorizeHttpRequests(getAuthorizeHttpRequestsCustomizer())
.exceptionHandling(getExceptionHandler(getAuthenticationEntryPoint()))
.build();
}
public Customizer<SessionManagementConfigurer<HttpSecurity>>
getSessionManagementConfigurerCustomizer() {
return session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Bean
public RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter() {
final var filter = new RequestHeaderAuthenticationFilter();
filter.setPrincipalRequestHeader("X-Auth-Key");
final var protectedRequestMatchers =
securityConfig.getMethodPatterns().stream()
.filter(isRolePopulated())
.map(
methodPattern ->
new AntPathRequestMatcher(methodPattern.pattern(), methodPattern.method()))
.toArray(AntPathRequestMatcher[]::new);
filter.setRequiresAuthenticationRequestMatcher(new OrRequestMatcher(protectedRequestMatchers));
filter.setAuthenticationManager(authenticationManager());
return filter;
}
@Bean
protected AuthenticationManager authenticationManager() {
return new ProviderManager(Collections.singletonList(requestHeaderAuthenticationProvider));
}
}