On this page
Authorization
Authorization determines what an authenticated user is allowed to do. Spring Security supports URL-based, method-level, and domain-object security.
URL-Based Authorization
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/api/public/**").permitAll()
.requestMatchers(HttpMethod.GET, "/api/products/**").hasAnyRole("USER", "ADMIN")
.requestMatchers(HttpMethod.POST, "/api/products/**").hasRole("ADMIN")
.anyRequest().authenticated()
);
Role names are prefixed with ROLE_ internally: hasRole("ADMIN") checks ROLE_ADMIN.
Method Security
@EnableMethodSecurity
@Configuration
public class MethodSecurityConfig { }
@Service
public class OrderService {
@PreAuthorize("hasRole('ADMIN') or #userId == authentication.principal.id")
public Order getOrder(Long orderId, Long userId) {
return orderRepository.findById(orderId).orElseThrow();
}
@PreAuthorize("hasAuthority('order:delete')")
public void deleteOrder(Long orderId) { orderRepository.deleteById(orderId); }
@PostAuthorize("returnObject.owner == authentication.name")
public Document getDocument(Long id) { return documentRepository.findById(id).orElseThrow(); }
@Secured({"ROLE_ADMIN", "ROLE_MANAGER"})
public void approveExpense(Long id) { /* ... */ }
}
Custom Permission Evaluator
@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
@Override
public boolean hasPermission(Authentication auth, Object target, Object permission) {
if (auth == null || target == null) return false;
Order order = (Order) target;
return order.getUserId().equals(getCurrentUserId(auth))
|| auth.getAuthorities().stream()
.anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));
}
@Override
public boolean hasPermission(Authentication auth, Serializable targetId,
String targetType, Object permission) {
return false;
}
}
Access Decision Managers
| Voter | Purpose |
|---|---|
RoleVoter |
Checks role-based access |
AuthenticatedVoter |
Checks IS_AUTHENTICATED_* |
AffirmativeBased |
Grants if any voter approves (default) |
UnanimousBased |
Grants only if all voters approve |
Domain Object Security (ACL)
For row-level security:
@PreAuthorize("hasPermission(#id, 'com.example.Order', 'read')")
public Order findById(Long id) { return orderRepository.findById(id).orElseThrow(); }
Requires Spring Security ACL module and database tables for ACL entries.
SpEL Expressions
Common expressions in @PreAuthorize:
@PreAuthorize("hasRole('ADMIN')")
@PreAuthorize("hasAuthority('user:write')")
@PreAuthorize("authentication.name == #username")
@PreAuthorize("@authService.canAccess(#orderId, authentication)")
@PreAuthorize("hasRole('ADMIN') or #entity.owner == authentication.name")
Best Practices
- Apply coarse-grained rules at URL level; fine-grained at method level
- Use
@PreAuthorizeover@Securedfor SpEL flexibility - Prefer permission strings (
order:write) over roles for fine-grained control - Test authorization rules with
@WithMockUserand@WithUserDetails - Never rely solely on UI hiding — always enforce on the server