For two specific endpoints in my springboot application I need to use authentication with api key.
At the moment I am trying to implement this in this way according to https://jackynote.medium.com/implementing-multiple-authentication-methods-in-a-spring-boot-3-e57feac77ebd:
SpringSecurityConfig:
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableMethodSecurity
@SuppressWarnings("PMD")
public class SecurityConfiguration {
private final GlobalRestExceptionHandler globalRestExceptionHandler;
@Bean
@Order(1)
public SecurityFilterChain filterChainPrivate(HttpSecurity http) throws Exception {
http
.securityMatcher("/specificEndpointToAuthoriseWithApiKey")
.addFilterBefore(new InternalApiKeyAuthenticationFilter(), ChannelProcessingFilter.class);
http.cors(AbstractHttpConfigurer::disable);
http.csrf(AbstractHttpConfigurer::disable);
http.exceptionHandling(httpSecurityExceptionHandlingConfigurer -> {
httpSecurityExceptionHandlingConfigurer.accessDeniedHandler(getAccessDeniedHandler());
httpSecurityExceptionHandlingConfigurer.authenticationEntryPoint(getAuthenticationEntryPoint());
});
return http.build();
}
@Bean
@Order(2)
SecurityFilterChain filterChain(HttpSecurity http,
Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter) throws Exception {
http.authorizeHttpRequests(accessManagement -> accessManagement
.requestMatchers(
new AntPathRequestMatcher("/api-docs/**"),
.permitAll()
.anyRequest().authenticated()
);`
InternalApiAuthenticationFilter:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.net.HttpHeaders;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
public class InternalApiKeyAuthenticationFilter implements Filter {
private final String internalApiKey = "123456";
@Override
public void init(FilterConfig filterConfig) {}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
String apiKey = httpServletRequest.getHeader("x-api-key");
if (apiKey == null) {
unauthorized(httpServletResponse);
return;
}
if (!internalApiKey.equals(apiKey)) {
unauthorized(httpServletResponse);
return;
}
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
"apiKeyUser", null, Collections.singletonList(new SimpleGrantedAuthority("ebf-adapter")));
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
private void unauthorized(HttpServletResponse httpServletResponse) throws IOException {
httpServletResponse.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
httpServletResponse.setStatus(401);
Map<String, Object> response = Map.of("message", "SC_UNAUTHORIZED");
String responseBody = new ObjectMapper().writeValueAsString(response);
httpServletResponse.getWriter().write(responseBody);
}
}`
`
However the endpoint /specificEndpointToAuthoriseWithApiKey deliveres me the following error:
"An Authentication object was not found in the SecurityContext"
I am happy to hear, what am I missing here?