How to Restrict Public APIs to Intranet Calls with Gateway, Redis Whitelist, and AOP
This article examines three practical approaches—microservice isolation, Redis‑based whitelist, and gateway‑plus‑AOP—to prevent external exposure of internal APIs, compares their trade‑offs, and provides a complete SpringBoot implementation of the gateway‑AOP solution with code samples.
Background
In many enterprise systems certain APIs must be callable only from internal services and must not be exposed to the public internet. The article evaluates three architectural approaches to enforce this restriction while keeping the rest of the system functional.
Three Feasible Solutions
Solution 1 – Microservice Isolation
Separate public‑facing and internal‑only APIs into two distinct microservices. The public service exposes all outward endpoints, while the internal service aggregates only the APIs that should be reachable within the intranet.
Advantages : clear separation of concerns; no runtime checks required.
Disadvantages : introduces an additional microservice, adds network latency, and increases operational complexity.
Solution 2 – Gateway + Redis Whitelist
Maintain a whitelist of allowed API paths in Redis. The API gateway queries Redis for each incoming request; if the request path is present, it is forwarded, otherwise it is rejected.
Advantages : zero intrusion into business code; only the whitelist needs to be managed.
Disadvantages : whitelist maintenance is a continuous effort, may require ticketing to modify, and every request incurs a Redis lookup which can affect performance.
Solution 3 – Gateway + AOP (Annotation‑Based Access Control)
External requests always pass through the gateway, while intra‑cluster calls use Kubernetes services directly. The gateway adds a custom header (e.g., from=public) to external requests. An AOP aspect on the service side reads this header; if the header indicates a public request and the target API is marked as internal, the call is blocked. Developers declare internal APIs with a custom annotation, keeping the check declarative.
Advantages : no gateway‑side bottleneck, fast response, centralized control, and minimal code intrusion when using an annotation.
Disadvantages : introduces a small amount of code intrusion, mitigated by the annotation approach.
Detailed Implementation of Solution 3
Step 1 – Add Header in the Gateway
@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; }
}This filter runs for every request that reaches the Spring Cloud Gateway. It injects the header from=public (and an empty id placeholder) into the outgoing request.
Step 2 – Define the AOP Aspect and Annotation
@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("onlyIntranetAccessOnMethod() || onlyIntranetAccessOnClass()")
public void before() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String from = request.getHeader("from");
if (!StringUtils.isEmpty(from) && "public".equals(from)) {
log.error("This API is only allowed to be invoked by intranet source");
throw new MMException(ReturnEnum.C_NETWORK_INTERNET_ACCESS_NOT_ALLOWED_ERROR);
}
}
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OnlyIntranetAccess {}The aspect intercepts any method or class annotated with @OnlyIntranetAccess. It extracts the from header; if the value is public, the request is rejected with a custom exception.
Step 3 – Apply the Annotation to Internal APIs
@GetMapping("/role/add")
@OnlyIntranetAccess
public String onlyIntranetAccess() {
return "该接口只允许内部服务调用";
}Any endpoint annotated with @OnlyIntranetAccess will be accessible only to calls that originate inside the cluster (i.e., without the from=public header). External calls are automatically blocked by the aspect.
Conclusion
Microservice isolation provides a clean architectural split but adds deployment and latency overhead. The Redis whitelist is easy to insert but requires continuous maintenance and adds per‑request latency. The gateway‑AOP approach offers a balanced solution: it centralizes the access decision in the gateway, uses a lightweight header, and enforces the rule declaratively via an annotation, resulting in minimal performance impact and manageable code changes.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
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.
