Information Security 9 min read

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.

Architect
Architect
Architect
Implementing API Key Authentication in Spring Security for REST APIs

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.

JavaauthenticationREST APISpring SecurityAPI key
Architect
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.