In modern web applications, securing API communication is important to protect sensitive data and ensuring the integrity of interactions between clients and servers. In this article, we will learn how to implement secure API communication in a Spring Boot application.
Securing API communication is crucial for protecting sensitive data and ensuring that only authorized clients can access specific endpoints. One common method to achieve this is by using API keys and secrets. This method can be simpler than other authentication mechanisms like OAuth2 or JWT but still provides a robust way to secure APIs.
API Key and SecretThe API key is a unique identifier passed with each request to authenticate the client. An API secret provides additional security measures to further validate the request. Together, they form a pair that the server checks to allow or deny access to the API endpoints of the application.
Working of API Key and Secret- Client Request: When the client makes a request to the API, it includes the API key and secret in the request headers.
- Server Validation: The server intercepts the request, extracts the API key and secret, and validates them against the known valid values.
- Access Control: If the API key and secret are valid, the server processes the request and returns the response. If they are invalid, the server denies access and returns an unauthorized error.
This method provides a lightweight and effective way to secure APIs without the complexity of token-based authentication mechanisms.
Implementation of the Secure API Communication in Spring BootBelow are the implementation steps to Secure API Communication in Spring Boot.
Step 1: Create Spring ProjectWe will now create the spring project using spring Initializr. When creating the project, add the following dependencies to the project.
Dependencies:
- Spring Web
- Spring Security
- Lombok
- Spring DevTools
After the project creation done, the folder structure will be like below image:

Step 2: Configure Application PropertiesOpen the application.properties file and add the configuration and security credentials of the application.
spring.application.name=secure-API-Demo
spring.security.user.name=user
spring.security.user.password=password
Step 3: Create the ApiKeyFilter classGo to src > main > java > org.example.secureapidemo > config > ApiKeyFilter and put the below code.
Java
package org.example.secureapidemo.config;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ApiKeyFilter extends HttpFilter {
private static final String API_KEY_HEADER = "x-api-key";
private static final String API_SECRET_HEADER = "x-api-secret";
private static final String VALID_API_KEY = "apikey123";
private static final String VALID_API_SECRET = "secret123";
@Override
protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
String apiKey = request.getHeader(API_KEY_HEADER);
String apiSecret = request.getHeader(API_SECRET_HEADER);
if (VALID_API_KEY.equals(apiKey) && VALID_API_SECRET.equals(apiSecret)) {
chain.doFilter(request, response);
} else {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid API Key or Secret");
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// Initialization code, if needed
}
@Override
public void destroy() {
// Cleanup code, if needed
}
}
In this filter:
- API_KEY_HEADER and API_SECRET_HEADER are the names of the headers expected in the request.
- VALID_API_KEY and VALID_API_SECRET are valid for the API key and secret of the application.
The doFilter method checks if the incoming request contains the valid API key and secret. If valid, it allows the request to proceed. if not, it sends the unauthorized error response.
Step 4: Create the SecurityConfig classGo to src > main > java > org.example.secureapidemo > config > SecurityConfig and put the below code.
Java
package org.example.secureapidemo.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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests(authorizeRequests ->
authorizeRequests
.requestMatchers("/auth/**").permitAll()
.anyRequest().authenticated()
)
.httpBasic();
http.addFilterBefore(apiKeyFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public ApiKeyFilter apiKeyFilter() {
return new ApiKeyFilter();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
In this security configuration:
- We disable the CSRF protection for the simplicity of the application.
- We permit all the requests to “/api/public/**” but require the authentication for all the other requests.
- We have added the custom ApiKeyFilter before the UsernamePasswordAuthenticationFilter, in the filter chain of the application.
Step 5: Create the SecureController ClassGo to src > main > java > org.example.secureapidemo > controller > SecureController and put the below code.
Java
package org.example.secureapidemo.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 SecureController {
@GetMapping("/public/hello")
public String publicHello() {
return "Hello, Public!";
}
@GetMapping("/private/hello")
public String privateHello() {
return "Hello, Private!";
}
}
In this controller:
- “/api/public/hello” can be accessible without any authentication.
- “/api/private/hello” requires the valid API key and secret to access the endpoint.
Step 6: Main ClassNo changes are required in the main class.
Java
package org.example.secureapidemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SecureApiDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SecureApiDemoApplication.class, args);
}
}
pom.xml:
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.3.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.example</groupId>
<artifactId>secure-API-Demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>secure-API-Demo</name>
<description>secure-API-Demo</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 7: Run the applicationOnce the project completed, run the application and it will start at port 8080.

Step 8: Testing the EndpointsSecure endpoint:
POST http://localhost:8080/api/private/hello Output:1. Authentication Details:

2. Headers API Keys:

Public Endpoint:
POST http://localhost:8080/api/public/hello Output: This example demonstrates how to secure the API communication in the Spring Boot application using API keys and secrets. This method provides the simple yet effective way to the protect your endpoints that only authorized the clients can access them.
|