Add API key authentication

This commit is contained in:
litoral05
2026-05-07 13:30:15 +01:00
parent b2d4da82d4
commit 45e8658de7
3 changed files with 119 additions and 0 deletions
@@ -0,0 +1,68 @@
package com.litoralregas.vpnprovisioner.auth;
import com.litoralregas.vpnprovisioner.config.AppSecurityProperties;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.util.List;
public class ApiKeyAuthFilter extends OncePerRequestFilter {
private static final String API_KEY_HEADER = "X-API-Key";
private final AppSecurityProperties securityProperties;
public ApiKeyAuthFilter(AppSecurityProperties securityProperties) {
this.securityProperties = securityProperties;
}
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
return "/actuator/health".equals(request.getRequestURI());
}
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain
) throws ServletException, IOException {
String providedApiKey = request.getHeader(API_KEY_HEADER);
String expectedApiKey = securityProperties.getApiKey();
if (!StringUtils.hasText(expectedApiKey)) {
response.sendError(
HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"API key is not configured"
);
return;
}
if (!expectedApiKey.equals(providedApiKey)) {
response.sendError(
HttpServletResponse.SC_UNAUTHORIZED,
"Invalid API key"
);
return;
}
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
"api-key-client",
null,
List.of()
);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
}
@@ -0,0 +1,17 @@
package com.litoralregas.vpnprovisioner.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "app.security")
public class AppSecurityProperties {
private String apiKey;
public String getApiKey() {
return apiKey;
}
public void setApiKey(String apiKey) {
this.apiKey = apiKey;
}
}
@@ -0,0 +1,34 @@
package com.litoralregas.vpnprovisioner.config;
import com.litoralregas.vpnprovisioner.auth.ApiKeyAuthFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableConfigurationProperties(AppSecurityProperties.class)
public class SecurityConfig {
private final AppSecurityProperties securityProperties;
public SecurityConfig(AppSecurityProperties securityProperties) {
this.securityProperties = securityProperties;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
ApiKeyAuthFilter apiKeyAuthFilter = new ApiKeyAuthFilter(securityProperties);
return http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(apiKeyAuthFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
}