How to Secure Internal‑Only APIs with SpringBoot, Gateway, Redis, and AOP
This article explores three practical approaches—microservice isolation, Redis‑based whitelist, and gateway‑AOP header checks—to restrict certain APIs to internal network calls, and provides step‑by‑step SpringBoot code examples for implementing the preferred gateway‑AOP solution.
When developing business services, you may encounter APIs that should not be exposed externally and must be callable only between internal services.
Feasible Solutions
Three approaches are considered: isolating internal and external APIs with separate microservices, using Redis with a gateway to enforce a whitelist, and adding an AOP check in the business side after the gateway.
Solution 1 – Microservice Isolation
Place public and internal APIs in two different microservices; the internal‑only service aggregates all internal endpoints and is called only by other internal services. This adds a new service, increasing system complexity and latency.
Solution 2 – Gateway + Redis Whitelist
Maintain a whitelist of allowed interfaces in Redis. The gateway checks each incoming request against the list and forwards only those in the whitelist. This requires no changes to business code but incurs ongoing maintenance of the whitelist and adds a lookup overhead for every request.
Solution 3 – Gateway + AOP Header Check
The preferred method adds a custom header (e.g.,
from=public) at the gateway. Downstream services read the header; if it indicates a public request, the service rejects the call for methods annotated as internal‑only. This moves the access control to the business side, reduces gateway load, and improves response time, at the cost of a small amount of code intrusion.
Implementation Details (Solution 3)
1. Add a filter on the gateway to set the header:
<code>@Component
public class AuthFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(
exchange.mutate().request(
exchange.getRequest().mutate()
.header("id", "")
.header("from", "public")
.build())
.build());
}
@Override
public int getOrder() {
return 0;
}
}
</code>2. Define an annotation and an AOP aspect to enforce internal‑only access:
<code>@Aspect
@Component
@Slf4j
public class OnlyIntranetAccessAspect {
@Pointcut("@within(org.openmmlab.platform.common.annotation.OnlyIntranetAccess)")
public void onlyIntranetAccessOnClass() {}
@Pointcut("@annotation(org.openmmlab.platform.common.annotation.OnlyIntranetAccess)")
public void onlyIntranetAccessOnMethod() {}
@Before(value = "onlyIntranetAccessOnMethod() || onlyIntranetAccessOnClass()")
public void before() {
HttpServletRequest hsr = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String from = hsr.getHeader("from");
if (!StringUtils.isEmpty(from) && "public".equals(from)) {
log.error("This api is only allowed invoked by intranet source");
throw new MMException(ReturnEnum.C_NETWORK_INTERNET_ACCESS_NOT_ALLOWED_ERROR);
}
}
}
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OnlyIntranetAccess {}
</code>3. Annotate internal‑only endpoints:
<code>@GetMapping("/role/add")
@OnlyIntranetAccess
public String onlyIntranetAccess() {
return "该接口只允许内部服务调用";
}
</code>macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.