Spring Boot API Key Authentication
1. Overview
In this tutorial, we will look at how to use an API key to secure a Spring Boot application endpoint. It is not uncommon for publicly available APIs to require that a key is passed via the header before making a request.
This serves as a means of protecting the API from unwanted access and also help identify the current user calling the API.
For this article, we will build a simple Spring Boot application that exposes an API for getting Chuck Norris facts and secure it with an API key.
2. Application Setup
We will use Spring Initializer to generate a Spring Boot application. The application will use Spring Boot version 3.2.2 and JDK 21.
You can download the application via this link. The complete source code will be made available at the of the article.
3. Creating the Quote Service
Let’s begin by creating a QuoteService
in the services
package. It will have only one method called
chuckNorrisFacts()
. This function will use the Faker
library to generate a random Chuck Norris fact
and return it.
Listing 3.1 QuoteService.java
|
|
We will create a corresponding QuoteController
that will expose the quotes
endpoint and delegate to the QuoteService
we created earlier.
Listing 3.2 QuoteController.java
|
|
At this point, we can start the application and curl the http://localhost:8080/quotes
endpoints, without any API key.
The Spring Boot application will not require any form of authentication yet because we have yet to add the
spring-boot-starter-security
dependency.
Listing 3.3 Curl Request
|
|
4. Spring Security Configuration
Spring Security is the library that contains the security-related features in the Spring ecosystem. Let’s add its dependency:
Listing 4.1 pom.xml
|
|
The version is controlled by the parent Spring Boot BOM. So we need not explicitly add a version
tag.
Spring security works by a series of filters that check every request and based on the attributes present in the request it determines what type of authentication mechanism to use.
At the end of all the filters, Spring will check the Authentication
object in the SecurityContextHolder
.
If the isAuthenticated()
method of the Authentication
returns true, then it will treat the request as
duly authenticated. Otherwise, it’ll return an unauthorized error.
SecurityFilterChain
is the name of the class that coordinates the flow of a request through all the Filters.
Therefore, we will need to create our own Filter
and add it to the SecurityFilterChain
.
This way, for every new request, our Filter
will be called, and we can check the incoming API key in the
request header.
If it’s valid, we will signal to Spring Security that this request is authenticated. If it’s not valid, we will return a 401 unauthorized error which will prevent the request from reaching the controller.
Let’s start by creating a helper class that will fetch the ClientCredential
from the database
using the request’s API key. If found, we will consider the apiKey valid:
Listing 4.2 ClientAuthenticationHelper.java
|
|
The logic in the helper class can be as complex as necessary and will vary based on the business requirement.
Next, we will create a custom Filter
class:
Listing 4.3 ApiKeyFilter.java
|
|
We read the Api-Key associated with the request and validate it with the help of the ClientAuthenticationHelper
.
If the apiKey is invalid, we return a 401 error. Otherwise, we simply signal to Spring Security that this
request is authenticated.
The signalling involves creating a new instance of the UsernamePasswordAuthenticationToken
.
We then update the SecurityContextHolder
to use the newly created authentication token via
SecurityContextHolder.getContext().setAuthentication();
.
This act of creating an authentication token and attaching it to the security context holder is what it means to log-in, in a Spring Boot application.
It’s very important to use the constructor with three args, as it’s the only one that sets the
authenticated
field as true
.
Listing 4.4 UsernamePasswordAuthenticationToken.java
|
|
Note that the UsernamePasswordAuthenticationToken
is part of the Spring security library. The fully qualified class name
is org.springframework.security.authentication.UsernamePasswordAuthenticationToken#UsernamePasswordAuthenticationToken
.
The last part is to plug our classes into the Spring Security chain. Since this is SpringBoot v3+,
we will do so by creating a SecurityFilterChain
bean that will host our custom configuration.
Listing 4.5 WebSecurityConfig.java
|
|
We configured the SecurityFilterChain
to place our ApiKeyFilter
before the AnonymousAuthenticationFilter
class.
This is so that our request will not be marked anonymous, which will trigger a forbidden error.
Now, if we try to invoke the /quotes
endpoint with no Api-Key or an invalid one, we will get an exception.
Listing 4.6 Invalid Curl Request
|
|
However, calling the endpoint with the right API key will return a quote:
Listing 4.7 Valid Curl Request
|
|
5. Multiple Authentication Mechanism
The beauty of Spring Security is that we can have more than one type of authentication working consecutively in the same application.
Let’s imagine that our application is a monolith and some other endpoints that do not require an API key. Some require the traditional username and password or even a JWT token.
We can configure the ApiKeyFilter
to ignore those requests. Thus, allowing different authentication mechanism
to work in the same application.
We simply need to add a common prefix to all the endpoints that require an API key.
Then we will override the shouldNotFilter()
method in ApiFilter
to return true
for every endpoint
that does not begin with the designated prefix.
Listing 5.1 ApiFilter.java
|
|
In Listing 5.1 above, we fetch the path prefix from a configuration which will allow us to
change it with little to no code changes. The prefix is set in the application.properties
file to be /api
.
So, we need to update the quotes
endpoint to /api/quotes
.
To buttress our points further, we will create another endpoint web/home
and configure Spring Security to
not require any form of authentication for that.
Listing 5.2 IndexController.java
|
|
Listing 5.3 WebSecurityConfig.java
|
|
On line 24 is where we configured the WEB_INDEX
endpoint to not require any form of authentication. This is
why when we make an HTTP request to /web/home
without an API key, we still get the right response.
But if we invoke api/quotes
without a valid key, we will get the usual 401 exception.
6. Conclusion
Spring Security is a versatile library that can cater for myriads of use cases and security requirements.
In this article, we’ve seen how to use a combination of custom Filter and SecurityFilterChain
to achieve securing
our API with an API Key.
The approach we considered here is the simple version. There’s another technique that’s more comprehensive than this. We will consider that in another article as a follow-up to this one.
The complete source code is available on GitHub.
Happy Coding!