I'm always excited to take on new projects and collaborate with innovative minds.
academy@sogdo.com
https://academy.sogdo.in/
Sogdo Academy
Spring Security is responsible for several distinct concerns:
Authentication is the process of verifying the identity of a user, application, or service before granting access to a system. It ensures that the entity requesting access is who it claims to be.
Purpose
Examples
Authentication and authorization are related but perform different functions.
| Authentication | Authorization |
|---|---|
| Verifies identity | Determines permissions |
| Answers "Who are you?" | Answers "What can you access?" |
| Performed before authorization | Performed after authentication |
| Validates credentials | Validates roles and privileges |
Example
A user logs into an online banking application.
Identity represents the actual user, application, or device registered in the system.
Examples:
Identity usually contains:
A Principal is the authenticated representation of an identity within the application. Once authentication succeeds, the application works with the Principal instead of the raw identity information.
In Spring Security, the authenticated Principal is available through the Authentication object.
Credentials are the information presented during authentication to prove identity.
Common credentials include:
Best Practices
Authentication factors define the methods used to verify identity.
Information known only to the user.
A physical object possessed by the user.
Biometric characteristics.
Location-based verification.
Behavior-based authentication.
Using multiple authentication factors significantly improves security.
Authentication is the process of verifying the identity of a user before granting access to protected resources. Among all authentication mechanisms, username and password authentication remains the most widely adopted approach in enterprise applications.
Despite the availability of OAuth2, OpenID Connect, Passkeys, and biometric authentication, almost every enterprise system still supports username and password authentication either as a primary authentication method or as part of a multi-factor authentication process.
Examples include:
Every system needs a reliable way to uniquely identify users.
Without authentication:
Authentication establishes trust between the user and the application.
A typical authentication lifecycle consists of the following stages:
Client
│
▼
Login Form
│
▼
UsernamePasswordAuthenticationFilter
│
▼
AuthenticationManager
│
▼
AuthenticationProvider
│
▼
UserDetailsService
│
▼
Database
│
▼
PasswordEncoder
│
▼
Authentication Success
│
▼
SecurityContext| Class | Responsibility |
|---|---|
| UsernamePasswordAuthenticationFilter | Reads login request |
| UsernamePasswordAuthenticationToken | Holds username and password |
| AuthenticationManager | Coordinates authentication |
| ProviderManager | Default AuthenticationManager implementation |
| DaoAuthenticationProvider | Database-based authentication |
| UserDetailsService | Loads user from database |
| UserDetails | Represents authenticated user |
| PasswordEncoder | Password hashing and verification |
| SecurityContextHolder | Stores authenticated user |
| Authentication | Represents logged-in user |
(Then explain every internal class one by one.)
Purpose
Responsibilities
Internal Source Code
Method Flow
Filter Execution
Request Interception
Authentication Token Creation
Success Handler
Failure Handler
Exception Handling
Filter Ordering
Thread Safety
Production Considerations
Definition
Responsibilities
Internal Working
ProviderManager
authenticate() Method
Authentication Object
Exception Handling
Custom AuthenticationManager
Best Practices
Production Flow
Definition
Responsibilities
supports()
retrieveUser()
additionalAuthenticationChecks()
Password Matching
UserDetailsService Integration
PasswordEncoder Integration
Authentication Success
Authentication Failure
Custom Provider
Enterprise Example
How BCrypt compares passwords
Why plain text passwords are never compared
Salt generation
Hash comparison
Constant time comparison
Password upgrade strategy
SecurityContext Creation
Authentication Object
Session Creation
Remember Me
Redirect Strategy
Success Handler
Audit Logging
Bad Credentials
Disabled User
Locked User
Expired Account
Credentials Expired
Exception Translation
Failure Handler
Audit Logging
Credential Stuffing
Password Spraying
Brute Force Attack
Replay Attack
Session Hijacking
MITM
Key Logging
Phishing
CSRF
XSS
SQL Injection
After completing this chapter, you should understand:
Spring Security is a comprehensive security framework for Java applications built on the Spring Framework.
It provides authentication, authorization, protection against common web attacks, session management, OAuth2, JWT, LDAP, SAML, OAuth2 Login, Method Security, CSRF protection, CORS support, and many other security features.
Instead of developers implementing security from scratch, Spring Security provides a flexible and extensible architecture that integrates with the Spring ecosystem.
Spring Security is a framework that provides authentication, authorization, and protection against common security vulnerabilities for Java applications.
This definition is correct, but it does not explain what Spring Security actually does internally.
Imagine you create a Spring Boot application.
Browser
│
▼
Spring Boot
│
▼
ControllerWithout Spring Security:
@GetMapping("/profile")
public String profile() {
return "Profile";
}Anyone can call:
GET /profileThere is no verification of:
The application simply executes the controller method.
Consider an Employee Management System.
Modules:
Without security:
GET /employeesAnyone can access employee records.
GET /salaryAnyone can view salary details.
DELETE /employee/15Anyone can delete employees.
This is unacceptable in any production application.
Before Spring Security, developers often wrote authentication logic in every controller.
Example:
@GetMapping("/employees")
public List<Employee> getEmployees(HttpSession session) {
if(session.getAttribute("USER") == null) {
throw new RuntimeException("Login Required");
}
return employeeService.findAll();
}Then the next controller:
@GetMapping("/salary")Again:
if(session.getAttribute("USER") == null)Then:
@PostMapping("/payroll")Again:
if(session.getAttribute("USER") == null)The same code is repeated everywhere.
Problems:
Instead of every controller checking security:
Controller
│
Check LoginSpring Security says:
"Intercept the request before it reaches the controller."
So the architecture becomes:
Browser
│
▼
Spring Security
│
▼
ControllerEvery request must pass through Spring Security first.
If security checks fail, the controller is never called.
Suppose a user requests:
GET /employeesWithout Spring Security:
Browser
│
▼
DispatcherServlet
│
▼
EmployeeControllerWith Spring Security:
Browser
│
▼
Spring Security Filters
│
▼
DispatcherServlet
│
▼
EmployeeControllerNotice something important:
Spring Security executes before Spring MVC.
This is one of the most fundamental concepts in Spring Security.
Spring Security is built on the Servlet Filter mechanism.
A filter sits between the client and the application.
Client
│
▼
Filter
│
▼
ApplicationA filter can:
Because filters execute before controllers, they are the ideal place to enforce security.
Authentication is the process of verifying the identity of a user, application, service, or device before allowing access to protected resources.
Simply put,
Authentication answers one question: "Who are you?"
The system does not care what you can do yet. It first wants to know whether you are really the person you claim to be.
Imagine you enter an airport.
The security officer asks for your passport.
You show your passport.
The officer compares:
If everything matches,
You are identified.
Only after that can you proceed to immigration.
This is authentication.
Suppose you visit
https://company.com/dashboardSpring Security asks
Who are you?You enter
Username:
john
Password:
123456Spring Security verifies
If every answer is valid
Authentication succeeds.
Many developers confuse these.
Authentication
Who are you?Authorization
What can you access?Example
Employee logs in.
Authentication says
You are John.Authorization says
John can view payroll.
John cannot delete employees.Two completely different responsibilities.
Suppose authentication didn't exist.
Anyone could access
GET /salaryAnyone could access
GET /adminAnyone could transfer money.
Anyone could delete data.
Clearly impossible.
Authentication protects identity.
Imagine this controller.
@RestController
public class EmployeeController {
@GetMapping("/employees")
public List<Employee> getEmployees() {
return employeeRepository.findAll();
}
}Anyone can call
GET /employeesNo login.
No password.
No identity.
No verification.
Many beginners write
@RestController
public class EmployeeController {
@GetMapping("/employees")
public String getEmployees(HttpServletRequest request){
String username=request.getParameter("username");
String password=request.getParameter("password");
if(username.equals("admin") &&
password.equals("admin123")){
return "Employee List";
}
return "Access Denied";
}
}Huge problems.
Password visible.
GET /employees?username=admin&password=admin123Password appears in
Very insecure.
Another problem
Every controller now contains login logic.
@GetMapping("/employee")Again
if(...)Next controller
@GetMapping("/salary")Again
if(...)Next
@GetMapping("/admin")Again
if(...)Same code everywhere.
Imagine company has
300 Controllers
420 REST APIs
150 DevelopersEvery controller
Check login
Check password
Check role
Check account
Check expirationNightmare.
Move authentication outside controllers.
Instead of
Controller
↓
Authenticate
↓
Business LogicSpring Security changes architecture to
Browser
↓
Spring Security
↓
Controller
↓
Business LogicNow
Controller only contains business logic.
Authentication becomes centralized.
Suppose user logs in.
Username
johnPassword
admin123What happens internally?
Browser
↓
POST /login
↓
UsernamePasswordAuthenticationFilter
↓
AuthenticationManager
↓
AuthenticationProvider
↓
UserDetailsService
↓
Database
↓
PasswordEncoder
↓
Authentication Success
↓
SecurityContext
↓
ControllerEvery box above is a Spring Security class.
We will study every one individually.
Let's understand the complete lifecycle.
User enters
Username
PasswordBrowser sends
POST /login
username=john
password=admin123Spring Security intercepts the request.
Controller has not executed yet.
Spring Security creates
UsernamePasswordAuthenticationTokenObject.
Initially
authenticated=falseAuthenticationManager receives
Authenticationobject.
AuthenticationManager asks
Which AuthenticationProvider can authenticate this request?DaoAuthenticationProvider says
I support UsernamePasswordAuthenticationTokenProvider calls
UserDetailsServiceUserDetailsService loads
User
↓
DatabasePasswordEncoder compares
Entered Password
↓
Stored Password HashIf matches
Authentication successful.
Otherwise
Throw exception.
Spring creates
Authenticationobject.
Now
authenticated=trueAuthentication stored inside
SecurityContextRequest continues.
Controller finally executes.
Username
john
Password
123PasswordEncoder returns
falseProvider throws
BadCredentialsExceptionRequest stops.
Controller never executes.
Spring creates
Authenticationcontaining
Principal
Authorities
Credentials
Authenticated=truePossible exceptions
BadCredentialsException
UsernameNotFoundException
DisabledException
LockedException
AccountExpiredException
CredentialsExpiredExceptionEach exception represents a different authentication failure.
Instead of every controller checking credentials, Spring Security authenticates once at the start of the request pipeline. This provides:
Consider an online banking application.
Users include:
All of them log in through the same authentication pipeline:
POST /login
│
▼
Spring Security
│
▼
AuthenticationManager
│
▼
AuthenticationProvider
│
▼
UserDetailsService
│
▼
Database / LDAP / Active DirectoryThe authentication process is identical regardless of the user's role. Only after authentication succeeds does authorization determine what each user is allowed to access.
Spring Security Has Only Two Main Jobs
Incoming Request
│
▼
Authenticate User
│
▼
Authorize User
│
▼
Controller
Everything else (JWT, OAuth2, LDAP, Session, SAML...) plugs into this pipeline.
Core Architecture
HTTP Request
↓
Tomcat
↓
Servlet Filter
↓
DelegatingFilterProxy
↓
FilterChainProxy
↓
SecurityFilterChain
↓
Authentication
↓
Authorization
↓
Controller
===================================================================
Spring Security Fundamentals
Spring Security is the standard security framework for Java Spring applications. It provides authentication, authorization, and protection against common web security vulnerabilities.
Authentication answers the question:
Who are you?
It verifies the user's identity using:
Example:
Username: john
Password: ********If valid, Spring creates an authenticated user.
Authorization answers:
What are you allowed to do?
Example:
| User | Role | Permission |
|---|---|---|
| John | USER | Read Products |
| Alice | ADMIN | Read, Create, Delete |
Spring checks permissions before allowing access.
Example:
@GetMapping("/admin")
@PreAuthorize("hasRole('ADMIN')")
public String adminPage() {
return "Admin";
}The authenticated user is called the Principal.
Example:
Authentication auth =
SecurityContextHolder.getContext().getAuthentication();
System.out.println(auth.getName());Output:
johnUser
│
▼
Login Request
│
▼
Authentication Filter
│
▼
Authentication Manager
│
▼
UserDetailsService
│
▼
Database
│
▼
Password Encoder
│
▼
Authentication Success
│
▼
Security ContextEvery request first goes through the Security Filter Chain.
Browser
│
▼
Security Filters
│
▼
ControllerIt performs:
Responsible for loading users.
Example:
@Service
public class MyUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) {
return User.withUsername("john")
.password("$2a$...")
.roles("USER")
.build();
}
}Never store plain-text passwords.
Example:
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}Encoding:
passwordEncoder.encode("password123");Verification:
passwordEncoder.matches(rawPassword, encodedPassword);Responsible for authenticating users.
Username
Password
│
▼
AuthenticationManager
│
▼
UserDetailsServiceStores authenticated user information during the request.
Example:
Authentication authentication =
SecurityContextHolder.getContext().getAuthentication();Without any configuration:
Example:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http)
throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults());
return http.build();
}
}ROLE_ADMIN
ROLE_USER
ROLE_MANAGERCheck:
.hasRole("ADMIN")Spring automatically adds the ROLE_ prefix.
More granular permissions.
READ_PRODUCT
CREATE_PRODUCT
DELETE_PRODUCTCheck:
.hasAuthority("DELETE_PRODUCT")Username
PasswordGood for web applications.
Authorization:
Basic base64(username:password)Commonly used for testing and internal APIs.
Login
│
▼
JWT Token
│
▼
Store Token
│
▼
Send TokenHeader:
Authorization:
Bearer eyJhbGciOi...Best for REST APIs.
Use third-party identity providers like:
CSRF (Cross-Site Request Forgery) prevents unauthorized actions performed on behalf of an authenticated user.
Enabled by default for browser-based applications.
Disabled in many stateless REST APIs using JWT:
http.csrf(csrf -> csrf.disable());Only disable CSRF when appropriate, such as for stateless APIs protected by tokens.
By default:
User
│
Login
│
Session Created
│
Session ID stored in CookieFor JWT:
User
│
JWT Token
│
No SessionConfigure stateless sessions:
http.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
);Enable:
@EnableMethodSecurityExamples:
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser() {}@PreAuthorize("hasAuthority('WRITE')")
public void saveProduct() {}Customize responses:
http.exceptionHandling(ex -> ex
.authenticationEntryPoint(new CustomAuthenticationEntryPoint())
.accessDeniedHandler(new CustomAccessDeniedHandler())
);Client Request
│
▼
Security Filter Chain
│
▼
Authentication Filter
│
▼
AuthenticationManager
│
▼
UserDetailsService
│
▼
PasswordEncoder
│
▼
Authentication Success
│
▼
SecurityContext
│
▼
Authorization Check
│
▼
Controller
│
▼
Response
Form Login Authentication
Form Login is the most common authentication method for web applications.
The user enters a username and password in a login form. Spring Security verifies the credentials and creates a session. After login, the user stays authenticated until they log out or the session expires.
User
│
▼
Login Page
│
Username + Password
│
▼
Spring Security
│
Verify User
│
▼
Session Created
│
▼
Access Protected Pages
HTTP Basic Authentication is a simple authentication method where the client sends the username and password with every HTTP request.
The credentials are Base64 encoded (not encrypted), so it should always be used with HTTPS.
Client
│
▼
Username + Password
│
Base64 Encode
│
▼
Authorization Header
│
▼
Spring Security
│
Verify User
│
▼
ResponseNo Login Page and No Session (commonly used for REST APIs).
src
├── config
│ └── SecurityConfig.java
├── controller
│ └── HomeController.java
└── pom.xml<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("admin")
.password(passwordEncoder().encode("1234"))
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/").permitAll()
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults())
.csrf(csrf -> csrf.disable());
return http.build();
}
}@RestController
public class HomeController {
@GetMapping("/")
public String home() {
return "Public Home";
}
@GetMapping("/api/data")
public String data() {
return "Secure Data";
}
}Open:
http://localhost:8080/Output:
Public HomeNow access:
http://localhost:8080/api/dataBrowser asks for credentials.
Enter:
Username : admin
Password : 1234Output:
Secure DataGET /api/data HTTP/1.1
Host: localhost:8080
Authorization: Basic YWRtaW46MTIzNA==admin:1234
↓
Base64 Encoding
↓
YWRtaW46MTIzNA==Client
│
▼
GET /api/data
│
Authorization Header
│
Basic admin:1234
│
▼
BasicAuthenticationFilter
│
▼
AuthenticationManager
│
▼
UserDetailsService
│
▼
Password Verification
│
▼
Responsecurl -u admin:1234 http://localhost:8080/api/dataadmin1234| Feature | Form Login | HTTP Basic |
|---|---|---|
| Login Page | Yes | No |
| Session | Yes | Typically No |
| Credentials Sent | Once (during login) | Every Request |
| Browser Redirect | Yes | No |
| Best For | Web Applications | REST APIs |
Authorization: Basic header with every request. Spring Security validates the credentials using BasicAuthenticationFilter and grants access if they are correct. It is simple and useful for internal or testing APIs but should always be used over HTTPS.Session Management is the process of keeping a user logged in after successful authentication.
When a user logs in, Spring Security creates an HTTP Session and stores the authenticated user's information. The browser receives a Session ID (JSESSIONID) cookie and sends it with every subsequent request.
The user does not need to log in again until they log out or the session expires.
Authentication Flow
User
│
▼
Login (Username + Password)
│
▼
Authentication Success
│
▼
Create HTTP Session
│
▼
Generate JSESSIONID
│
▼
Browser Stores Cookie
│
▼
Every Request Sends JSESSIONID
│
▼
Spring Security Identifies User
Username : admin
Password : 1234↓
Spring Security authenticates the user.
HTTP Session
Session ID : ABC123XYZ
User : admin
Role : ADMINSet-Cookie: JSESSIONID=ABC123XYZBrowser stores the cookie automatically.
GET /dashboard
Cookie: JSESSIONID=ABC123XYZSpring finds the session and recognizes the user.
Login
│
▼
Session Created
│
▼
User Browses Website
│
▼
Session Active
│
▼
Logout / Timeout
│
▼
Session Destroyed@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults())
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
);
return http.build();
}Always creates a session.
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)Creates a session only when needed.
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)Spring Security does not create a session but can use an existing one.
.sessionCreationPolicy(SessionCreationPolicy.NEVER)No session is created or used.
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)Used with:
http.sessionManagement(session -> session
.maximumSessions(1)
.maxSessionsPreventsLogin(false)
);If the same user logs in from another device:
maxSessionsPreventsLogin(true) is used.http.logout(logout -> logout
.logoutUrl("/logout")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
);When the user logs out:
JSESSIONID cookie is deleted.| Feature | Session | JWT |
|---|---|---|
| Server Stores Login | Yes | No |
| Cookie Used | Yes (JSESSIONID) | No |
| Session Created | Yes | No |
| Best For | Web Applications | REST APIs |
| Logout | Easy (invalidate session) | Client removes token or token expires |
=> Session Management keeps users authenticated by creating an HTTP Session after login. The browser stores a JSESSIONID cookie and sends it with each request. Spring Security uses this session to identify the authenticated user. For traditional web applications, sessions are the standard approach, while modern REST APIs commonly use SessionCreationPolicy.STATELESS with JWT instead.
JWT (JSON Web Token) is a compact, self-contained, and digitally signed token used to securely transfer user identity and authorization information between two parties.
Unlike session authentication, the server does not store user login information. Instead, all required user information is stored inside the token itself.
JWT is an authentication token, not encryption.
It is signed to detect tampering but its payload is usually readable by anyone who has the token.
Without JWT (Session):
Client
│
Login
│
▼
Server
│
Create Session
│
Store User in Memory/Redis/DB
│
Session IDEvery request:
Client
│
Cookie(JSESSIONID)
│
▼
Server
│
Find Session
│
Find UserServer must maintain state.
With JWT
Client
│
Login
│
▼
Auth Server
│
Generate JWT
│
Return TokenEvery request
Authorization:
Bearer eyJhbGciOiJIUzI1Ni...Server verifies token.
No session lookup.
No database lookup (in most cases).
LOGIN
+---------+ +----------------+
| Client |----Username/Password------->| Authentication |
| | | Service |
+---------+ +----------------+
│
│ Validate User
▼
+----------------+
| Database |
+----------------+
│
User Verified
│
▼
Generate JWT
│
▼
<------------- JWT Token ----------------------
=================================================
ACCESSING PROTECTED APIs
Client
│
Authorization: Bearer JWT
│
▼
Spring Security JWT Filter
│
Extract Token
│
Verify Signature
│
Check Expiry
│
Extract Username & Roles
│
Create Authentication Object
│
Store in SecurityContext
│
Authorization Check
│
ControllerA JWT has 3 parts.
xxxxx.yyyyy.zzzzzHeader
.
Payload
.
SignatureExample
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiJuaXRlc2giLCJyb2xlIjoiQURNSU4iLCJleHAiOjE3MzAwMDAwMDB9
.
A8jd92jdlkjs9823jskdj8234...Contains algorithm.
{
"alg":"HS256",
"typ":"JWT"
}Contains claims.
{
"sub":"roman",
"role":"ADMIN",
"email":"abc@gmail.com",
"exp":1730000000
}Anyone can decode this.
Never store
inside JWT.
HMACSHA256(
Header
+
Payload
+
SecretKey
)Example
Signature =
HMACSHA256(
Base64(Header)
+
Base64(Payload),
SECRET_KEY
)Only server knows
SECRET_KEYNo.
JWT is Signed ,Not Encrypted
Anyone can decode Header & Payload.
Only Signature cannot be forged without the secret key.
Suppose attacker changes
{
"role":"USER"
}to
{
"role":"ADMIN"
}New payload
↓
Old signature
↓
Signature mismatch
↓
Rejected.
Incoming JWT
│
▼
Split Token
Header
Payload
Signature
│
▼
Recalculate Signature
using Secret Key
│
▼
Compare
│
├── Same
│ │
│ ▼
│ Valid Token
│
└── Different
│
▼
Invalid TokenIf implemented correctly:
Then forging a valid JWT is computationally infeasible.
JWTs are commonly compromised through:
Not by "cracking" the signature.
Example
Access Token
15 MinutesAfter 15 minutes
↓
401 Unauthorized
↓
Need Refresh Token
Suppose attacker steals token.
Without expiry
↓
Works forever.
With expiry
↓
Only few minutes.
Login
↓
Access Token (15 min)
+
Refresh Token (7 Days)
Flow
Login
│
▼
Access Token
Refresh Token
│
▼
API Calls
│
Access Token Expires
│
▼
Send Refresh Token
│
▼
Generate New Access Token
Access Token
Refresh Token
Immediately rejected.
Signature verification fails.
User must login again.
Yes.
Example
{
"sub":"roman",
"roles":[
"ADMIN",
"USER"
]
}Then Spring Security creates authorities.
.hasRole("ADMIN")works without querying the database for every request (unless you intentionally re-load user data).
API Level
.requestMatchers("/admin/**")
.hasRole("ADMIN")Method Level
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(){}Both work with JWT because the roles are extracted from the validated token and placed into the SecurityContext.
Yes.
Authentication
Who are you?JWT identifies the user.
Authorization
What can you access?Roles/authorities inside the JWT determine access.
Client
│
JWT
▼
Order Service
│
JWT
▼
Payment ServiceMost commonly:
Forward user JWT.
Client
↓
API Gateway
↓
Order Service
↓
Inventory Service
↓
Payment ServiceEach service validates JWT.
Good for user-context propagation and end-to-end authorization.
Service-to-Service Token
Order Service
↓
Client Credentials Token
↓
Payment ServiceNo user context.
Instead,
Service authenticates itself.
Used for internal scheduled jobs, backend communication, etc.
Client
│
Bearer JWT
│
▼
API Gateway
│
Validate JWT (optional)
│
▼
Order Service
│
Validate JWT again
│
▼
Business LogicIn production, gateways often perform initial validation (authentication, rate limiting, routing), but backend services usually also validate the token rather than blindly trusting the gateway. This is called defense in depth.
Absolutely valid.
Client
↓
Spring Boot App
↓
JWT Authentication
↓
ControllerJWT is not only for microservices.
It is widely used for:
Client
│
Login
│
▼
AuthenticationManager
│
▼
UserDetailsService
│
▼
PasswordEncoder
│
▼
Generate JWT
│
▼
Client Stores JWT
│
Every Request
Authorization: Bearer JWT
│
▼
JwtAuthenticationFilter
│
Validate Signature
Check Expiry
Extract Claims
│
▼
Create Authentication
│
▼
SecurityContextHolder
│
▼
Authorization
│
▼
Controller
Real-Time JWT Authentication Project
+----------------------+
| React / App |
+----------+-----------+
|
| POST /login
| username,password
|
v
+---------------------------------------------------------+
| Spring Boot Application |
| |
| AuthenticationController |
| | |
| v |
| AuthenticationManager |
| | |
| v |
| DaoAuthenticationProvider |
| | |
| v |
| UserDetailsService |
| | |
| v |
| Database |
| | |
| v |
| BCryptPasswordEncoder.matches() |
| | |
| v |
| Authentication Success |
| | |
| v |
| JwtService.generateToken() |
| | |
| v |
| Access Token + Refresh Token |
+---------------------------------------------------------+
|
|
| Bearer Token
|
v
+---------------------------------------------------------+
| JwtAuthenticationFilter |
| |
| Extract Token |
| Validate Signature |
| Check Expiry |
| Load User |
| Create Authentication |
| SecurityContextHolder |
+---------------------------------------------------------+
|
v
Controller
Real Project Folder Structure
src/main/java
│
├── config
│ SecurityConfig
│
├── controller
│ AuthenticationController
│ UserController
│
├── entity
│ User
│
├── repository
│ UserRepository
│
├── security
│ JwtAuthenticationFilter
│ JwtService
│ JwtAuthenticationEntryPoint
│
├── service
│ CustomUserDetailsService
│ AuthenticationService
│
├── dto
│ LoginRequest
│ LoginResponse
│ RefreshTokenRequest
│
└── util
JwtUtil (optional)
Imagine this request
POST /login
{
"username":"roman",
"password":"123456"
}Now let's go inside Spring Security.
Request reaches
AuthenticationController@PostMapping("/login")
public LoginResponse login(LoginRequest request){
}Controller should NOT verify password.
Controller only receives request.
Controller calls
authenticationService.login(request);Now controller's work is finished.
AuthenticationService
This is where real authentication begins.
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
username,
password
));Question:
Who checks username?
Who checks password?
Answer:
AuthenticationManager
AuthenticationManager
Think of it as
Traffic PoliceIt doesn't know users.
It simply asks
AuthenticationProvider
Can you authenticate?AuthenticationProvider
Usually
DaoAuthenticationProviderNow Provider asks
UserDetailsService
Load UserCustomUserDetailsService
loadUserByUsername(username)It queries database
SELECT *
FROM users
WHERE username='roman'Database returns
id=10
username=roman
password=$2a$asdad...
roles=ADMINPassword Verification
Very important.
Developer sends
123456Database has
$2a$8jd92j...Now BCrypt
passwordEncoder.matches(
rawPassword,
encodedPassword
)If matches
↓
Authentication Success
Authentication Object Created
Spring creates
Authentication authenticationInside it
Username
Authorities
Principal
isAuthenticated=trueStill
NO JWT.
Now OUR code runs
jwtService.generateToken(user)Now JWT generation begins.
This is NOT Spring Security.
This is YOUR code.
JWT Generation
Header
{
"alg":"HS256",
"typ":"JWT"
}Payload
{
"sub":"roman",
"role":"ADMIN",
"iat":123456,
"exp":123999
}Then
Base64(Header)
+
Base64(Payload)
+
SecretKey
↓
HMACSHA256
↓
SignatureFinally
xxxxx.yyyyy.zzzzzReturn
{
"accessToken":"eyJhbGc..."
}React stores
Local Storage
or
HttpOnly CookieGET /products
Authorization:
Bearer eyJhbGc...Now login is NOT called.
Instead
JWT Filter starts.
Every request
Request
↓
JwtAuthenticationFilter
↓
ControllerFilter executes FIRST.
Filter extracts
Authorization
Bearer eyJhbGc...Remove
BearerNow only
eyJhbGc...Your code
jwtService.isTokenValid(token)Inside
Spring JWT Library
does
Split Token
↓
Header
↓
Payload
↓
SignatureAgain compute
Header
+
Payload
+
SecretKey
↓
HMACSHA256Compare
Generated Signature
==
Incoming SignatureIf different
↓
401 Unauthorized
Then
Expiry check
exp
>
Current Time ?If expired
↓
401
Then
Extract Username
sub=romanLoad User Again
loadUserByUsername("roman")Why?
Because
User may be
Disabled
Deleted
Locked
Roles changed
Create Authentication
UsernamePasswordAuthenticationTokenNow store
SecurityContextHolder
.setAuthentication(authentication)Now Spring knows
Current User
=
romanController starts.
Controller
@GetMapping("/admin")Now authorization begins.
Security checks
.hasRole("ADMIN")JWT contains
ADMINAllowed.
Otherwise
403 Forbidden.
Browser
↓
POST /login
↓
AuthenticationController
↓
AuthenticationService
↓
AuthenticationManager
↓
AuthenticationProvider
↓
UserDetailsService
↓
Database
↓
PasswordEncoder
↓
Authentication Success
↓
JwtService
↓
JWT Generated
↓
Browser Stores JWT
↓
=================================================
GET /products
↓
JwtAuthenticationFilter
↓
Extract JWT
↓
Validate Signature
↓
Check Expiry
↓
Load User
↓
Create Authentication
↓
SecurityContextHolder
↓
Authorization
↓
Controller
↓
ResponsePhase 1 — Build the Project
Spring Boot 3.x
Spring Security 6.x
MySQL/PostgreSQL
JWT
Refresh Token
Role Based Authentication
Method Security
API Level Security
Exception Handling
Logout
Token Revocation
Microservice Ready
Project Structure
auth-service
├── config
├── controller
├── dto
├── entity
├── repository
├── security
├── service
├── exception
├── util
└── application.yml
Client
↓
POST /login
↓
AuthenticationManager
↓
JWT Generated
↓
Access Token
↓
Refresh Token
↓
200 OK
Read Project code you will more details.