Preventing External API Exposure: 3 Microservice Patterns Compared
The article compares three approaches to restrict APIs to internal-only access in microservices: service isolation, gateway whitelist with Redis, and a gateway-plus-AOP solution that adds a header at the gateway and validates it via annotation-driven aspect, favoring the latter for performance and developer efficiency.
Problem: Internal-Only API Access in Microservices
During business development, certain APIs must not be exposed externally and should only be callable by internal services. The article presents three feasible solutions and analyzes their trade-offs.
Solution 1: Microservice Isolation for Internal and External Interfaces
Separate externally exposed interfaces and internally exposed interfaces into two distinct microservices. One service contains only public APIs; the other aggregates all internal-only APIs and forwards requests to downstream business services.
Drawbacks: This introduces an additional microservice for request forwarding, increasing system complexity, call latency, and long-term maintenance cost.
Solution 2: Gateway + Redis Whitelist
Maintain a whitelist of allowed interfaces in Redis. When an external request reaches the gateway, fetch the whitelist from Redis; allow requests on the whitelist, reject others.
Benefits: Zero intrusion into business code; only the whitelist needs maintenance.
Drawbacks: Whitelist maintenance is a continuous operational burden. In many organizations, developers cannot access Redis directly and must raise tickets, increasing development overhead. Every request incurs a whitelist lookup, adding latency. Since most external requests are legitimate, the cost-benefit ratio is low.
Solution 3: Gateway + AOP (Recommended)
Instead of checking an interface whitelist at the gateway, this approach checks the request source and pushes the validation down to the business side, eliminating gateway-side logic and improving response speed.
Key insight: External requests always pass through the gateway before being routed to business services, while internal service-to-service calls bypass the external gateway (using Kubernetes Service).
Implementation: At the gateway, add a custom header (e.g., from=public) to every incoming request. On the business side, an AOP aspect inspects the header; if present and equal to "public", the request is external and should be rejected for internal-only endpoints. If absent, the request is internal and allowed.
Advantages: Distributes access-control logic to each service, removing a central bottleneck. Developers declare internal-only endpoints directly via an annotation, improving readability and development efficiency. The annotation-based approach minimizes code invasiveness.
Gateway Filter: Adding the External Marker Header
@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;
}
}AOP Aspect and Annotation for Internal-Only Access
@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 {
}Usage: Annotate Internal-Only Endpoints
@GetMapping("/role/add")
@OnlyIntranetAccess
public String onlyIntranetAccess() {
return "该接口只允许内部服务调用";
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
