Spring Boot
Capstone – Article 8: Role-Based Authorization (USER vs ADMIN)
By Utility Zone · 2026-01-27T18:32:43.959975
1. Introduction
Authentication answers who you are. Authorization answers what you are allowed to do.
In this article, we will:
- Introduce roles (
USER,ADMIN) - Restrict APIs based on roles
- Apply authorization the Spring Security way
This is critical for enterprise-grade applications.
2. Role-Based Authorization Overview
Typical rules:
- USER → manage own data
- ADMIN → manage all users
Authorization can be applied:
- At endpoint level
- At method level
Spring Security supports both.
3. Storing Roles in User Entity
We already have:
@Column(nullable = false)
private String role;
Values:
USER
ADMIN
Best practice:
- Prefix internally with
ROLE_ - Expose clean role names
4. Converting Role to GrantedAuthority
Update JwtAuthFilter logic conceptually:
List<GrantedAuthority> authorities =
List.of(new SimpleGrantedAuthority("ROLE_" + userRole));
Spring Security checks roles using:
ROLE_ADMIN
ROLE_USER
5. Updating JWT to Include Role
Enhance JWT generation:
public String generateToken(String email, String role) {
return Jwts.builder()
.setSubject(email)
.claim("role", role)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(Keys.hmacShaKeyFor(secret.getBytes()), SignatureAlgorithm.HS256)
.compact();
}
Extract role:
public String extractRole(String token) {
return Jwts.parserBuilder()
.setSigningKey(secret.getBytes())
.build()
.parseClaimsJws(token)
.getBody()
.get("role", String.class);
}
6. Updating JWT Filter to Set Role
String role = jwtUtil.extractRole(token);
GrantedAuthority authority =
new SimpleGrantedAuthority("ROLE_" + role);
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(email, null, List.of(authority));
SecurityContextHolder.getContext().setAuthentication(auth);
This attaches role to the request context.
7. Securing Endpoints by Role
7.1 Using Security Config
.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/users/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
)
7.2 Example Admin Controller
@RestController
@RequestMapping("/admin")
public class AdminController {
@GetMapping("/users")
public List<UserResponse> getAllUsers() {
return userService.getAllUsers();
}
}
Only ADMIN can access this.
8. Method-Level Security (Optional but Powerful)
Enable method security:
@EnableMethodSecurity
Add to config.
Then:
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
Cleaner and more granular.
9. Testing Authorization
Test scenarios:
- USER token →
/admin/users→ 403 - ADMIN token →
/admin/users→ 200 - No token → any secured API → 401
This proves authorization works.
10. Common Authorization Mistakes
❌ Forgetting ROLE_ prefix
❌ Using roles as strings everywhere
❌ Not testing forbidden cases
❌ Mixing auth & business logic
11. Git Commit (Important)
git add .
git commit -m "Add role-based authorization for USER and ADMIN"
12. What You Should Have Now
At this point:
- Roles are enforced
- APIs are protected correctly
- Security is enterprise-grade
13. What’s Next?
➡ Capstone – Article 9: Expense Entity & CRUD APIs
- Expense entity
- User-expense relationship
- Core business logic
Type Next when you’re ready 🚀