Open In App

Implementing Secure API Communication in Spring Boot

Last Updated : 21 Jun, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

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 Secret

The 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 Boot

Below are the implementation steps to Secure API Communication in Spring Boot.

Step 1: Create Spring Project

We 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:

Folder Structure


Step 2: Configure Application Properties

Open 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 class

Go 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 class

Go 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 Class

Go 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 Class

No 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 application

Once the project completed, run the application and it will start at port 8080.

Application Runs


Step 8: Testing the Endpoints

Secure endpoint:

POST http://localhost:8080/api/private/hello

Output:

1. Authentication Details:

Authentication Details


2. Headers API Keys:

Headers API Keys


Public Endpoint:

POST http://localhost:8080/api/public/hello

Output:

public Endpoint Testing

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.


Next Article

Similar Reads