Implementing API Key Authentication in Spring Security for REST APIs
This tutorial explains how to secure a Spring Boot REST API using API key authentication, covering the addition of Maven dependencies, creating a custom filter, extending authentication tokens, configuring the security filter chain, and testing the protected endpoint with curl.
1. Overview
Security is crucial for REST APIs because an insecure API can expose sensitive backend data. Spring Security offers mechanisms such as API keys to protect APIs. This tutorial demonstrates how to implement API‑key‑based authentication with Spring Security.
2. REST API Security
REST APIs are stateless, so session or cookie‑based authentication is unsuitable. Common approaches include Basic authentication, OAuth2, and API keys.
2.1 Basic Authentication
Clients send an Authorization header with a Base64‑encoded username:password . It is only safe over HTTPS.
2.2 OAuth2
OAuth2 is the industry standard for delegating access via tokens.
2.3 API Keys
Some APIs use a simple token passed in a query string or request header to identify the client without a user context.
3. Protecting a REST API with API Keys
3.1 Add Maven Dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>3.2 Create a Custom Filter
The filter extracts the API key from the request header and sets an Authentication object in the security context.
public class AuthenticationFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
try {
Authentication authentication = AuthenticationService.getAuthentication((HttpServletRequest) request);
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (Exception exp) {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
httpResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
PrintWriter writer = httpResponse.getWriter();
writer.print(exp.getMessage());
writer.flush();
writer.close();
}
filterChain.doFilter(request, response);
}
}3.3 Authentication Service
Validates the X-API-KEY header against a predefined secret.
public class AuthenticationService {
private static final String AUTH_TOKEN_HEADER_NAME = "X-API-KEY";
private static final String AUTH_TOKEN = "Baeldung";
public static Authentication getAuthentication(HttpServletRequest request) {
String apiKey = request.getHeader(AUTH_TOKEN_HEADER_NAME);
if (apiKey == null || !apiKey.equals(AUTH_TOKEN)) {
throw new BadCredentialsException("Invalid API Key");
}
return new ApiKeyAuthentication(apiKey, AuthorityUtils.NO_AUTHORITIES);
}
}3.4 Extend AbstractAuthenticationToken
Wraps the API key in a token compatible with Spring Security.
public class ApiKeyAuthentication extends AbstractAuthenticationToken {
private final String apiKey;
public ApiKeyAuthentication(String apiKey, Collection
authorities) {
super(authorities);
this.apiKey = apiKey;
setAuthenticated(true);
}
@Override
public Object getCredentials() { return null; }
@Override
public Object getPrincipal() { return apiKey; }
}3.5 Security Configuration
Registers the custom filter before UsernamePasswordAuthenticationFilter and enforces stateless session management.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().antMatchers("/**").authenticated()
.and().httpBasic()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().addFilterBefore(new AuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}3.6 Resource Controller
Provides a simple /home endpoint to test the security setup.
@RestController
public class ResourceController {
@GetMapping("/home")
public String homeEndpoint() {
return "Baeldung !";
}
}3.7 Disable Auto‑Configuration
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class, UserDetailsServiceAutoConfiguration.class})
public class ApiKeySecretAuthApplication {
public static void main(String[] args) {
SpringApplication.run(ApiKeySecretAuthApplication.class, args);
}
}4. Testing
Without an API key the request returns 401 Unauthorized :
curl --location --request GET 'http://localhost:8080/home'Adding the correct X-API-KEY header returns 200 OK :
curl --location --request GET 'http://localhost:8080/home' \
--header 'X-API-KEY: Baeldung'Source code can be found at the Baeldung tutorial repository.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.