Authorization & Role-Based Access Control
Module 13 answered "who are you." This module is "what are you allowed to do" — and the two places in Spring Security you can actually enforce that answer.
Learning objectives
- Beginner: URL-based role rules only, sufficient when authorization genuinely tracks request paths cleanly (e.g. all of /admin/** is admin-only).
- Intermediate: @PreAuthorize at the service layer for operations needing more than a URL pattern can express, keeping the rule next to the code it protects.
- Advanced: Custom bean-referencing SpEL expressions combining role and resource-ownership checks, for genuinely fine-grained, per-resource authorization.
Spring Security's underlying model is authorities — arbitrary string permissions like ROLE_ADMIN or books:write. A role is just a specific naming convention for a coarse-grained authority, prefixed with ROLE_ by convention, which is why.hasRole("ADMIN") in Module 12 §3's config is really shorthand for checking the authority ROLE_ADMIN — Spring adds the prefix for you automatically.
.authorizeHttpRequests(auth -> auth .requestMatchers("/auth/**").permitAll() .requestMatchers(HttpMethod.GET, "/books/**").authenticated() .requestMatchers(HttpMethod.POST, "/books/**").hasAnyRole("ADMIN", "EDITOR") .requestMatchers("/admin/**").hasRole("ADMIN") .anyRequest().authenticated())
▲ Pitfall
Rules are evaluated in declaration order, and the first matching rule wins — a broad.anyRequest().authenticated() placed before a more specific admin-only rule would silently make the admin rule unreachable. Order matchers from most specific to least specific.
💻 Code example
.authorizeHttpRequests(auth -> auth .requestMatchers("/auth/**").permitAll() .requestMatchers(HttpMethod.GET, "/books/**").authenticated() .requestMatchers(HttpMethod.POST, "/books/**").hasAnyRole("ADMIN", "EDITOR") .requestMatchers("/admin/**").hasRole("ADMIN") .anyRequest().authenticated())
◆ The problem
URL-based rules (§14.2) get coarse and hard to maintain once authorization logic depends on more than the URL pattern alone — e.g. "only an ADMIN, or the book's original creator, can delete it."
@Configuration @EnableMethodSecurity // required to activate @PreAuthorize/@PostAuthorize public class MethodSecurityConfig { }
@Service public class BookService {
@PreAuthorize("hasRole('ADMIN')")
public void deleteBook(Long id) {
bookRepository.deleteById(id);
}
}
@PreAuthorize evaluates its expression before the method body runs, throwing AccessDeniedException (handled centrally in Module 16) if it fails — placing authorization logic right next to the operation it protects, rather than only in a separate, easy-to-drift-out-of-sync security config class.
💻 Code example
@Configuration @EnableMethodSecurity // required to activate @PreAuthorize/@PostAuthorize public class MethodSecurityConfig { }
@PreAuthorize("hasRole('ADMIN') or @bookSecurity.isOwner(#id, authentication.name)") public void deleteBook(Long id) { bookRepository.deleteById(id); }
@Component("bookSecurity") public class BookSecurity { public boolean isOwner(Long bookId, String username) { return bookRepository.findById(bookId) .map(b -> b.getCreatedBy().equals(username)) .orElse(false); } }
◆ Under the hood
@PreAuthorize expressions can reference any Spring bean by name (via @bookSecurity above) — this is what lets authorization logic grow beyond simple role checks into genuinely custom business rules, like "an ADMIN can always act, or the original creator can act on their own resource," expressed declaratively rather than as imperative if-checks scattered through the method body.
✓ Quick recap
What is.hasRole("ADMIN") actually shorthand for? Checking for the authority "ROLE_ADMIN" — Spring adds the ROLE_ prefix automatically. Why does the order of authorizeHttpRequests matchers matter? Rules are evaluated in declaration order and the first match wins — a broad rule placed too early can shadow a more specific one.
💻 Code example
@PreAuthorize("hasRole('ADMIN') or @bookSecurity.isOwner(#id, authentication.name)") public void deleteBook(Long id) { bookRepository.deleteById(id); }
Want a visual for this concept?
Generate a diagram tailored to “Authorization & Role-Based Access Control” — the AI picks whichever visual (flowchart, comparison, sequence, etc.) best fits.
Sign in to generate a visual →