In the previous article, we learned how to add Spring Security to a Spring Boot application and secure our first REST API.
However, simply protecting endpoints is not enough. Real-world applications require different users to have different levels of access. For example, customers should not be able to perform administrative operations, and administrators should have privileges that normal users do not.
This is where authentication and authorization come into the picture.
In this article, we will understand these two fundamental concepts and implement role-based access control using Spring Security 7 with Spring Boot 4 and Java 21.
What is Authentication?
Authentication is the process of verifying a user's identity.
In simple words, authentication answers the question:
"Who are you?"
Examples:
Username and password login
OTP verification
Fingerprint authentication
JWT token validation
Google OAuth login
If the identity is valid, the user is authenticated.
What is Authorization?
Authorization determines what resources an authenticated user can access.
Authorization answers the question:
"What are you allowed to do?"
Example:
User Role | Access |
|---|---|
USER | View Products |
ADMIN | Add Products |
ADMIN | Delete Products |
USER | Place Orders |
A user may be authenticated successfully but still be denied access to certain APIs.
Authentication vs Authorization
Authentication | Authorization |
|---|---|
Identifies user | Determines permissions |
Happens first | Happens after authentication |
Answers "Who are you?" | Answers "What can you do?" |
Username and password | Roles and privileges |
Creates Security Context | Checks permissions |
Real-World Example
Consider an E-Commerce Application.
Customer
Allowed to:
View products
Place orders
Track orders
Not allowed to:
Add products
Delete products
Admin
Allowed to:
Manage products
Manage inventory
View reports
Delete orders
Spring Security handles these restrictions using roles and authorities.
Creating In-Memory Users
For learning purposes, we will create users in memory.
@Configuration
public class UserConfig {
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("john")
.password(passwordEncoder().encode("password"))
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password(passwordEncoder().encode("admin123"))
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Understanding the Code
UserDetails
Represents a user inside Spring Security.
It contains:
Username
Password
Roles
Authorities
UserDetailsService
Responsible for loading users.
Spring Security calls this service whenever a user tries to authenticate.
InMemoryUserDetailsManager
Stores users in memory.
Useful for:
Learning
Testing
Prototyping
Not recommended for production systems because users disappear after application restart.
PasswordEncoder
Passwords should never be stored in plain text.
We use BCrypt for hashing passwords.
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
Example:
Plain password:
password
Stored value:
$2a$10$P2P9P7j4t4Fj7e.....
Even if attackers gain database access, original passwords remain protected.
Creating Controller APIs
@RestController
@RequestMapping("/api")
public class EmployeeController {
@GetMapping("/welcome")
public String welcome() {
return "Public API";
}
@GetMapping("/user")
public String userApi() {
return "User Dashboard";
}
@GetMapping("/admin")
public String adminApi() {
return "Admin Dashboard";
}
}
Configuring Role-Based Access
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http)
throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/welcome")
.permitAll()
.requestMatchers("/api/user")
.hasAnyRole("USER", "ADMIN")
.requestMatchers("/api/admin")
.hasRole("ADMIN")
.anyRequest()
.authenticated())
.httpBasic(Customizer.withDefaults())
.build();
}
}
Understanding Role-Based Authorization
Public API
.requestMatchers("/api/welcome")
.permitAll()
Everyone can access this endpoint.
User API
.requestMatchers("/api/user")
.hasAnyRole("USER","ADMIN")
Accessible by:
USER
ADMIN
Admin API
.requestMatchers("/api/admin")
.hasRole("ADMIN")
Only ADMIN users can access this endpoint.
Testing APIs
Public Endpoint
GET /api/welcome
Response:
Public API
No authentication required.
User Endpoint
Login:
Username : john
Password : password
Request:
GET /api/user
Response:
User Dashboard
Admin Endpoint
Login:
Username : admin
Password : admin123
Request:
GET /api/admin
Response:
Admin Dashboard
Unauthorized Access
Suppose user "john" tries to access:
GET /api/admin
Spring Security returns:
403 Forbidden
This means:
User is authenticated.
User does not have sufficient permissions.
Internal Authentication Flow
Client
↓
Security Filter Chain
↓
Authentication Manager
↓
UserDetailsService
↓
PasswordEncoder
↓
Security Context
↓
Authorization
↓
Controller
The authenticated user information is stored inside the Security Context and is used during authorization checks.
Production-Level Best Practices
Never Store Plain Passwords
Always use:
BCryptPasswordEncoder
or stronger algorithms supported by Spring Security.
Avoid InMemoryUserDetailsManager
Production applications should load users from:
PostgreSQL
MySQL
MongoDB
LDAP
using a custom UserDetailsService.
Use the Principle of Least Privilege
Users should only receive permissions required to perform their tasks.
Avoid giving ADMIN privileges unnecessarily.
Separate Roles and Permissions
Large applications usually define:
Roles:
ADMIN
USER
MANAGER
Permissions:
READ_PRODUCTS
CREATE_PRODUCTS
DELETE_PRODUCTS
This provides better flexibility.
Use JWT for Stateless APIs
Modern microservices generally use:
JWT Authentication
OAuth2
OpenID Connect
instead of HTTP Basic Authentication.
Summary
In this article, we learned:
Authentication and Authorization
Their differences
UserDetails and UserDetailsService
InMemoryUserDetailsManager
PasswordEncoder and BCrypt
Role-based authorization
hasRole() and hasAnyRole()
Security flow inside Spring Security
Production-level recommendations
In Part 3, we will explore Password Encoding, BCryptPasswordEncoder, DelegatingPasswordEncoder, and password security best practices used in production systems.



