Implementation to Secure Spring Boot API With API Key and Secret
We can develop the simple spring boot application that can demonstrates the securing spring boot API key and secret of the application.
Step 1: Create the Spring project.
Create a new Spring Boot project using Spring Initializr and add the required dependencies,
- Spring Web
- Spring Security
- Lombok
- Spring DevTools
After the creation of the project has done, the folder structure will be like below image.
Step 2: Configure the Application properties
Open application.properties file and add the configuration for the server port in the project.
spring.application.name=spring-boot-secure-API
server.port=8081
Step 3: Create the ApiKeyAuthenticationToken class
This class can handles the API Key and Secret authentication and this token will holds the API Key and secret and manage the authentication state.
Go to src > main > java > org.example.springbootsecureapi > config > ApiKeyAuthenticationToken and put the below code.
package org.example.springbootsecureapi.config;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
public class ApiKeyAuthenticationToken extends AbstractAuthenticationToken {
private final String apiKey;
private final String apiSecret;
public ApiKeyAuthenticationToken(String apiKey, String apiSecret) {
super(null);
this.apiKey = apiKey;
this.apiSecret = apiSecret;
setAuthenticated(false);
}
public ApiKeyAuthenticationToken(String apiKey, String apiSecret, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.apiKey = apiKey;
this.apiSecret = apiSecret;
super.setAuthenticated(true);
}
@Override
public Object getCredentials() {
return this.apiSecret;
}
@Override
public Object getPrincipal() {
return this.apiKey;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
if (isAuthenticated) {
throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
}
super.setAuthenticated(false);
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
}
}
Step 4: Create the ApiKeyAuthFilter class
This filter class will attempt to the authenticate the request by creating an instance of ApiKeyAuthenticationToken and passing it to authentication manager.
Go to src > main > java > org.example.springbootsecureapi > config > ApiKeyAuthFilter and put the below code.
package org.example.springbootsecureapi.config;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.RequestMatcher;
import java.io.IOException;
public class ApiKeyAuthFilter extends AbstractAuthenticationProcessingFilter {
private static final String API_KEY_HEADER = "API-Key";
private static final String API_SECRET_HEADER = "API-Secret";
public ApiKeyAuthFilter(RequestMatcher requiresAuth) {
super(requiresAuth);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException {
String apiKey = request.getHeader(API_KEY_HEADER);
String apiSecret = request.getHeader(API_SECRET_HEADER);
if (apiKey == null || apiSecret == null) {
throw new RuntimeException("Missing API Key or Secret");
}
Authentication auth = new ApiKeyAuthenticationToken(apiKey, apiSecret);
return getAuthenticationManager().authenticate(auth);
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
super.successfulAuthentication(request, response, chain, authResult);
chain.doFilter(request, response);
}
}
Step 5: Create the SecurityConfig class
This SecurityConfig class can sets up the custom authentication filter and defines the security rules for the API endpoints.
Go to src > main > java > org.example.springbootsecureapi > config > SecurityConfig and put the below code.
package org.example.springbootsecureapi.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import java.util.Collections;
import java.util.Collections;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
ApiKeyAuthFilter filter = new ApiKeyAuthFilter(new AntPathRequestMatcher("/api/**"));
filter.setAuthenticationManager(authentication -> {
String apiKey = (String) authentication.getPrincipal();
String apiSecret = (String) authentication.getCredentials();
if ("valid-api-key".equals(apiKey) && "valid-api-secret".equals(apiSecret)) {
return new ApiKeyAuthenticationToken(apiKey, apiSecret, Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")));
} else {
authentication.setAuthenticated(false);
}
return authentication;
});
http
.csrf().disable()
.authorizeRequests(authorizeRequests ->
authorizeRequests
.requestMatchers("/api/**").authenticated()
.anyRequest().permitAll()
)
.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
Step 6: Create the ApiController class
Go to src > main > java > org.example.springbootsecureapi > controller > ApiController and put the below code.
package org.example.springbootsecureapi.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class ApiController {
@GetMapping("/data")
public String getSecureData() {
return "This is Spring Boot API secured data!";
}
}
Step 7: Main Class
Main class will be as it is, we do not need to change anything.
package org.example.springbootsecureapi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootSecureApiApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootSecureApiApplication.class, args);
}
}
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.example</groupId>
<artifactId>spring-boot-secure-API</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-secure-API</name>
<description>spring-boot-secure-API</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Step 8: Run the application
Once, we run the application, it will start at port 8081.
Step 9: Endpoint Testing
1. Endpoint without the api key and secret
GET http://localhost:8081/api/data
Then show the error like below:
Missing API Key and secret
Output:
2. Endpoint Test with API key and secret
GET http://localhost:8081/api/data
Add the API key and secret in Header section.
API Key : valid-api-key
API Secret: valid-api-key
Output:
By the following these steps, we can secure the Spring Boot API using API keys and secrets. This method ensures that only the clients with valid credentials can access the API endpoints and thereby adding the extra layer of the security to the Spring Boot application.
Securing Spring Boot API With API Key and Secret
In Web applications, securing the APIs is critical. One of the common methods of securing the APIs is by using API keys and secrets. This ensures that only the authorized clients can access the API endpoints. This article can guide you through the process of securing the Spring Boot API using the API keys and secrets.
Securing the Spring Boot API with an API key and Secret can involve authenticating and authorizing the incoming requests to ensure only valid clients can access the API endpoints. This concept primarily revolves around intercepting HTTP requests and extracting the API key and secret, validating them. It can allow or deny access based on the validity of the application.
1. Authentication and Authorization
- Authentication is the process of verifying the identity of the client. In this case, it can involve checking the provided API key and secret.
- Authorization is the process of checking whether the authenticated client has permission to access the specific resource.
2.API Key and Secret
The API Key is a unique identifier used to the authenticate the client making a request. The API Secret is a private key can be used in conjunction with the API Key to the add the extra layer of the security. Both are typically provided in the request headers.
3.Custom Authentication token
To handle the API Key and secret authentication, We need a custom authentication token that can extends the Abstract Authentication Token. This token will hold the API Key and secret and manage the authentication state of the application.
4.Custom Authentication Filter
The Custom filter can be need to intercept the HTTP requests and extract the API key and secret from the header. This filter will attempt to the authenticate the request by the creating an instance of API Key Authentication Token and passing it to the authentication manager.
5.Security Configuration
The Spring Security configuration sets up the custom authentication filter and defines the security rules for the API endpoints. The filter can added to the security filter chain to the process requests to /api/** endpoints.
6.Endpoint Implementation
Finally, we can develop the API endpoint is defined in the controller that can demonstrates the secured endpoint.
Contact Us