Skip to content
Back EndJavaWeb Security

Zero-Trust Architecture in Spring Boot 4 with Spring Security 7

15 min readsalahtobok
Zero-Trust Architecture in Spring Boot 4 Hero Image

For decades, enterprise security relied on the traditional “Castle and Moat” perimeter strategy. Network administrators installed powerful firewalls around data centers. Everything outside the firewall was treated as untrusted; everything inside the private subnet was granted complete trust. In modern cloud-native architectures, this perimeter model is dead. Modern workloads run across Kubernetes pods, serverless functions, multi-tenant public clouds, and remote branch offices. Once an attacker breaches a single perimeter edge, internal lateral movement is unrestricted.

To secure distributed microservices, engineering teams must adopt a Zero-Trust Architecture. Zero-Trust is governed by one non-negotiable rule: never trust, always verify. In a Zero-Trust network, private IP addresses grant zero privileges. Every transaction must carry cryptographic proof of identity, delegated authorization scopes, and transport encryption. This architectural guide explains how to construct an enterprise-grade defense-in-depth Zero-Trust backend using Java 25, Spring Boot 4, Spring Security 7, and eBPF (Extended Berkeley Packet Filter) service meshes.

1. Why the Traditional Castle-and-Moat Model Fails

In the traditional castle-and-moat model, the perimeter firewall acts as the castle wall. Once traffic passes through the firewall, backend servers communicate over cleartext HTTP. Microservices assume that any connection originating from an internal IP address (such as 10.0.x.x or 192.168.x.x) is safe and trusted.

This design creates catastrophic vulnerabilities in modern systems:

  • Lateral Movement: If an attacker exploits a single vulnerability (such as a remote code execution bug in a frontend gateway), they gain unfettered access to internal databases, caches, and message brokers.
  • Perimeter Dissolution: Modern systems run workloads across multi-region cloud providers and hybrid Kubernetes clusters. There is no longer a single perimeter to defend.
  • Insider Threats: Disgruntled employees, compromised service accounts, and supply-chain dependencies bypass network-level perimeter firewalls completely.
Traditional Castle and Moat vs Zero-Trust Security Architecture
Figure 1: Traditional perimeter security trusts internal network traffic, whereas Zero-Trust treats all network boundaries as hostile.

Figure 1 demonstrates the fundamental paradigm shift. In a Zero-Trust architecture, private networks do not provide automatic safety. Every single microservice behaves as though it is directly connected to the public internet.

2. The Three Core Tenets of Zero-Trust Security

The National Institute of Standards and Technology (NIST SP 800-207) formalizes Zero-Trust into three foundational requirements:

  1. Verify Explicitly: Always authenticate and authorize based on all available data points. These data points include user identity, client application scopes, device posture, and cryptographic token signatures. Never rely on network topology or source IP addresses.
  2. Enforce Least Privilege: Limit access using Just-In-Time (JIT) and Just-Enough-Access (JEA) controls. Services must only receive the exact delegated scopes required to execute specific business actions.
  3. Assume Breach: Operate under the assumption that an adversary already has active presence inside your private container network. Minimize the blast radius by encrypting all traffic in transit, verifying identity on every network hop, and maintaining immutable audit logs.

To inspect identity governance at the user and realm layer, read our companion guide on Mastering Zero-Trust Architecture: Implementing OAuth2 and Keycloak in Java 25.

3. Dual-Layer Defense: Layer 4 Network vs. Layer 7 Application

True Zero-Trust cannot be achieved at the application layer alone or at the network layer alone. Building a resilient architecture requires a dual-layer defense-in-depth model:

  • Layer 4 / Network Layer (Transport Security): Manages Mutual TLS (mTLS) encryption, packet filtering, network identity attestation (such as SPIFFE/SPIRE), and connection-level rate limiting. In modern architectures, this layer is managed transparently by the Linux kernel using eBPF.
  • Layer 7 / Application Layer (Business Authorization): Validates user identities, decodes JSON Web Tokens (JWT), checks cryptographic signatures against an Identity Provider (IdP) like Keycloak, and enforces granular method-level business rules via Spring Security 7.
Spring Boot REST API OIDC Integration with Keycloak
Figure 2: The end-to-end token flow between Keycloak (OIDC), the client application, and the Spring Boot Resource Server.

Figure 2 illustrates the application-layer lifecycle. Clients authenticate against Keycloak, obtain an asymmetric RS256 JSON Web Token, and pass it in the HTTP Authorization: Bearer <token> header. Meanwhile, the underlying network mesh transparently encrypts the socket payload without modifying a single line of Java application code.

4. Project Setup: Java 25 and Spring Boot 4

Let us implement our Zero-Trust financial service. We build this service with Java 25 and Spring Boot 4.0.0. Spring Security 7 provides full native support for OAuth2 Resource Servers and OpenID Connect token validation.

