docs
Spring Security provides powerful expression-based access control using Spring Expression Language (SpEL). This enables fine-grained authorization decisions using dynamic expressions evaluated at runtime.
Core Capabilities:
SecurityExpressionHandler<T> creates and evaluates security expressionsSecurityExpressionOperations provides methods available in expressions (hasRole, hasAuthority, etc.)MethodSecurityExpressionHandler with parameter accessPermissionEvaluator for domain object permissionsauthentication, principal, filterObject, returnObjectKey Interfaces and Classes:
SecurityExpressionHandler<T> - Interface: getExpressionParser(), createEvaluationContext()AbstractSecurityExpressionHandler<T> - Base implementation with configurable componentsSecurityExpressionOperations - Interface defining expression methods (hasRole, hasAuthority, etc.)SecurityExpressionRoot - Base implementation of expression operationsMethodSecurityExpressionHandler - Specialized for method security (parameter access)DefaultMethodSecurityExpressionHandler - Default implementation for method securityMethodSecurityExpressionOperations - Extended operations for method securityPermissionEvaluator - Interface for domain object permission checksDefault Behaviors:
SpelExpressionParser by defaultsetDefaultRolePrefix())DenyAllPermissionEvaluator by default (denies all)#paramName syntax (set via setParameterNameDiscoverer())Threading Model:
Lifecycle:
Exceptions:
SpelEvaluationException (handle gracefully)Edge Cases:
@PostAuthorize expressions must handle null returnObjectDefaultParameterNameDiscoverer)MethodSecurityExpressionRoot to add custom expression methodsBase interface for creating and evaluating security expressions.
package org.springframework.security.access.expression;
interface SecurityExpressionHandler<T> extends AopInfrastructureBean {
ExpressionParser getExpressionParser();
EvaluationContext createEvaluationContext(
Authentication authentication,
T invocation);
}Key Methods:
ExpressionParser getExpressionParser()EvaluationContext createEvaluationContext(
Authentication authentication,
T invocation)authentication - The current authenticationinvocation - The secured objectAbstract base implementation providing common functionality.
package org.springframework.security.access.expression;
abstract class AbstractSecurityExpressionHandler<T>
implements SecurityExpressionHandler<T> {
AbstractSecurityExpressionHandler();
void setPermissionEvaluator(PermissionEvaluator permissionEvaluator);
void setRoleHierarchy(RoleHierarchy roleHierarchy);
void setDefaultRolePrefix(String defaultRolePrefix);
ExpressionParser getExpressionParser();
}Key Methods:
void setPermissionEvaluator(PermissionEvaluator permissionEvaluator)void setRoleHierarchy(RoleHierarchy roleHierarchy)void setDefaultRolePrefix(String defaultRolePrefix)Example:
@Bean
public DefaultMethodSecurityExpressionHandler expressionHandler() {
DefaultMethodSecurityExpressionHandler handler =
new DefaultMethodSecurityExpressionHandler();
handler.setPermissionEvaluator(customPermissionEvaluator());
handler.setRoleHierarchy(roleHierarchy());
handler.setDefaultRolePrefix("ROLE_");
return handler;
}Interface defining available operations in security expressions.
package org.springframework.security.access.expression;
interface SecurityExpressionOperations {
Authentication getAuthentication();
Object getPrincipal();
boolean hasAuthority(String authority);
boolean hasAnyAuthority(String... authorities);
boolean hasRole(String role);
boolean hasAnyRole(String... roles);
boolean isAnonymous();
boolean isAuthenticated();
boolean isRememberMe();
boolean isFullyAuthenticated();
boolean permitAll();
boolean denyAll();
boolean hasPermission(Object target, Object permission);
boolean hasPermission(Object targetId, String targetType, Object permission);
Object getFilterObject();
Object getReturnObject();
Object getThis();
}Authentication Access:
Authentication getAuthentication()Object getPrincipal()Authority Checks:
boolean hasAuthority(String authority)boolean hasAnyAuthority(String... authorities)boolean hasRole(String role)boolean hasAnyRole(String... roles)Authentication State:
boolean isAnonymous()boolean isAuthenticated()boolean isRememberMe()boolean isFullyAuthenticated()Universal Access:
boolean permitAll()boolean denyAll()Permission Checks:
boolean hasPermission(Object target, Object permission)boolean hasPermission(Object targetId, String targetType, Object permission)Context Objects:
Object getFilterObject()Object getReturnObject()Object getThis()Abstract base implementation of SecurityExpressionOperations.
package org.springframework.security.access.expression;
abstract class SecurityExpressionRoot implements SecurityExpressionOperations {
SecurityExpressionRoot(Authentication authentication);
}Constructor:
SecurityExpressionRoot(Authentication authentication)Expression handler specialized for method security.
package org.springframework.security.access.expression.method;
interface MethodSecurityExpressionHandler
extends SecurityExpressionHandler<MethodInvocation> {
Object filter(
Object filterTarget,
Expression filterExpression,
EvaluationContext ctx);
void setReturnObject(Object returnObject, EvaluationContext ctx);
}Key Methods:
Object filter(
Object filterTarget,
Expression filterExpression,
EvaluationContext ctx)void setReturnObject(Object returnObject, EvaluationContext ctx)Default implementation for method security expressions.
package org.springframework.security.access.expression.method;
class DefaultMethodSecurityExpressionHandler
extends AbstractSecurityExpressionHandler<MethodInvocation>
implements MethodSecurityExpressionHandler {
DefaultMethodSecurityExpressionHandler();
void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer);
void setTrustResolver(AuthenticationTrustResolver trustResolver);
EvaluationContext createEvaluationContext(
Authentication auth,
MethodInvocation mi);
}Key Methods:
void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer)void setTrustResolver(AuthenticationTrustResolver trustResolver)Example:
@Configuration
@EnableMethodSecurity
public class MethodSecurityConfig {
@Bean
public MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
DefaultMethodSecurityExpressionHandler handler =
new DefaultMethodSecurityExpressionHandler();
handler.setPermissionEvaluator(permissionEvaluator());
handler.setRoleHierarchy(roleHierarchy());
handler.setDefaultRolePrefix("ROLE_");
// Enable parameter name discovery
handler.setParameterNameDiscoverer(
new DefaultParameterNameDiscoverer()
);
return handler;
}
@Bean
public PermissionEvaluator permissionEvaluator() {
return new CustomPermissionEvaluator();
}
@Bean
public RoleHierarchy roleHierarchy() {
return RoleHierarchyImpl.withDefaultRolePrefix()
.role("ADMIN").implies("USER")
.role("USER").implies("GUEST")
.build();
}
}Extended operations interface for method security.
package org.springframework.security.access.expression.method;
interface MethodSecurityExpressionOperations
extends SecurityExpressionOperations {
}Root object for method security expressions.
package org.springframework.security.access.expression.method;
class MethodSecurityExpressionRoot extends SecurityExpressionRoot
implements MethodSecurityExpressionOperations {
MethodSecurityExpressionRoot(
Authentication authentication,
MethodInvocation methodInvocation);
}Constructor:
MethodSecurityExpressionRoot(
Authentication authentication,
MethodInvocation methodInvocation)Evaluates expression before method invocation.
@PreAuthorize("hasRole('ADMIN')")
public void adminOnly() {
// Only users with ROLE_ADMIN can execute
}
@PreAuthorize("hasAuthority('READ_PRIVILEGE')")
public Document readDocument(Long id) {
return documentRepository.findById(id);
}
@PreAuthorize("isAuthenticated()")
public void authenticatedOnly() {
// Any authenticated user can execute
}
@PreAuthorize("#username == authentication.name")
public User getUser(String username) {
// Users can only access their own data
return userRepository.findByUsername(username);
}
@PreAuthorize("hasPermission(#document, 'read')")
public void processDocument(Document document) {
// Check permission on domain object
}Evaluates expression after method invocation with access to return value.
@PostAuthorize("returnObject.owner == authentication.name")
public Document getDocument(Long id) {
// Method executes, then checks if returned document
// belongs to current user
return documentRepository.findById(id);
}
@PostAuthorize("hasPermission(returnObject, 'read')")
public Project getProject(Long id) {
Project project = projectRepository.findById(id);
// Checks permission on returned project
return project;
}Filters method parameter collection/array before invocation.
@PreFilter("hasPermission(filterObject, 'delete')")
public void deleteDocuments(List<Document> documents) {
// Only documents user has 'delete' permission on are passed
documentRepository.deleteAll(documents);
}
@PreFilter(value = "filterObject.owner == authentication.name",
filterTarget = "documents")
public void processDocuments(List<Document> documents, String action) {
// Filter specific parameter when multiple collections
}Filters method return collection/array after invocation.
@PostFilter("hasPermission(filterObject, 'read')")
public List<Document> getAllDocuments() {
List<Document> all = documentRepository.findAll();
// Returns only documents user can read
return all;
}
@PostFilter("filterObject.owner == authentication.name")
public List<Project> getProjects() {
// Returns only projects owned by current user
return projectRepository.findAll();
}@PreAuthorize("hasRole('ADMIN')")
public void adminFunction() { }
@PreAuthorize("hasAnyRole('ADMIN', 'MANAGER')")
public void managerOrAdminFunction() { }
// With role hierarchy configured
@PreAuthorize("hasRole('USER')") // Admins also have access if hierarchy configured
public void userFunction() { }@PreAuthorize("hasAuthority('WRITE_PRIVILEGE')")
public void writeData() { }
@PreAuthorize("hasAnyAuthority('READ_PRIVILEGE', 'WRITE_PRIVILEGE')")
public void accessData() { }@PreAuthorize("isAuthenticated()")
public void authenticatedUsers() { }
@PreAuthorize("isAnonymous()")
public void anonymousOnly() { }
@PreAuthorize("isFullyAuthenticated()") // Not remember-me
public void sensitiveOperation() { }
@PreAuthorize("permitAll()")
public void publicAccess() { }
@PreAuthorize("denyAll()")
public void neverAccessible() { }// Access method parameter by name
@PreAuthorize("#username == authentication.name")
public void updateUser(String username, UserDto updates) { }
// Access parameter properties
@PreAuthorize("#user.id == authentication.principal.id")
public void updateUser(@P("user") User user) { }
// Complex expressions with parameters
@PreAuthorize("#id == authentication.principal.id or hasRole('ADMIN')")
public void deleteUser(Long id) { }// Check return value property
@PostAuthorize("returnObject.createdBy == authentication.name")
public Report getReport(Long id) { }
// Check multiple conditions on return value
@PostAuthorize("returnObject != null and returnObject.status == 'PUBLISHED'")
public Article getArticle(Long id) { }// Permission on parameter
@PreAuthorize("hasPermission(#document, 'write')")
public void updateDocument(Document document) { }
// Permission by ID and type
@PreAuthorize("hasPermission(#id, 'com.example.Document', 'delete')")
public void deleteDocument(Long id) { }
// Permission on return value
@PostAuthorize("hasPermission(returnObject, 'read')")
public Document getDocument(Long id) { }// AND conditions
@PreAuthorize("hasRole('ADMIN') and #user.department == authentication.principal.department")
public void updateUser(User user) { }
// OR conditions
@PreAuthorize("#userId == authentication.principal.id or hasRole('ADMIN')")
public void viewProfile(Long userId) { }
// Complex conditions
@PreAuthorize("(hasRole('MANAGER') and #report.department == authentication.principal.department) " +
"or hasRole('ADMIN')")
public void approveReport(Report report) { }Extend MethodSecurityExpressionRoot to add custom methods:
public class CustomMethodSecurityExpressionRoot
extends MethodSecurityExpressionRoot {
public CustomMethodSecurityExpressionRoot(
Authentication authentication, MethodInvocation methodInvocation) {
super(authentication, methodInvocation);
}
// Custom expression method
public boolean isOwner(Object object) {
if (object instanceof Ownable) {
Ownable ownable = (Ownable) object;
String currentUser = getAuthentication().getName();
return ownable.getOwner().equals(currentUser);
}
return false;
}
// Custom method with additional logic
public boolean isSameDepartment(Long userId) {
User currentUser = (User) getAuthentication().getPrincipal();
UserService userService = getBean(UserService.class);
User targetUser = userService.findById(userId);
return currentUser.getDepartment()
.equals(targetUser.getDepartment());
}
}
// Custom expression handler
public class CustomMethodSecurityExpressionHandler
extends DefaultMethodSecurityExpressionHandler {
private ApplicationContext applicationContext;
@Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(
Authentication authentication, MethodInvocation invocation) {
CustomMethodSecurityExpressionRoot root =
new CustomMethodSecurityExpressionRoot(authentication, invocation);
root.setApplicationContext(applicationContext);
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(getTrustResolver());
root.setRoleHierarchy(getRoleHierarchy());
return root;
}
public void setApplicationContext(ApplicationContext context) {
this.applicationContext = context;
}
}
// Configuration
@Bean
public MethodSecurityExpressionHandler expressionHandler(
ApplicationContext context) {
CustomMethodSecurityExpressionHandler handler =
new CustomMethodSecurityExpressionHandler();
handler.setApplicationContext(context);
return handler;
}
// Usage
@PreAuthorize("isOwner(#document)")
public void updateDocument(Document document) { }
@PreAuthorize("isSameDepartment(#userId)")
public void shareData(Long userId) { }Interface for evaluating permissions on domain objects.
package org.springframework.security.access;
interface PermissionEvaluator extends AopInfrastructureBean {
boolean hasPermission(
Authentication authentication,
Object targetDomainObject,
Object permission);
boolean hasPermission(
Authentication authentication,
Serializable targetId,
String targetType,
Object permission);
}Methods:
boolean hasPermission(
Authentication authentication,
Object targetDomainObject,
Object permission)boolean hasPermission(
Authentication authentication,
Serializable targetId,
String targetType,
Object permission)Example:
public class CustomPermissionEvaluator implements PermissionEvaluator {
private final PermissionRepository permissionRepository;
@Override
public boolean hasPermission(Authentication authentication,
Object targetDomainObject,
Object permission) {
if (authentication == null || targetDomainObject == null) {
return false;
}
String username = authentication.getName();
String permissionName = permission.toString();
// Check permission based on domain object
if (targetDomainObject instanceof Document) {
Document doc = (Document) targetDomainObject;
return checkDocumentPermission(username, doc, permissionName);
}
return false;
}
@Override
public boolean hasPermission(Authentication authentication,
Serializable targetId,
String targetType,
Object permission) {
if (authentication == null || targetId == null) {
return false;
}
String username = authentication.getName();
return permissionRepository.hasPermission(
username, targetId, targetType, permission.toString()
);
}
private boolean checkDocumentPermission(
String username, Document doc, String permission) {
// Custom permission logic
if ("read".equals(permission)) {
return doc.isPublic() || doc.getOwner().equals(username) ||
doc.getReaders().contains(username);
}
if ("write".equals(permission)) {
return doc.getOwner().equals(username) ||
doc.getEditors().contains(username);
}
return false;
}
}Permission evaluator that denies all permissions.
package org.springframework.security.access.expression;
class DenyAllPermissionEvaluator implements PermissionEvaluator {
DenyAllPermissionEvaluator();
}Description: Default implementation that always returns false.
Utility class for expression evaluation.
package org.springframework.security.access.expression;
class ExpressionUtils {
static boolean evaluateAsBoolean(
Expression expr,
EvaluationContext ctx);
}Static Methods:
static boolean evaluateAsBoolean(
Expression expr,
EvaluationContext ctx)org.springframework.security.access.expressionorg.springframework.security.access.expression.methodorg.springframework.security.access