Authentication is foundational to every application, but the way we implement it has evolved significantly. For years, session-based authentication (using cookies and server-side state) was the default. Today, token-based authentication, powered by standards like OAuth 2.0 and JWTs, dominates modern API architectures.
Note: This article belongs to Part 1.2: Identity & Access Management in our Application Security series.
Have you ever wandered about Stateless vs Stateful Authentication: When to Use Tokens Over Sessions? In this article, we’ll explore both models in depth, contrast their trade-offs, and walk through implementation examples using Spring Security.
Session-Based Authentication: The Traditional Model
🔍 What It Is
In session-based authentication, a user logs in with credentials. The server validates them and creates a session, storing session data (like the user’s ID) on the server. A session ID is then sent to the client in a cookie.
⚙️ How It Works
- User logs in via form → server creates a session (
HttpSession
) - Server stores user info (e.g., in-memory, Redis, database)
- Client gets a cookie with a session ID (
JSESSIONID
) - On each request, the browser automatically sends the cookie
- The server uses the session ID to look up the user
✅ Spring Security: Session-Based Auth Configuration
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain sessionBasedSecurity(HttpSecurity http) throws Exception {
http
.formLogin() // Enables traditional login page
.and()
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED); // default behavior
return http.build();
}
}
Spring automatically manages the session lifecycle and stores the user in SecurityContext
.
✔️ Pros
- Simpler for web apps (especially monoliths)
- Built-in CSRF protection using same-origin cookies
- Transparent to developers — sessions are managed by framework
❌ Cons
- Doesn’t scale well in distributed systems without session replication
- Requires sticky sessions or centralized session stores (e.g., Redis)
- Not ideal for mobile or single-page apps (SPA) that rely on APIs
- Poor fit for stateless REST APIs

Token-Based Authentication: The Modern Approach
🔍 What It Is
Token-based authentication issues a self-contained token (e.g., JWT) after login. The server does not store any session data. Instead, the token includes all necessary user claims and is sent with every request (usually in an Authorization
header).
⚙️ How It Works
- User logs in (e.g., via OAuth2/OIDC)
- Server or IdP issues an access token (JWT)
- Client stores the token (in memory or localStorage)
- Client includes token in each API request:
Authorization: Bearer <token>
- Backend verifies the token and grants access
✅ Spring Security: Token-Based (Stateless) Auth
@Configuration
public class ResourceServerConfig {
@Bean
public SecurityFilterChain tokenSecurity(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt() // Enables JWT validation
)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS); // crucial for token auth
return http.build();
}
}
Spring will decode the JWT and extract claims automatically. You can inject them using @AuthenticationPrincipal
or access via SecurityContextHolder
.
✔️ Pros
- Stateless — no need for server-side session storage
- Scales easily in distributed and microservices architectures
- Works across web, mobile, CLI, and IoT clients
- Ideal for API-first or decoupled frontend-backend applications
❌ Cons
- Requires secure storage of tokens on the client
- No built-in invalidation (logout can be hard to enforce without token revocation)
- More complex to secure (token expiration, refresh flow, replay protection)
Technical Comparison: Session vs Token Auth
Feature | Session-Based | Token-Based (e.g., JWT) |
---|---|---|
Storage | Server (session store) | Client-side (localStorage, memory) |
Transport | Cookie (JSESSIONID ) | Header (Authorization: Bearer ) |
Stateless | ❌ No | ✅ Yes |
Scalability | Requires sticky sessions or shared store | Scales naturally across services |
Token Format | Session ID (opaque) | JWT (self-contained) |
Revocation | Easy (server-side) | Complex (need blacklist or short TTL) |
CSRF Risk | High (requires CSRF tokens) | Low (tokens in headers are not auto-sent) |
Spring Support | formLogin() , session management | oauth2ResourceServer() , jwt() |
When to Use Which
Use Case | Recommended Approach |
---|---|
Traditional web app (monolith) | ✅ Session-based auth |
SPA with REST APIs | ✅ Token-based auth |
Mobile applications | ✅ Token-based auth |
Microservices with shared identity | ✅ Token-based auth |
Admin panel on intranet | ✅ Session-based auth |
Common Mistakes to Avoid
- ❌ Using sessions in a microservices environment without sticky sessions
- ❌ Storing JWTs in localStorage (consider XSS risks)
- ❌ Not setting
sessionCreationPolicy(STATELESS)
when using tokens - ❌ Confusing
id_token
(OIDC) withaccess_token
(OAuth2)
Security Best Practices
- Use HTTPS exclusively for both models
- Store tokens securely — prefer in-memory over localStorage if possible
- Use short-lived access tokens + refresh tokens
- Protect against CSRF (with session-based auth) and XSS (with token-based auth)
- Rotate keys for signing JWTs regularly
Conclusion
The choice between session-based and token-based authentication depends on your application’s architecture, client types, and scalability requirements.
- Session-based authentication remains appropriate for traditional web apps and internal tools.
- Token-based authentication is the de facto standard for modern, distributed, API-driven systems.
Spring Security supports both models with robust, configurable patterns. By understanding their differences and trade-offs, developers can make informed decisions that balance user experience, security, and scalability.
Check out https://en.wikipedia.org/wiki/Access_token and https://en.wikipedia.org/wiki/JSON_Web_Token Wikipedia links for more information on the current topic.
What’s Next
Up Next in our Application Security Series is:
Part 2.1 👉 OAuth 2.0 Deep Dive: A Guide to Tokens, Flows & Delegate Access with Spring Security
We’ll explore the OAuth 2.0 concepts:
- Authorization flows (Authorization Code, Client Credentials, etc.)
- Access tokens vs refresh tokens
- How OAuth enables delegated access
We’ll also look at how these flows are implemented using Spring Security’s @EnableOAuth2Client
, @EnableAuthorizationServer
, and oauth2Login()
.
Have a Question?
Have implementation questions about Spring Security or architecture concerns around auth? Drop them in the comments — I’d love to hear how you’re applying these patterns in your own stack.