Splitting a monolith into microservices multiplies your attack surface. What used to be one process behind one login is now a dozen services talking over the network, each an entry point an attacker could target. Security stops being a wall around the building and becomes a lock on every door inside it. The organising principle for that shift has a name: zero trust, meaning no request is trusted simply because it came from inside.
This tutorial walks through the practices that matter most, from authenticating at the edge to encrypting internal traffic. It assumes you know how services communicate in a microservices architecture; here we harden those communication paths.
Authenticate once at the edge, verify everywhere
The first decision is where login happens. Duplicating username-and-password logic in every service is a maintenance and security nightmare. Instead, authenticate the user once at the API gateway, then issue a signed token that downstream services validate.
The gateway becomes your single front door. It checks credentials, applies rate limiting, and rejects malformed requests before they ever reach an internal service. But — and this is the part beginners miss — downstream services must still validate the token themselves. Trusting the gateway blindly means one bypassed gateway compromises everything.
| Concern | Handle at gateway | Handle at each service |
|---|---|---|
| Login / credential check | Yes | No |
| Rate limiting, IP filtering | Yes | Rarely |
| Token (JWT) validation | Yes | Yes, again |
| Fine-grained authorisation (roles) | Sometimes | Yes |
| Input validation | Basic | Always |
Stateless auth with JWT
A JWT (JSON Web Token) is a signed token carrying the user's identity and claims such as roles. Because it is signed, any service can verify it with the signing key — no shared session database, no lookup, no coordination. That statelessness is exactly why JWTs fit independently scaled services so well.
Here is a Spring Security configuration that validates incoming JWTs on a resource service. Every request must present a valid token, and Spring rejects anything unsigned or expired automatically.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated())
.oauth2ResourceServer(oauth -> oauth.jwt(org.springframework.security.config
.Customizer.withDefaults()))
.csrf(csrf -> csrf.disable()); // stateless API, no browser session
return http.build();
}
}
With oauth2ResourceServer().jwt(), Spring validates the token's signature and expiry against a
configured public key or JWKS endpoint. The hasRole("ADMIN") line shows authorisation: even a
valid token cannot reach /admin/** unless it carries the ADMIN role. Authentication proves who
you are; authorisation decides what you may do.
Common mistake: Storing sensitive data or long-lived sessions inside the JWT payload. A JWT is signed, not encrypted — anyone can decode and read its contents. Never put passwords or secrets in claims, and keep expiry short (minutes, not days) with a refresh-token flow for renewal.
Encrypt internal traffic with mutual TLS
Perimeter security is not enough. If an attacker gets a foothold inside your network, unencrypted service-to-service calls let them eavesdrop and impersonate services. Mutual TLS (mTLS) fixes this: both the calling service and the receiving service present certificates and verify each other. Only services holding a trusted certificate can talk.
Plain TLS proves the server's identity to the client, the way your browser trusts a website. mTLS adds the reverse: the server also demands proof of the client's identity. In a zero-trust cluster, that means service A cannot call service B just by knowing its address — it must present a valid certificate B recognises.
In practice a service mesh like Istio or Linkerd handles mTLS transparently, so your application code stays clean while every hop between pods is encrypted and mutually authenticated. You should be able to explain the concept even before you operate a mesh.
Least privilege and secrets
Two habits prevent a large share of real breaches, and neither requires fancy tooling.
Least privilege means every service, database account and token gets the minimum access it needs and nothing more. The order service should have read-write access to the orders table and no access at all to the payments database. If that service is compromised, the blast radius is limited to what it could already touch.
Secrets management means passwords, API keys and certificates never live in source code or container images. Hardcode a database password and it ends up in Git history, in the built image, and probably in a log line somewhere — and rotating it means rebuilding and redeploying. Instead, pull secrets from a dedicated store at runtime.
# Kubernetes injects the secret as an environment variable at runtime;
# the value never appears in the image or the source repository.
env:
- name: SPRING_DATASOURCE_PASSWORD
valueFrom:
secretKeyRef:
name: orders-db-credentials
key: password
The application reads SPRING_DATASOURCE_PASSWORD like any environment variable, but the value is
supplied by Kubernetes Secrets (or Vault, or AWS Secrets Manager) at container start. Rotating the
password becomes a config change, not a code change.
Pro tip: Add secret scanning to your CI pipeline. Tools that reject a commit containing something that looks like an API key catch the most common mistake — a developer pasting a real credential into a config file "just to test" and forgetting to remove it. Prevention at commit time beats cleanup after a leak.
Validate every input and fail safely
Each service exposes an API, and every API is an attack surface. Validate all incoming data at the boundary: reject payloads that are too large, fields that are the wrong type, and strings that exceed sane lengths. This blunts injection attacks and malformed-request denial of service.
Failing safely matters too. When a downstream dependency misbehaves, your service should degrade gracefully rather than leak stack traces or hang. This is where security and resilience meet — a circuit breaker not only improves reliability but also stops an attacker from tying up all your threads by hammering a slow endpoint.
A layered checklist
Security in microservices is never a single control; it is layers that each assume the previous one might fail. Put together, a solid baseline looks like this:
- Authenticate users once at the gateway, and validate the JWT again in every service.
- Authorise by role or scope at the service that owns the data, not only at the edge.
- Encrypt internal traffic with mTLS so a network foothold does not become full access.
- Grant least privilege to every service account, database user and token.
- Keep secrets in a manager and inject them at runtime; never in code or images.
- Validate all input at each boundary and fail safely under load.
Adopt these as defaults and you have covered the failure modes that account for the majority of real-world incidents.
Why interviewers probe security
Most freshers can build a working API; far fewer can talk about protecting it. When an interviewer asks "how would you secure communication between two microservices?", they are checking whether you think past the happy path. A strong answer covers edge authentication, per-service JWT validation, and encrypting internal calls with mTLS — and mentions that internal traffic still needs protection because of zero trust.
Bring up least privilege and externalised secrets and you sound like someone who has read a real post-mortem. You do not need to have configured a service mesh; you need to reason about the layers clearly. Rehearse these points with the microservices interview questions guide, and build the muscle memory on real services in the Java Full Stack with AI program, where you secure a Spring Boot API end to end. Start from Spring Boot and Spring Security basics if authentication is still new, then come back and layer on the network-level controls covered here.
Frequently Asked Questions
Where should authentication happen in microservices?
What is a JWT and why is it common in microservices?
What is mutual TLS and why use it between services?
What does zero trust mean for microservices?
Why should secrets never be hardcoded in microservices?
Do freshers need to know microservices security for interviews?
Want to Build Your Career in Java Full Stack with AI?
Join CodeBegun and train with working industry engineers — Explore the Program

