Why Do Microservices Still Need Their Own Authorization After Gateway Authentication?
Even though a gateway performs unified authentication and injects user information into request headers, microservices must still execute a second layer of authorization to verify data ownership, enforce business rules, and protect against internal threats, following a zero‑trust security model.
Gateway and Microservice Responsibility Split
The gateway performs authentication : it validates the token, extracts core user fields ( userId, tenantId, roles) and forwards them to downstream services via HTTP headers. Microservices perform authorization : they decide whether the authenticated user is allowed to act on a specific resource, e.g., checking order ownership or order status before modification. This follows the single‑responsibility principle for security.
Internal Network Is Not Implicitly Safe
Assuming the internal network is trustworthy leads to three common pitfalls:
Lateral movement : an attacker who compromises an external‑facing service can obtain a server inside the network and call other services directly, e.g., http://order-service/delete?id=xxx, bypassing the gateway.
SSRF attacks : a service with an SSRF flaw can be tricked into sending forged requests to internal services, which will execute them if no secondary verification is performed.
Insider or ops mistakes : developers or operators may bypass the gateway during debugging or misconfigure service URLs, leading to unauthorized data deletions.
Consequently, modern microservice security adopts a zero‑trust model: every request and every node must be re‑validated for identity and permissions.
Why the Gateway Should Not Perform Fine‑Grained Business Authorization
Embedding detailed business checks in the gateway creates tight coupling and performance issues:
The gateway would need to query the order database to verify ownership and status, turning a lightweight traffic router into a heavyweight monolith.
Such database calls consume the gateway’s Netty thread pool, dramatically reducing overall request throughput.
The gateway should remain a fast‑path forwarder with rate‑limiting and degradation capabilities, while microservices retain responsibility for business‑level checks.
Common Practice: Coarse Authentication at the Gateway, Fine Authorization Inside Services
The gateway validates the token, extracts essential fields, and injects them into a header (e.g., X-User-Context) as JSON:
{
"userId": "10001",
"userName": "小富",
"roles": ["ADMIN"]
}Microservices use an Interceptor or Filter to read this header, bind the context to MDC, and then perform authorization checks in controllers or service methods.
Example: Order Update with Ownership Check
@RestController
@RequestMapping("/orders")
public class OrderController {
@Autowired
private OrderService orderService;
// 1. Role check via Spring Security
@PutMapping("/{orderId}")
@PreAuthorize("hasAuthority('ORDER_WRITE')")
public ResponseEntity<Void> updateOrder(@PathVariable Long orderId, @RequestBody OrderDto orderDto) {
// 2. Get user ID from ThreadLocal set by the interceptor
Long currentUserId = UserContextHolder.getUserId();
// 3. Verify data ownership
Order order = orderService.getById(orderId);
if (order == null) {
return ResponseEntity.notFound().build();
}
if (!order.getUserId().equals(currentUserId)) {
// 4. Reject if the user does not own the order
throw new AccessDeniedException("You are not authorized to modify this order!");
}
orderService.update(orderId, orderDto);
return ResponseEntity.ok().build();
}
}This code separates identity parsing (handled by the interceptor) from role verification (Spring Security) and data‑ownership validation (business logic).
Propagating User Context Between Services
When Service A calls Service B via Feign, a RequestInterceptor can automatically attach the X-User-Context header:
@Component
public class FeignAuthInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
// Retrieve the user context from ThreadLocal
String userContextJson = UserContextHolder.getRawContext();
if (userContextJson != null) {
// Inject it into the outgoing Feign request
template.header("X-User-Context", userContextJson);
}
}
}Defending Against Header Tampering
To prevent malicious actors from forging the X-User-Context header, common defenses include:
Lightweight symmetric HMAC‑SHA256 signing : the gateway signs the context with a shared secret and a timestamp; downstream services verify the signature and expiration.
Private‑network whitelist : only allow calls from the gateway’s IP range or from internal Kubernetes pod CIDR.
Service‑mesh mutual TLS (e.g., Istio) : all inter‑service traffic is encrypted and authenticated by Envoy sidecars, eliminating the need for custom header checks.
Conclusion
The security boundary in a microservice system can be summed up as “gateway protects the outside, microservices protect the inside.” Implementing a second‑level authorization adds code, but it can be encapsulated in a shared interceptor or starter, keeping business development simple while ensuring a robust zero‑trust posture.
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.
Su San Talks Tech
Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.
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.
