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 🚀