Modern cloud systems must protect sensitive data and user privacy. Years ago, developers wrote basic username and password checks against a local database. Today, enterprise systems look very different. Distributed microservices, mobile apps, and single-page web applications communicate across networks every second. Teams now rely on modern standards like OpenID Connect (OIDC), OAuth 2.0, and stateless JSON Web Tokens (JWT) to secure their cloud services.
In 2026, two primary frameworks dominate enterprise security in the Java ecosystem: Spring Security 7 and Jakarta Security 4.0. Spring Security 7 comes with Spring Boot 4 and brings a modern, type-safe Lambda Domain Specific Language (DSL). Jakarta Security 4.0 is part of Jakarta EE 11 and provides standard, annotation-driven security across cloud-native runtimes like Quarkus, Helidon, and WildFly. Both tools handle complex authentication flows, but they follow fundamentally different designs.
This comprehensive guide compares Spring Security 7 and Jakarta Security 4.0 in detail. We examine their architectural models, explore their handling of OAuth 2.0 and JWTs, test their performance with GraalVM native images, and review working code examples from our companion workshop project. By the end of this guide, you will know exactly which security framework fits your team, your infrastructure, and your cloud strategy.
Table of Contents
- 1. Core Architectural Paradigms: Centralized Filter Chains vs Decentralized CDI Annotations
- 2. Spring Security 7 Deep Dive: Lambda DSL, Filter Order, and Virtual Threads
- 3. Jakarta Security 4.0 Deep Dive: Annotations, IdentityStore, and CDI Proxies
- 4. Modern OAuth 2.0 and OpenID Connect (OIDC) Resource Server Flows
- 5. Data Layer Security: Method Authorization, Jakarta Data 1.0 vs Spring Data JPA
- 6. Ahead-of-Time (AOT) Compilation and GraalVM Native Image Performance
- 7. Comparative Analysis Matrix
- 8. Enterprise Production Use Cases: When to Choose Which Framework
- 9. Common Security Pitfalls and How to Fix Them
- 10. Frequently Asked Questions (FAQ)
- 11. Conclusion and Next Steps
- 12. Verified Resources and Documentation
1. Core Architectural Paradigms: Centralized Filter Chains vs Decentralized CDI Annotations
To understand the difference between Spring Security and Jakarta Security, we must look at how each framework intercepts incoming HTTP traffic. Every security framework answers two primary questions: Who is making this request? (authentication) and Is this user allowed to perform this action? (authorization). However, where and when these checks occur differs greatly between the two platforms.
Spring Security uses a centralized, pipeline-based model. It intercepts requests at the servlet container boundary using a single entry point called DelegatingFilterProxy. This proxy hands control to FilterChainProxy, which manages a strictly ordered list of servlet filters known as the SecurityFilterChain. Every incoming HTTP request must pass through these filters sequentially before reaching Spring MVC’s DispatcherServlet. If an unauthenticated user attempts to reach a protected endpoint, a filter rejects the request immediately. This architecture centralizes all security rules into a single configuration class.
Jakarta Security takes an annotation-driven, contract-based approach. Instead of wrapping the entire application inside a monolithic filter chain, Jakarta Security integrates directly into Contexts and Dependency Injection (CDI) and the Jakarta RESTful Web Services (JAX-RS) pipeline. Authentication is handled by an implementation of HttpAuthenticationMechanism, while authorization rules are declared directly on endpoints and business services using standard annotations like @RolesAllowed. CDI interceptor proxies evaluate security rules when methods execute.
Both models provide strong guarantees. The centralized model gives security administrators a single place to inspect every routing rule, URL match pattern, and header policy. The decentralized model gives developers clean, self-documenting code where business methods clearly state their own access requirements.
2. Spring Security 7 Deep Dive: Lambda DSL, Filter Order, and Virtual Threads
Spring Security 7 builds upon the foundation of Spring Boot 4. In earlier versions of Spring, developers frequently extended base classes like WebSecurityConfigurerAdapter and wrote chained builder calls. Spring Security 7 eliminates legacy patterns entirely. Today, configuration is type-safe and declarative thanks to the functional Lambda DSL.
The core configuration unit in Spring Security 7 is the SecurityFilterChain bean. By declaring this bean, developers configure endpoint authorizations, CSRF protection, CORS policies, session management, and OAuth2 resource server settings in one place:
package com.webcodein.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
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
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
.build();
}
}
In this example, incoming requests matching /api/public/** bypass authorization checks. Requests to /api/admin/** require the caller to possess the ROLE_ADMIN authority. Any other request requires a valid, authenticated principal. The call to oauth2ResourceServer() activates the JWT bearer token filter.

Filter Ordering and Execution Lifecycle
Understanding the exact sequence of servlet filters is essential when debugging Spring Security applications. In Spring Security 7, the default filter chain processes requests in a precise order:
- SecurityContextHolderFilter: Loads the authenticated principal from the persistent session repository or clears the context for stateless requests.
- HeaderWriterFilter: Appends standard security headers (Content Security Policy, X-Frame-Options, Strict-Transport-Security) to the HTTP response.
- CorsFilter: Handles Cross-Origin Resource Sharing pre-flight OPTIONS requests before authentication begins.
- CsrfFilter: Validates anti-forgery tokens for state-modifying requests (PUT, POST, DELETE) when sessions are stateful.
- BearerTokenAuthenticationFilter: Reads the
Authorization: Bearer <token>header from the request, invokes the configuredAuthenticationManager, and stores the resulting token in theSecurityContext. - AuthorizationFilter: Evaluates request matchers declared in
authorizeHttpRequests()and throws anAccessDeniedExceptionif the caller lacks required permissions.
Compatibility with Java 25 Virtual Threads
Modern applications running on Java 25 Virtual Threads require special consideration. By default, Spring Security stores identity data inside a standard ThreadLocal variable through the SecurityContextHolder. When millions of lightweight Virtual Threads mount and unmount from OS carrier threads, developers must ensure security data transfers cleanly without leaking across requests. Spring Security 7 natively adapts to Virtual Threads by cleaning contexts immediately upon request completion.
3. Jakarta Security 4.0 Deep Dive: Annotations, IdentityStore, and CDI Proxies
Jakarta Security 4.0 is part of the Jakarta EE 11 specification. Instead of locking projects into a single vendor ecosystem, Jakarta Security provides a standard set of interfaces and annotations. Any compliant runtime—such as Quarkus, Open Liberty, WildFly, or Eclipse GlassFish—can execute this code without modification.
The Jakarta Security model separates authentication from identity storage. It defines three key abstractions:
- HttpAuthenticationMechanism: Intercepts the HTTP message exchange, extracts credentials (such as cookies, tokens, or headers), and validates them.
- IdentityStore: Validates user credentials against an identity provider (such as an LDAP directory, a relational database, or an OIDC server) and returns identity groups and roles.
- SecurityContext: A standardized CDI bean that provides program access to the caller’s principal and role membership at runtime.
Below is the configuration from our companion workshop project. Notice how declarative the setup is:
package com.webcodein.security;
import jakarta.annotation.security.RolesAllowed;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.security.enterprise.authentication.mechanism.http.OpenIdAuthenticationMechanismDefinition;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@OpenIdAuthenticationMechanismDefinition(
providerURI = "${oidc.provider.url}",
clientId = "${oidc.client.id}",
clientSecret = "${oidc.client.secret}"
)
@ApplicationScoped
public class SecuritySetup {
// Jakarta EE container wires up OIDC automatically via MicroProfile Config
}
@Path("/api/admin")
@ApplicationScoped
public class AdminResource {
@GET
@RolesAllowed("ADMIN")
public String getAdminData() {
return "Top Secret Enterprise Data";
}
}

Direct Claim Injection with MicroProfile JWT
One major strength of the Jakarta ecosystem is its native integration with Eclipse MicroProfile JWT Authentication. In Spring Security, extracting custom claims from a JWT token often requires writing a custom Converter<Jwt, AbstractAuthenticationToken> class. In Jakarta Security, developers inject claims directly into CDI beans using the @Claim annotation:
package com.webcodein.security;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import org.eclipse.microprofile.jwt.Claim;
@RequestScoped
public class UserProfileService {
@Inject
@Claim("preferred_username")
private String username;
@Inject
@Claim("email_verified")
private Boolean isEmailVerified;
public String getUsername() {
return username;
}
public Boolean getIsEmailVerified() {
return isEmailVerified;
}
}
When an authenticated request arrives, the CDI container automatically inspects the active token, deserializes the requested claim, and injects the value into the field. This pattern reduces boilerplate code and keeps business services decoupled from HTTP request objects.
4. Modern OAuth 2.0 and OpenID Connect (OIDC) Resource Server Flows
Modern enterprise architectures rarely store passwords inside local application databases. Instead, organizations deploy dedicated Identity Providers (IdP) such as Keycloak, Microsoft Entra ID, or Okta. Both Spring Security 7 and Jakarta Security 4.0 operate primarily as OAuth 2.0 Resource Servers in these topologies.
The token exchange workflow follows five standard steps:
- User Authentication: The client application (such as an Angular single-page application) redirects the user to the IdP. The user signs in using multi-factor authentication, and the client completes the authorization code flow with Proof Key for Code Exchange (PKCE).
- Token Issuance: The IdP generates a cryptographically signed JSON Web Token (JWT) using the RS256 or ES256 algorithm. The token payload contains claims describing the user:
sub,iss,exp, and role memberships. - API Request: The client sends an HTTP request to the backend resource server, attaching the token in the
Authorization: Bearer <token>header. - JWKS Key Discovery & Verification: The resource server retrieves public keys from the IdP JSON Web Key Set (JWKS) endpoint at
/.well-known/openid-configuration. It caches these keys and validates the cryptographic signature of the token without making remote calls for every request. - Context Population: Once validated, the security runtime extracts user roles and assigns them to the current execution context. The backend service checks authorization rules and returns protected business data.

Configuration Differences
In Spring Security 7, setting up an OIDC resource server requires just two properties inside application.yml:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://auth.webcodein.com/realms/enterprise-realm
In Jakarta Security with MicroProfile Config, equivalent settings are placed inside microprofile-config.properties:
mp.jwt.verify.publickey.location=https://auth.webcodein.com/realms/enterprise-realm/protocol/openid-connect/certs
mp.jwt.verify.issuer=https://auth.webcodein.com/realms/enterprise-realm
Both frameworks handle JWKS caching and key rotation automatically. If an identity provider rotates its RSA keys, both runtimes reload the new public keys when an unknown key ID (kid) appears in an incoming token header.
5. Data Layer Security: Method Authorization, Jakarta Data 1.0 vs Spring Data JPA
Security must extend beyond the HTTP controller layer. Defense-in-depth requires that service methods and repository interfaces enforce authorization rules before database queries execute. If an unauthorized component calls a service method internally, the framework must block the call.
In Spring Security 7, method security uses Spring Expression Language (SpEL) annotations like @PreAuthorize and @PostAuthorize. These annotations can inspect method parameters and caller roles:
@Service
public class OrderService {
@PreAuthorize("hasRole('ADMIN') or #customerId == authentication.name")
public Order getOrderDetails(String customerId, Long orderId) {
return orderRepository.findByCustomerIdAndId(customerId, orderId);
}
}
In the Jakarta ecosystem, Jakarta Security integrates smoothly with Jakarta Data 1.0. Because Jakarta Data repositories are standard CDI beans, standard annotations like @RolesAllowed apply directly to repository interfaces:
import jakarta.annotation.security.RolesAllowed;
import jakarta.data.repository.CrudRepository;
import jakarta.data.repository.Repository;
@Repository
public interface FinancialAuditRepository extends CrudRepository<AuditRecord, Long> {
@RolesAllowed("COMPLIANCE_OFFICER")
List<AuditRecord> findByFlaggedTrue();
}
When any component invokes findByFlaggedTrue(), the CDI container checks the current SecurityContext before executing the database query. This ensures security rules remain enforced even during internal batch processing or asynchronous jobs.
6. Ahead-of-Time (AOT) Compilation and GraalVM Native Image Performance
Cloud-native architectures increasingly rely on GraalVM Native Images to minimize cold-start latency and reduce memory usage in serverless environments like AWS Lambda and Google Cloud Run. Compiling Java applications into native machine code requires static analysis ahead of time.
Spring Security and Jakarta Security approach native compilation with different design philosophies:
Spring Security 7 (Spring AOT): Spring Boot 4 includes a powerful AOT engine. During the build process (mvn spring-boot:process-aot), Spring analyzes configuration classes, evaluates bean definitions, and pre-computes the SecurityFilterChain. It generates GraalVM reachability metadata (reflect-config.json and proxy-config.json) automatically. While this significantly speeds up startup times, Spring still retains some dynamic proxy infrastructure at runtime.
Jakarta Security 4.0 (Quarkus / Helidon Build-Time CDI): Runtimes like Quarkus follow a “closed-world assumption.” At compile time, the framework scans all @RolesAllowed and @OpenIdAuthenticationMechanismDefinition annotations, strips out unused code paths, and synthesizes direct bytecode invocations. Because dynamic class loading and runtime reflection are completely removed, the resulting native binary starts instantly and consumes minimal memory.

7. Comparative Analysis Matrix
The table below summarizes the key architectural, performance, and functional differences between Spring Security 7 and Jakarta Security 4.0:
| Dimension | Spring Security 7 (Spring Boot 4) | Jakarta Security 4.0 (Jakarta EE 11) |
|---|---|---|
| Core Architectural Model | Centralized Servlet Filter Chain (SecurityFilterChain) |
Decentralized CDI Interceptors and HttpAuthenticationMechanism |
| Configuration Style | Type-safe functional Lambda DSL | Declarative annotations (@RolesAllowed, @Claim) |
| Runtime Portability | Bound to the Spring Boot ecosystem | Runs on Quarkus, Open Liberty, WildFly, Helidon |
| Token & Claim Extraction | Custom Converter<Jwt, AbstractAuthenticationToken> |
Direct injection via MicroProfile @Claim("key") |
| GraalVM Cold Start Time | ~45 ms – 75 ms (Spring AOT pre-computation) | ~15 ms – 25 ms (Compile-time bytecode synthesis) |
| Native Image Memory (RSS) | ~55 MB – 70 MB baseline | ~28 MB – 35 MB baseline |
| Virtual Threads (Java 25) | Fully supported via automatic context cleanup | Fully supported via non-blocking CDI request scopes |
| Ecosystem & Extension Support | Unmatched (SAML, LDAP, OAuth2 Client, WebFlux) | Standardized (MicroProfile JWT, Jakarta REST) |
8. Enterprise Production Use Cases: When to Choose Which Framework
Every engineering organization faces distinct technical constraints. Selecting the correct security framework depends on your deployment targets, team expertise, and existing infrastructure:
Scenario A: Spring Cloud Microservice Fleet
If your organization runs dozens of microservices orchestrated with Spring Cloud, Spring Security 7 is the natural choice. Features like Spring Cloud Gateway filters, OAuth2 client token relay, and fine-grained SpEL method authorizations integrate seamlessly. Furthermore, if you need to support legacy protocols alongside modern JWTs—such as enterprise LDAP or SAML 2.0—Spring Security provides battle-tested implementations with zero custom glue code.
Scenario B: High-Density Edge & Serverless Functions (Quarkus)
When building microservices that run inside serverless environments (AWS Lambda, Google Cloud Run) or high-density Kubernetes nodes where cold starts impact user experience, Jakarta Security 4.0 paired with Quarkus offers a clear advantage. Its sub-25ms cold starts and tiny 32MB memory footprint reduce cloud infrastructure costs significantly.
Scenario C: Regulated Enterprise Environments (Open Standards)
Government agencies and financial institutions often require software architectures that avoid vendor lock-in. Because Jakarta Security 4.0 is an open standard governed by the Eclipse Foundation, applications can transition between runtimes (e.g., from Open Liberty on IBM Cloud to WildFly on Red Hat OpenShift) without modifying security code.
9. Common Security Pitfalls and How to Fix Them
Deploying security frameworks in production can introduce subtle bugs. Below are three common pitfalls developers encounter and how to fix them:
Pitfall 1: Missing @EnableWebSecurity Annotation in Spring Boot 4
In Spring Boot 4, custom SecurityFilterChain beans require the @EnableWebSecurity annotation on the configuration class when testing slices (such as @WebMvcTest). Without this annotation, the context loader fails with a NoSuchBeanDefinitionException: No qualifying bean of type HttpSecurity available.
Fix: Always place @EnableWebSecurity alongside @Configuration on your main security configuration class:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
// Bean definitions
}
Pitfall 2: Audience (aud) Validation Failures in Multi-Service Topologies
When an Identity Provider issues a JWT token for a specific client, it places the client identifier in the aud (audience) claim. If a backend microservice expects a different audience name, the token is rejected with an HTTP 401 Unauthorized status, even though the cryptographic signature is valid.
Fix: In Spring Security, configure an audience validator explicitly using JwtClaimValidator. In Jakarta Security, configure the expected audience inside microprofile-config.properties:
mp.jwt.verify.audiences=financial-service-api
Pitfall 3: Security Context Leaks Across Virtual Threads
Virtual threads are multiplexed onto carrier threads. If an application uses an unmanaged ThreadLocal to store user principals and forgets to clear it after a request completes, a subsequent request processed on the same carrier thread might inherit the previous user’s credentials.
Fix: Never manage manual ThreadLocal security variables. In Spring Security, always use SecurityContextHolder.getContext() and let Spring’s request filters manage cleanup. In Jakarta Security, rely exclusively on @RequestScoped CDI beans, which are cleaned up automatically when the request terminates.
10. Frequently Asked Questions (FAQ)
Can I use Jakarta Security 4.0 inside a Spring Boot application?
While technically possible using adapter libraries, mixing the two ecosystems is not recommended. Spring Boot is optimized for Spring Security. Attempting to run Jakarta Security CDI mechanisms inside Spring’s IoC container causes conflicting proxy definitions and higher memory overhead.
Which framework is more secure?
Both frameworks provide enterprise-grade security. Both implement standard cryptographic checks for JWT validation, protect against common web attacks (CSRF, clickjacking, header tampering), and follow Zero Trust principles. Security depends on correct configuration rather than the choice of framework.
How do the frameworks handle key rotation for OIDC identity providers?
Both frameworks handle key rotation automatically. When an Identity Provider rotates its signing keys, it publishes new public keys to its JWKS endpoint. When an incoming token has a Key ID (kid) not found in the local cache, both runtimes refresh their JWKS cache from the IdP.
Do both frameworks support Java 25 Virtual Threads?
Yes. Spring Security 7 and modern Jakarta Security runtimes (such as Quarkus 3.x and Open Liberty) are fully compatible with Java 25 Virtual Threads. They ensure that blocking security operations do not pin operating system threads.
How difficult is it to migrate from Spring Security 6 to Spring Security 7?
The migration is straightforward if your application already uses the Lambda DSL introduced in Spring Security 5 and 6. If your code still relies on deprecated classes like WebSecurityConfigurerAdapter, you must refactor your configuration into modern SecurityFilterChain beans.
11. Conclusion and Next Steps
Both Spring Security 7 and Jakarta Security 4.0 represent the state of the art in Java application security. Spring Security remains the dominant choice for teams building Spring Boot applications, offering unmatched flexibility, rich third-party adapters, and a centralized DSL. Jakarta Security 4.0 provides a lightweight, open standard that excels in cloud-native runtimes, delivering instant GraalVM cold starts and clean annotation-driven code.
To deepen your understanding, review the working code samples in our companion workshop repository, explore the configuration files, and run the automated test suites on Java 25.
12. Verified Resources and Documentation
- Companion Workshop Repository on GitHub
- Official Spring Security Documentation
- Jakarta Security 4.0 Official Specification
- Quarkus Security Architecture Guide
- What is Spring Boot? A Complete Guide to Spring Boot 4 in 2026
- Introducing Jakarta Data 1.0: Enterprise Alternative to Spring Data
- GraalVM Native Images in Spring Boot 4: Cloud-Native Java Guide
- Java 25 Virtual Threads: Production Pitfalls and Best Practices
