In this post, I will explore how to implement API security with an API key using Spring Cloud Gateway.
Overview
In microservices architectures, the security of data passing between the different API services becomes decisive. it is therefore important to setup several levels in order to improve the security between the different exchanges between these services.
That’s why the API Security Model, based on Richardson Maturity Model, describes API security at increasing levels of security.

In this model, security and trust are increasingly improved at each level. It has four levels:
- Level 0: API Keys and Basic Authentication
- Level 1: Token-Based Authentication
- Level 2: Token-Based Authorization
- Level 3: Centralized Trust Using Claims
In this story, we will focus on level 0 (API Keys) with implementation through the Spring Cloud Gateway.
Spring Cloud Gateway provides a library for building API gateways on top of Spring and Java. It provides a flexible way of routing requests based on a number of criteria, as well as focuses on cross-cutting concerns such as security, resiliency, and monitoring.

According to the architecture above, access to services (A or B) will be conditioned by the validation of the key which will be sent in the header of the request.
Project Setup
Creating a simple Spring Boot project from start.spring.io with the following dependencies: Lombok, spring cloud gateway, and spring data redis.
Project Structure

Our architecture contains three main packages: configuration, filter, domain
Global filters are executed for every route defined in the API Gateway. In our case, we are going to create a custom pre-filter. This filter code is executed before Spring Cloud Gateway routes the request to a destination web service endpoint.
@Slf4j
@Component
public class AuthFilter implements GlobalFilter, Ordered {
@Autowired
RedisHashComponent redisHashComponent;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
List<String> apiKeyHeader = exchange.getRequest().getHeaders().get("gatewayKey");
log.info("Api key: {}", apiKeyHeader);
Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
String routeId = route != null ? route.getId() : null;
if (routeId == null || CollectionUtils.isEmpty(apiKeyHeader) || !isAuthorized(routeId, apiKeyHeader.get(0))) {
log.error("Api Key not valid");
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "You cannot consume this service. Please check your api key.");
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
/**
* @param routeId id of route
* @param apikey client header api key
* @return true if is authorized, false otherwise
*/
private boolean isAuthorized(String routeId, String apikey) {
Object apiKeyObject = redisHashComponent.hGet(AppConstant.RECORD_KEY, apikey);
if(apiKeyObject != null){
ApiKey key = ObjectMapperUtils.objectMapper(apiKeyObject, ApiKey.class);
return key.getServices().contains(routeId);
}else {
return false;
}
}
}
For each call made by the client, a check is made in redis to confirm that the client has the right to consume the requested service.
Now , We have a custom routing configuration.
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route(AppConstant.SERVICE_A_KEY,
r -> r.path("/api/service-a/**")
.filters(f -> f.stripPrefix(2)).uri("http://localhost:8081"))
.route(AppConstant.SERVICE_B_KEY,
r -> r.path("/api/service-b/**")
.filters(f -> f.stripPrefix(2)).uri("http://localhost:8082"))
.build();
}
Testing
We can run our gateway and our services.
- When the client is not authorized to consume the service with its api key

When the client is authorized to consume the service

Summary
When used in large solutions, it is necessary to use existing API Gateway solutions that offer already implemented security services like Kong API Gateway, Tyk, etc.
All source code is available on GitHub.
References
- https://curity.io/resources/learn/the-api-security-maturity-model/
- https://nordicapis.com/introducing-the-api-security-maturity-model/
** Cover image by Nicolas Picard on Unsplash