Role-Based Access
IntermediateSpring Security's authorization model uses roles (ROLE_ADMIN) and authorities (fine-grained permissions). URL-level access rules in HttpSecurity handle coarse-grained control; method security handles fine-grained access within a use case.
Overview
Roles are prefixed authorities — ROLE_ADMIN is an authority, hasRole("ADMIN") is shorthand for hasAuthority("ROLE_ADMIN"). GrantedAuthority objects are loaded in UserDetails.getAuthorities() and stored in the SecurityContext. URL-level authorization with authorizeHttpRequests() is coarse-grained (protect all admin endpoints). Method-level security with @PreAuthorize is fine-grained (a user can access their own data but not others'). Use roles for coarse access; custom permissions or ownership checks for fine-grained control.
Roles, Authorities and URL-Level Access
Load roles from the database in UserDetailsService. In SecurityFilterChain, use hasRole(), hasAnyRole(), or hasAuthority() to protect URL patterns.
// Loading roles from DB in UserDetailsService
@Override
public UserDetails loadUserByUsername(String email) {
User user = userRepository.findByEmail(email).orElseThrow(...);
return org.springframework.security.core.userdetails.User
.withUsername(user.getEmail())
.password(user.getPasswordHash())
.roles(user.getRole().name()) // adds "ROLE_" prefix automatically
// Or for fine-grained authorities:
// .authorities(user.getPermissions().stream()
// .map(p -> new SimpleGrantedAuthority(p.name()))
// .toList())
.build();
}
// URL-level authorization in SecurityConfig
.authorizeHttpRequests(auth -> auth
// Public
.requestMatchers("/api/v1/auth/**").permitAll()
.requestMatchers(HttpMethod.GET, "/api/v1/courses/**").permitAll()
// Role-based
.requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
// Multiple roles
.requestMatchers("/api/v1/cohorts/**").hasAnyRole("ADMIN", "INSTRUCTOR")
// Fine-grained authority
.requestMatchers(HttpMethod.POST, "/api/v1/blog/**").hasAuthority("WRITE_BLOG")
// Catch-all
.anyRequest().authenticated()
)Access Current User's Data — Ownership Check
Roles alone can't express "a user can only access their own data." Use the authenticated principal from SecurityContext in the service layer to enforce ownership.
// ✅ Ownership check in service layer
@Service
public class SubmissionService {
public SubmissionDto getSubmission(String submissionId,
String requestingUserId) {
Submission sub = submissionRepository.findById(submissionId)
.orElseThrow(() -> new NotFoundException(submissionId));
// Check ownership — admins bypass
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
boolean isAdmin = auth.getAuthorities().stream()
.anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));
if (!isAdmin && !sub.getUserId().equals(requestingUserId)) {
throw new AccessDeniedException("Cannot access another user's submission");
}
return submissionMapper.toDto(sub);
}
}
// Controller — inject principal from SecurityContext
@RestController
@RequestMapping("/api/v1/submissions")
public class SubmissionController {
@GetMapping("/{id}")
public SubmissionDto getSubmission(
@PathVariable String id,
@AuthenticationPrincipal String userId) {
return submissionService.getSubmission(id, userId);
}
}Key Points to Remember
- 1Roles are prefixed authorities — ROLE_ADMIN; hasRole("ADMIN") is shorthand for hasAuthority("ROLE_ADMIN").
- 2URL-level access rules in HttpSecurity are coarse-grained — evaluated in the order you declare them.
- 3More specific matchers must come before general ones — .anyRequest().authenticated() must be last.
- 4For ownership checks, verify the principal in the service layer — URL rules can't express "own resource".
- 5Load authorities from the database in UserDetailsService — they travel with the JWT or session.
- 6Use hasAuthority() for fine-grained permissions (WRITE_BLOG, DELETE_COURSE) separate from broad roles.
Interview Questions
Sign in to ask AriaWhat is the difference between a role and an authority in Spring Security?
How do you ensure users can only access their own data?
Why must .anyRequest().authenticated() be the last rule in authorizeHttpRequests?
What is the difference between hasRole("ADMIN") and hasAuthority("ROLE_ADMIN")?
How would you implement multi-tenancy access control (users only see their org's data)?
Ask Aria about Role-Based Access
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.