In the previous article, we implemented database authentication using a custom UserDetailsService and Spring Data JPA.
Authentication answers the question:
"Who is the user?"
However, once a user is authenticated, the application still needs to determine what resources that user can access. This is where authorization becomes essential.
Many beginners believe that roles alone are sufficient for access control. While roles work well for smaller applications, enterprise systems often require fine-grained permissions. Instead of granting broad access based on a role, permissions allow precise control over specific actions.
In this article, we will explore role-based and authority-based authorization in Spring Security 7, understand their differences, and implement production-ready access control for REST APIs.
What is Authorization?
Authorization is the process of determining whether an authenticated user has permission to access a resource or perform an action.
For example:
A customer can view products.
A warehouse employee can update inventory.
An administrator can manage users.
A finance manager can generate reports.
All of these users may be authenticated successfully, but each has different permissions.
Understanding Roles
A role is a collection of permissions assigned to a user.
Examples:
ROLE_USER
ROLE_ADMIN
ROLE_MANAGER
ROLE_SUPER_ADMIN
Roles make user management easier because permissions can be grouped together.
Example:
Role | Permissions |
|---|---|
USER | View Products |
ADMIN | Manage Products, Manage Users |
MANAGER | View Reports, Update Inventory |
Understanding Authorities
Authorities represent individual permissions.
Examples:
READ_PRODUCTS
CREATE_PRODUCTS
UPDATE_PRODUCTS
DELETE_PRODUCTS
READ_USERS
CREATE_USERS
DELETE_USERS
VIEW_REPORTS
Instead of asking whether someone is an administrator, Spring Security checks whether they possess the required permission.
This provides much greater flexibility.
Roles vs Authorities
Roles | Authorities |
|---|---|
Broad access | Fine-grained access |
Easier to manage | More flexible |
Suitable for small projects | Suitable for enterprise systems |
Example: ADMIN | Example: DELETE_PRODUCTS |
In production systems, roles usually contain multiple authorities.
Example:
ROLE_ADMIN
READ_PRODUCTS
CREATE_PRODUCTS
UPDATE_PRODUCTS
DELETE_PRODUCTS
READ_USERS
CREATE_USERS
DELETE_USERS
Real-World Example
Consider an E-Commerce Platform.
Customer
Permissions:
Browse products
Place orders
Track shipments
Seller
Permissions:
Add products
Update inventory
View orders
Administrator
Permissions:
Manage users
Manage products
Generate reports
View system logs
Instead of checking only for the ADMIN role, the application checks whether the user has the required authority.
Using Roles
Spring Security provides the hasRole() method.
.requestMatchers("/admin/**")
.hasRole("ADMIN")
Only users with the ROLE_ADMIN role can access these APIs.
Internally, Spring Security automatically adds the ROLE_ prefix.
Therefore:
.hasRole("ADMIN")
actually checks:
ROLE_ADMIN
Using Authorities
Spring Security also provides hasAuthority().
.requestMatchers("/products/create")
.hasAuthority("CREATE_PRODUCTS")
Now access depends on a permission rather than a role.
This approach is more flexible because multiple roles can share the same authority.
Using Multiple Roles
.requestMatchers("/dashboard")
.hasAnyRole("ADMIN","MANAGER")
Both administrators and managers can access the dashboard.
Using Multiple Authorities
.requestMatchers("/reports")
.hasAnyAuthority(
"VIEW_REPORTS",
"DOWNLOAD_REPORTS")
A user possessing either permission can access the endpoint.
Security Configuration Example
@Bean
publicSecurityFilterChainsecurityFilterChain(HttpSecurityhttp)
throws Exception {
returnhttp
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth ->auth
.requestMatchers("/public/**")
.permitAll()
.requestMatchers("/admin/**")
.hasRole("ADMIN")
.requestMatchers("/products")
.hasAuthority("READ_PRODUCTS")
.requestMatchers("/products/create")
.hasAuthority("CREATE_PRODUCTS")
.requestMatchers("/reports")
.hasAnyRole("ADMIN","MANAGER")
.anyRequest()
.authenticated())
.httpBasic(Customizer.withDefaults())
.build();
}
Creating Authorities
Instead of:
ROLE_ADMIN
We can assign:
READ_PRODUCTS
CREATE_PRODUCTS
UPDATE_PRODUCTS
DELETE_PRODUCTS
READ_USERS
UPDATE_USERS
DELETE_USERS
VIEW_REPORTS
The administrator receives all permissions.
Managers receive only reporting permissions.
Customers receive only product viewing permissions.
Converting Database Roles into Authorities
Suppose the database stores:
Username | Role |
|---|---|
john | USER |
admin | ADMIN |
During authentication:
returnUser.builder()
.username(user.getUsername())
.password(user.getPassword())
.roles(user.getRole())
.build();
Spring Security converts these roles into granted authorities.
Using GrantedAuthority
Every permission inside Spring Security is represented by a GrantedAuthority.
Example:
Collection<GrantedAuthority>authorities=
List.of(
newSimpleGrantedAuthority("READ_PRODUCTS"),
newSimpleGrantedAuthority("CREATE_PRODUCTS")
);
These authorities are stored inside the authenticated user.
Whenever an endpoint is accessed, Spring Security compares the required permission with the user's granted authorities.
Real-World Enterprise Example
Imagine a Hospital Management System.
Doctor
Permissions:
READ_PATIENTS
UPDATE_PATIENTS
CREATE_PRESCRIPTIONS
Receptionist
Permissions:
CREATE_PATIENT
VIEW_APPOINTMENTS
Administrator
Permissions:
CREATE_USERS
DELETE_USERS
VIEW_REPORTS
SYSTEM_CONFIGURATION
Instead of creating dozens of roles, permissions provide precise access control.
Common Mistakes
Using Roles for Everything
Small applications can rely on roles.
Large enterprise applications should use authorities.
Hardcoding Permissions
Avoid scattering permission names throughout the codebase.
Instead, create constants.
publicclassPermissions {
publicstaticfinalString
READ_PRODUCTS="READ_PRODUCTS";
}
Giving Everyone ADMIN
Grant only the permissions required for a user's responsibilities.
Follow the Principle of Least Privilege.
Mixing Business Logic with Authorization
Controllers should focus on business operations.
Authorization rules should remain inside the security configuration or method-level annotations.
Production-Level Best Practices
Prefer Authorities for Large Applications
Authorities provide greater flexibility and scalability.
Use RBAC with Fine-Grained Permissions
A common enterprise model:
User
↓
Role
↓
Authorities
↓
API Access
Store Permissions in Database
Instead of hardcoding permissions, maintain them in dedicated tables.
Example:
users
roles
permissions
user_roles
role_permissions
This allows administrators to manage permissions without changing code.
Audit Permission Changes
Record every role assignment and permission modification.
This improves traceability and security.
Avoid Duplicate Permissions
Create reusable permission definitions shared across multiple roles.