Configure your pom.xml with the required starters:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>4.0.0</version>
        <relativePath/>
    </parent>
    
    <groupId>com.webcodein.security</groupId>
    <artifactId>zero-trust-spring-security-7-ebpf-mesh</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>zero-trust-spring-security-7-ebpf-mesh</name>
    <description>Zero-Trust Microservices with Spring Security 7 and eBPF</description>

    <properties>
        <java.version>25</java.version>
    </properties>

    <dependencies>
        <!-- Spring Boot Web MVC -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring Security 7 OAuth2 Resource Server -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
        </dependency>

        <!-- Actuator for Health & Kubernetes Probes -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!-- Testing Dependencies -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webmvc-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Next, configure your application.yml file. We set the service name, HTTP port, and point Spring Security to our Keycloak realm issuer:

spring:
  application:
    name: finance-service
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: http://localhost:8080/realms/webcodein-realm

server:
  port: 8080

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics

5. Configuring the Spring Security 7 Filter Chain

In Spring Security 7, the SecurityFilterChain bean defines which routes are publicly accessible and which require cryptographic authentication. In a strict Zero-Trust model, we apply the default-deny rule: only health probes are permitted; every other route requires an authenticated JWT.

Spring Security 7 JWT Validation Flow
Figure 3: Step-by-step JWT signature verification, claims validation, and security context initialization.

Figure 3 shows how Spring Security processes an incoming request. The BearerTokenAuthenticationFilter extracts the token. The NimbusJwtDecoder validates the digital signature against Keycloak’s cached JSON Web Key Set (JWKS), verifies timestamp validity (nbf and exp), and checks the iss (issuer) claim. If validation fails, Spring Security rejects the request immediately with HTTP 401 Unauthorized.

Here is our complete SecurityConfig.java configuration:

package com.webcodein.security.zerotrust.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authz -> authz
                // 1. Open endpoints for Kubernetes health probes (eBPF mesh validates node L4 access)
                .requestMatchers("/actuator/health/**").permitAll()
                // 2. Default Deny: all other endpoints require valid authentication
                .anyRequest().authenticated()
            )
            // 3. Configure as OAuth2 Resource Server with JWT validation
            .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> {}));
            
        return http.build();
    }
}

Notice the @EnableMethodSecurity annotation on line 12. This enables Spring’s method-level security expressions (such as @PreAuthorize), allowing us to enforce granular data ownership directly on business methods.

For fine-grained domain object access control, examine our detailed tutorials on Spring Security ACL in Spring Boot 4 and PostgreSQL 16 Row-Level Security.

6. Fine-Grained Method Security and Ownership Verification

Authenticating a request is only step one of Zero-Trust. An authenticated user must not be allowed to inspect other users’ data. For instance, user Alice should never be allowed to view Bob’s account balance, even if Alice presents a valid JWT.

We enforce this using a dedicated SecurityService combined with SpEL (Spring Expression Language) expressions in our REST controller:

package com.webcodein.security.zerotrust.service;

import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.stereotype.Service;

@Service("securityService")
public class SecurityService {

    public boolean isOwner(Authentication authentication, String accountId) {
        if (authentication == null || !(authentication.getPrincipal() instanceof Jwt jwt)) {
            return false;
        }
        
        // In a production application, map the JWT subject to account owners in the database.
        // For this demonstration, we verify that the JWT subject matches the requested account ID.
        String subject = jwt.getSubject();
        return accountId != null && accountId.equals(subject);
    }
}

Now, we create our protected REST controller FinancialDataController.java:

package com.webcodein.security.zerotrust.controller;

import com.webcodein.security.zerotrust.model.BalanceResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class FinancialDataController {

    @GetMapping("/api/v1/accounts/{accountId}/balance")
    @PreAuthorize("hasAuthority('SCOPE_finance:read') and @securityService.isOwner(authentication, #accountId)")
    public BalanceResponse getBalance(@PathVariable String accountId) {
        // Business logic retrieves balance securely
        return new BalanceResponse(accountId, 15000.00, "USD");
    }
}

