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 by adding the necessary Maven dependency, creating a custom filter, extending AbstractAuthenticationToken, configuring Spring Security, implementing a controller, and testing the endpoint with and without the API key.
1. Overview
Security is crucial in REST API development because an insecure API can expose sensitive backend data, so organizations must pay attention to API security.
Spring Security offers various mechanisms to protect REST APIs, one of which is API keys—tokens supplied by clients when invoking an API.
This tutorial discusses how to implement API‑key‑based authentication in Spring Security.
2. REST API Security
Spring Security can protect REST APIs. Since REST APIs are stateless, sessions or cookies should not be used; instead, Basic authentication, API Keys, JWT, or OAuth2‑based tokens should be employed.
2.1. Basic Authentication
Basic authentication is a simple scheme where the client sends an Authorization header containing a Base64‑encoded username:password pair, and it is considered safe only over HTTPS or other secure transports.
2.2. OAuth2
OAuth2 is the industry‑standard open protocol for authentication and authorization, allowing resource owners to delegate access to clients via access tokens.
2.3. API Keys
Some REST APIs use API keys as a token that identifies the client without referencing an actual user; the key can be sent as a query parameter or in a request header.
3. Protecting REST API with API Keys
3.1 Add Maven Dependency
First declare the Spring Boot starter security dependency in pom.xml :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>3.2 Create Custom Filter
The idea is to obtain the API key from the request header and validate it using our configuration, which requires adding a custom filter to the Spring Security chain.
We start from GenericFilterBean , a simple Spring implementation of javax.servlet.Filter :
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);
}
}Only the doFilter() method needs to read the header, create an Authentication object, and place it into the current SecurityContext .
3.3 Extend AbstractAuthenticationToken
To turn the API key into an Authentication object, we extend AbstractAuthenticationToken :
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.4 Security Config
Register the custom filter before UsernamePasswordAuthenticationFilter in a SecurityFilterChain bean:
@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();
}
}The session policy is set to STATELESS because we are building a REST API.
3.5 ResourceController
Finally, create a simple controller exposing a GET endpoint /home :
@RestController
public class ResourceController {
@GetMapping("/home")
public String homeEndpoint() {
return "Baeldung !";
}
}3.6 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
Calling the endpoint without an API key returns a 401 Unauthorized error:
curl --location --request GET 'http://localhost:8080/home'Adding the header X-API-KEY: Baeldung and requesting again returns 200 OK:
curl --location --request GET 'http://localhost:8080/home' \
--header 'X-API-KEY: Baeldung'Source: baeldung.com/spring-boot-api-key-secret
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
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.