Chaturmind
LearnDSASystem DesignBlogPremium
Sign inGet started
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML

Company

  • Blog
  • Premium
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Spring Security & JWT Auth

Spring Security Basics

  • Spring Security Overview
  • JWT Authentication

Authorization

  • Role-Based Access Control
  • Password Encoding
  • OAuth2 & Social Login Basics
Chaturmind
← Spring Security & JWT Auth

Spring Security Basics

  • Spring Security Overview
  • JWT Authentication

Authorization

  • Role-Based Access Control
  • Password Encoding
  • OAuth2 & Social Login Basics
HomeLearnSpring BootSpring SecurityAuthorization
✓ FreeIntermediate· 13 min read

Role-Based Access Control

Implement RBAC with Spring Security roles, authorities, and method-level security annotations.

Published March 3, 2025


Role-Based Access Control (RBAC) in Spring Security

RBAC restricts access based on roles assigned to users. Spring Security supports two levels: URL-based (in SecurityFilterChain) and method-level (@PreAuthorize).

Roles vs Authorities

In Spring Security:

  • A role is a string prefixed with ROLE_ (e.g., ROLE_ADMIN)
  • An authority is a fine-grained permission (e.g., user:write, report:read)
// hasRole("ADMIN")   checks for GrantedAuthority = "ROLE_ADMIN"
// hasAuthority("user:write") checks for GrantedAuthority = "user:write" exactly

Defining Roles in the User Entity

@Document("users")
public class User implements UserDetails {
    private String email;
    private String passwordHash;
    private Set<String> roles = new HashSet<>(); // e.g., ["USER", "ADMIN"]

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return roles.stream()
            .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
            .toList();
    }

    @Override
    public String getUsername() { return email; }

    @Override
    public String getPassword() { return passwordHash; }

    // Other UserDetails methods default to true for active accounts
}

URL-Level Authorization

http.authorizeHttpRequests(auth -> auth
    .requestMatchers(HttpMethod.GET, "/api/v1/courses/**").permitAll()
    .requestMatchers(HttpMethod.GET, "/api/v1/lessons/**").authenticated()
    .requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
    .requestMatchers("/api/v1/instructor/**").hasAnyRole("ADMIN", "INSTRUCTOR")
    .anyRequest().authenticated()
);

Method-Level Security

@Configuration
@EnableMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig {}

@RestController
public class CourseController {

    @GetMapping("/api/v1/courses")
    public List<CourseDto> listCourses() {
        return courseService.list(); // public — no annotation needed
    }

    @PostMapping("/api/v1/courses")
    @PreAuthorize("hasRole('ADMIN')")
    public CourseDto create(@RequestBody @Valid CourseRequest req) {
        return courseService.create(req);
    }

    @DeleteMapping("/api/v1/courses/{id}")
    @PreAuthorize("hasRole('ADMIN') or @courseService.isOwner(#id, authentication.name)")
    public void delete(@PathVariable String id) {
        courseService.delete(id);
    }
}

Custom Permission Evaluator

For complex rules, implement PermissionEvaluator:

@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {

    @Override
    public boolean hasPermission(Authentication auth, Object target, Object permission) {
        if (target instanceof String resourceId && permission instanceof String action) {
            // Check if user has permission to perform action on resource
            String userId = auth.getName();
            return resourceAccessService.canAccess(userId, resourceId, action);
        }
        return false;
    }

    @Override
    public boolean hasPermission(Authentication auth, Serializable targetId,
            String targetType, Object permission) {
        return false;
    }
}

// Usage:
@PreAuthorize("hasPermission(#lessonId, 'read')")
public LessonDto getLesson(String lessonId) { ... }

Accessing Current User in Service Layer

@Service
public class OrderService {

    public List<Order> getMyOrders() {
        String username = SecurityContextHolder.getContext()
            .getAuthentication().getName();
        return orderRepository.findByUserId(username);
    }
}

// Or inject via @AuthenticationPrincipal in controllers:
@GetMapping("/orders")
public List<OrderDto> getOrders(@AuthenticationPrincipal UserDetails user) {
    return orderService.getOrdersFor(user.getUsername());
}

Interview Tips

  1. The difference between hasRole('ADMIN') and hasAuthority('ROLE_ADMIN') — they're equivalent but hasRole auto-prepends ROLE_.
  2. @PreAuthorize vs @Secured: @PreAuthorize supports SpEL and is more flexible; @Secured only supports role names.
  3. Always use method-level security on the service layer if the same service is called from multiple controllers or background jobs.

Previous

JWT Authentication

Next

Password Encoding

AI Tutor

Lesson: Role-Based Access Control

Quick actions

AI responses can be inaccurate. Verify critical information.