Look closely at the @PreAuthorize expression on line 13:

  1. hasAuthority('SCOPE_finance:read') verifies that the client application was granted delegated read access to financial records.
  2. @securityService.isOwner(authentication, #accountId) invokes our bean to confirm that the token subject matches the URL parameter {accountId}.

If either condition fails, Spring Security stops execution before the method runs and returns HTTP 403 Forbidden.

7. How eBPF Meshes Accelerate Service-to-Service Security

While Spring Security 7 provides Layer 7 application governance, microservices still need transport encryption. Traditional service meshes (like early Istio or Linkerd) injected an Envoy sidecar proxy into every Kubernetes pod. Sidecar proxies consume high CPU and memory, add 2 to 6 milliseconds of latency, and require maintaining complex TLS certificates across containers.

eBPF-Based Service Mesh Securing Spring Boot
Figure 4: Sidecarless eBPF architecture intercepting and encrypting network sockets directly inside the Linux kernel.

eBPF (Extended Berkeley Packet Filter) revolutionizes microservice security. Instead of running proxy containers in user space, eBPF runs sandboxed bytecode programs directly inside the Linux kernel:

  • Socket-Level Redirection (sockops): When container A sends packets to container B on the same node, eBPF detects the socket connection and copies data directly between socket buffers. Packets completely bypass the host TCP/IP networking stack, reducing latency by up to 80%.
  • Kernel-Level mTLS: eBPF meshes (such as Cilium or Istio Ambient Mesh with ztunnel) handle Mutual TLS transparently. The Spring Boot application sends standard HTTP requests over its loopback interface. The eBPF kernel program intercepts the socket, wraps packets in an encrypted mTLS tunnel using WireGuard or IPsec, and transmits them across nodes.
  • Zero Java Keystore Maintenance: Java developers never need to manage local keystores, PKCS12 files, or certificate renewal scripts. The infrastructure team manages workload cryptographic identities (via SPIRE/SPIFFE) at the node level.

8. Comparative Matrix: eBPF Mesh vs. Sidecars vs. App mTLS

Security architects must choose the right transport security model for their infrastructure. Table 1 compares modern network security strategies:

Architecture Dimension eBPF Ambient Mesh (Cilium / Ambient) Envoy Sidecar Mesh (Traditional Istio) Application-Level mTLS (Java Keystores) Node-to-Node IPsec / WireGuard
Operating Layer Linux Kernel (Layer 4 socket & tc hooks) User-space sidecar container (Layer 4/7) JVM Application space (Java SSLContext) Operating system network interface
Memory Overhead Ultra-low (single agent per Kubernetes node) High (50MB–150MB overhead per pod) Zero additional containers; JVM heap usage Extremely low (kernel driver)
Latency Impact Sub-millisecond (socket-to-socket bypass) 2–6 ms added latency per network hop 1–2 ms (Java TLS cryptographic overhead) Sub-millisecond (kernel packet encryption)
Application Changes None (plain HTTP in container) None (sidecar intercepts pod traffic) High (keystores, truststores, renewal code) None (transparent network tunnels)
L7 Policy Enforcement Delegated to Spring Security 7 or waypoint proxy Supported inside Envoy proxy Supported inside Spring Security None (Layer 3/4 encryption only)
Certificate Rotation Automated via node agent (SPIFFE/SPIRE) Automated via sidecar pilot control plane Manual or requires custom Spring reloaders Automated via VPN/host key rotation

Combining eBPF Ambient Mesh at Layer 4 with Spring Security 7 at Layer 7 creates the optimal enterprise design. eBPF delivers fast, zero-overhead transport encryption, while Spring Boot enforces domain ownership and delegated OAuth2 scopes.

9. Real-World Enterprise Implementation Scenarios

The combination of Spring Boot 4 and eBPF Zero-Trust architecture addresses complex challenges across regulated industries:

Use Case 1: Core Banking & Instant Payment Processing

In high-throughput financial clearing systems, payment transactions must meet sub-10ms service-level agreements (SLAs). Traditional sidecar proxies introduce too much latency for high-frequency ledger updates. With an eBPF mesh and Spring Boot 4:

  • The eBPF kernel layer encrypts peer-to-peer traffic using WireGuard, maintaining line-rate network throughput.
  • Spring Security validates banking JWT scopes (e.g., SCOPE_payments:write) and enforces transaction limit checks in memory.
  • If an attacker compromises an internal microservice, eBPF network security policies prevent the compromised pod from connecting to the core ledger database port.

Use Case 2: Multi-Cloud Healthcare Electronic Health Records

Healthcare providers operate patient telemetry and appointment portals across AWS, Azure, and private hospital servers under strict HIPAA compliance rules:

  • eBPF transparently establishes cross-cloud encrypted tunnels, ensuring patient vitals are never transmitted across the public internet unencrypted.
  • Spring Security evaluates method-level patient ownership rules, ensuring medical records are only accessible to assigned healthcare professionals.
  • Audit logging filters capture the authenticated physician ID and patient record ID, providing tamper-evident compliance trails.

Use Case 3: Critical Infrastructure & Smart Grid Telemetry

Smart energy grids collect sensor data from millions of regional electrical substations:

  • Edge gateway containers authenticate via mTLS certificates attested by hardware Security Modules (HSMs).
  • The Spring Boot ingestion cluster validates lightweight JWTs without querying central database servers, handling millions of telemetry events per second.
  • eBPF kernel probes monitor unexpected outbound network connections from the Java ingestion nodes, instantly killing containers that attempt unauthorized outbound internet calls.

10. Production Hardening & Operational Best Practices

Deploying Zero-Trust systems to production requires adherence to strict operational standards:

  1. Separate Network and Application Responsibilities: Let eBPF manage L4 network encryption and node identity. Let Spring Security manage L7 authorization, scopes, and user permissions. Do not duplicate mTLS inside Java keystores when an ambient mesh is present.
  2. Tune JWKS Cache Settings: By default, Nimbus caches public keys for 5 minutes. In high-traffic environments, verify your cache timeout so that public key rotation does not cause temporary authentication failures during rolling IdP deployments.
  3. Keep Token Expirations Short: Configure access tokens to expire within 5 to 15 minutes. Use refresh token rotation in Keycloak to reissue tokens securely.
  4. Validate Token Audience (aud): Always configure Spring Security to validate the aud claim. This ensures a token issued for a frontend client cannot be maliciously replayed against backend administrative APIs.
  5. Enforce Kubernetes Network Policies: Complement eBPF transport security with strict Cilium Network Policies (CNP) that restrict ingress and egress traffic at the pod label level.

11. Common Pitfalls and How to Avoid Them

Engineers implementing Zero-Trust microservices frequently encounter three critical pitfalls:

Pitfall 1: eBPF Linux Kernel Version Incompatibility

Symptom: eBPF programs fail to attach or drop socket connections unexpectedly on container startup.

Root Cause: Advanced eBPF features (such as BPF sockops and sidecarless ambient redirection) require modern Linux kernels. Running on legacy enterprise distributions with Linux kernel versions below 5.10 causes verification errors in the kernel BPF verifier.

Remediation: Ensure your Kubernetes worker nodes run Linux kernel 5.15 or newer (such as Ubuntu 22.04 LTS or Amazon Linux 2023) with BTF (BPF Type Format) enabled.

Pitfall 2: Missing “SCOPE_” Prefix in SpEL Expressions

Symptom: The token contains "scope": "finance:read", but the @PreAuthorize("hasAuthority('finance:read')") check fails with HTTP 403 Forbidden.

Root Cause: Spring Security’s default JwtGrantedAuthoritiesConverter automatically prepends SCOPE_ to each string found in the scope claim.

Remediation: In your SpEL expressions, always reference the authority with the prefix: hasAuthority('SCOPE_finance:read') or use the convenience expression hasScope('finance:read').

Pitfall 3: Accidental Lockout of Kubernetes Liveness Probes

Symptom: Kubernetes pods enter a CrashLoopBackOff state immediately after deploying the Spring Security configuration.

Root Cause: The Kubernetes kubelet probes /actuator/health/liveness over plain HTTP without a Bearer token. If Spring Security requires authentication on all routes without an explicit permit rule, health checks return HTTP 401, causing Kubernetes to kill the container.

Remediation: Always include requestMatchers("/actuator/health/**").permitAll() in your SecurityFilterChain before declaring anyRequest().authenticated().

12. Technical FAQ, Companion Code & References

Frequently Asked Questions

Can I implement Zero-Trust in Spring Boot without an eBPF service mesh?

Yes. You can achieve Layer 7 Zero-Trust using Spring Security 7, JWT validation, and manual SSL/TLS keystores. However, managing Mutual TLS across hundreds of Java microservices without an eBPF mesh creates severe operational overhead and increased network latency.

How does eBPF encrypt traffic without changing the Spring Boot code?

eBPF programs attach to Linux kernel network hooks (such as sockops and tc). When the Java process writes data to its local socket, eBPF intercepts the packet at the kernel level, wraps it in an encrypted tunnel (WireGuard or IPsec), and decrypts it upon arrival at the destination node kernel before delivering it to the target pod socket.

What is the difference between Role-Based Access Control (RBAC) and Attribute-Based Access Control (ABAC)?

RBAC assigns permissions based strictly on static user roles (such as ROLE_ADMIN or ROLE_USER). ABAC evaluates dynamic attributes such as the resource owner ID, time of day, geolocation, and request context (for example, verifying that @securityService.isOwner(authentication, #accountId) evaluates to true).

Does Spring Security 7 support stateless JWT verification without database lookups?

Yes. Spring Security’s JwtDecoder downloads and caches the public keys from Keycloak’s JWKS endpoint. It verifies asymmetric digital signatures entirely in memory, validating claims and scopes in less than one millisecond per request.


Companion Workshop Code

The companion codebase for this guide is available on GitHub. It contains the complete Spring Boot 4 financial microservice, Spring Security 7 configuration, Keycloak Docker Compose environment, and the full automated verification test suite:

👉 View the Companion Workshop Code on GitHub

Authoritative References

Comments