Spring Security ACL provides instance-based security for domain objects through a comprehensive Access Control List implementation
—
Spring Security ACL's domain model provides a clean abstraction for representing access control lists and their components. Understanding these core interfaces is essential for working with the ACL module.
The domain model consists of seven core interfaces that work together to represent permissions:
Acl - Contains access control entries for a domain object (read-only view)MutableAcl - Extends Acl with modification capabilitiesObjectIdentity - Identifies a specific domain object instanceSid - Represents a security identity (user or role)Permission - Defines the type of access being grantedAccessControlEntry - Individual permission assignment within an ACLAuditableAccessControlEntry - ACE with audit success/failure trackingThe Acl interface represents an access control list for a domain object:
package org.springframework.security.acls.model;
public interface Acl extends Serializable {
// Get all ACL entries (for administrative purposes)
List<AccessControlEntry> getEntries();
// Get the domain object this ACL protects
ObjectIdentity getObjectIdentity();
// Get the owner of this ACL
Sid getOwner();
// Get parent ACL for inheritance
Acl getParentAcl();
// Check if entries inherit from parent
boolean isEntriesInheriting();
// Main authorization method
boolean isGranted(List<Permission> permission, List<Sid> sids, boolean administrativeMode)
throws NotFoundException, UnloadedSidException;
// Check if specific SIDs are loaded
boolean isSidLoaded(List<Sid> sids);
}Key Points:
isGranted() is the primary method for authorization decisionsThe MutableAcl interface extends Acl to provide modification capabilities:
package org.springframework.security.acls.model;
public interface MutableAcl extends Acl {
// Get the ACL identifier
Serializable getId();
// Insert new ACE at specific index
void insertAce(int atIndexLocation, Permission permission, Sid sid, boolean granting)
throws NotFoundException;
// Update permission for existing ACE
void updateAce(int aceIndex, Permission permission) throws NotFoundException;
// Remove ACE at specific index
void deleteAce(int aceIndex) throws NotFoundException;
// Change ACL ownership
void setOwner(Sid newOwner);
// Set parent ACL for inheritance
void setParent(Acl newParent);
// Control inheritance behavior
void setEntriesInheriting(boolean entriesInheriting);
}Key Points:
Represents the identity of a domain object instance:
package org.springframework.security.acls.model;
public interface ObjectIdentity extends Serializable {
// Get the object's unique identifier
Serializable getIdentifier();
// Get the object's type/class name
String getType();
// Standard equality methods
boolean equals(Object obj);
int hashCode();
}Implementation Example:
// Using ObjectIdentityImpl for different scenarios
ObjectIdentity docId = new ObjectIdentityImpl(Document.class, 123L);
ObjectIdentity customId = new ObjectIdentityImpl("com.example.Document", "DOC-456");
ObjectIdentity fromObject = new ObjectIdentityImpl(documentInstance);Represents a security identity - either a principal (user) or granted authority (role):
package org.springframework.security.acls.model;
public interface Sid extends Serializable {
boolean equals(Object obj);
int hashCode();
}Built-in Implementations:
// Principal-based SID (represents a user)
public class PrincipalSid implements Sid {
public PrincipalSid(String principal);
public PrincipalSid(Authentication authentication);
public String getPrincipal();
}
// Authority-based SID (represents a role/group)
public class GrantedAuthoritySid implements Sid {
public GrantedAuthoritySid(String grantedAuthority);
public GrantedAuthoritySid(GrantedAuthority grantedAuthority);
public String getGrantedAuthority();
}Usage Examples:
// Create SIDs for different scenarios
Sid userSid = new PrincipalSid("john.doe");
Sid adminSid = new GrantedAuthoritySid("ROLE_ADMIN");
Sid managerSid = new GrantedAuthoritySid("ROLE_DOCUMENT_MANAGER");
// From Spring Security Authentication
Sid currentUser = new PrincipalSid(authentication);
List<Sid> userAuthorities = authentication.getAuthorities()
.stream()
.map(GrantedAuthoritySid::new)
.collect(Collectors.toList());Represents a permission that can be granted or denied:
package org.springframework.security.acls.model;
public interface Permission extends Serializable {
// Get the permission bitmask
int getMask();
// Get human-readable pattern representation
String getPattern();
// Constants for pattern display
char RESERVED_ON = '~';
char RESERVED_OFF = '.';
String THIRTY_TWO_RESERVED_OFF = "................................";
}Built-in Permissions:
public class BasePermission extends AbstractPermission {
public static final Permission READ = new BasePermission(1 << 0, 'R'); // 1
public static final Permission WRITE = new BasePermission(1 << 1, 'W'); // 2
public static final Permission CREATE = new BasePermission(1 << 2, 'C'); // 4
public static final Permission DELETE = new BasePermission(1 << 3, 'D'); // 8
public static final Permission ADMINISTRATION = new BasePermission(1 << 4, 'A'); // 16
protected BasePermission(int mask);
protected BasePermission(int mask, char code);
}Custom Permissions:
public class CustomPermission extends AbstractPermission {
public static final Permission APPROVE = new CustomPermission(1 << 5, 'P');
public static final Permission PUBLISH = new CustomPermission(1 << 6, 'U');
public CustomPermission(int mask, char code) {
super(mask, code);
}
}Represents an individual permission assignment within an ACL:
package org.springframework.security.acls.model;
public interface AccessControlEntry extends Serializable {
// Get the ACL this entry belongs to
Acl getAcl();
// Get unique identifier for this entry
Serializable getId();
// Get the permission being granted/denied
Permission getPermission();
// Get the security identity
Sid getSid();
// Check if this is a grant (true) or deny (false)
boolean isGranting();
}Extended Interface for Auditing:
public interface AuditableAccessControlEntry extends AccessControlEntry {
boolean isAuditFailure();
boolean isAuditSuccess();
}The ACL module provides additional specialized interfaces:
// For ownership operations
public interface OwnershipAcl extends MutableAcl {
void setOwner(Sid newOwner);
}
// For audit configuration
public interface AuditableAcl extends MutableAcl {
void updateAuditing(int aceIndex, boolean auditSuccess, boolean auditFailure);
}// An ACL contains multiple ACEs
Acl documentAcl = aclService.readAclById(objectIdentity);
List<AccessControlEntry> entries = documentAcl.getEntries();
for (AccessControlEntry ace : entries) {
Sid sid = ace.getSid();
Permission permission = ace.getPermission();
boolean isGrant = ace.isGranting();
System.out.printf("SID: %s, Permission: %s, Grant: %s%n",
sid, permission.getPattern(), isGrant);
}// 1. Get object identity
ObjectIdentity identity = new ObjectIdentityImpl(Document.class, documentId);
// 2. Get current user's SIDs
List<Sid> sids = Arrays.asList(
new PrincipalSid(authentication.getName()),
new GrantedAuthoritySid("ROLE_USER")
);
// 3. Define required permissions
List<Permission> permissions = Arrays.asList(BasePermission.READ);
// 4. Load ACL and check permissions
Acl acl = aclService.readAclById(identity, sids);
boolean granted = acl.isGranted(permissions, sids, false);// Parent-child relationship example
ObjectIdentity parentFolder = new ObjectIdentityImpl(Folder.class, parentId);
ObjectIdentity childDocument = new ObjectIdentityImpl(Document.class, documentId);
// Child ACL can inherit from parent
MutableAcl childAcl = aclService.createAcl(childDocument);
Acl parentAcl = aclService.readAclById(parentFolder);
childAcl.setParent(parentAcl);
childAcl.setEntriesInheriting(true); // Enable inheritance
aclService.updateAcl(childAcl);
// Permission check will consider both child and parent entries
boolean hasAccess = childAcl.isGranted(Arrays.asList(BasePermission.READ), sids, false);The ACL module provides several strategy interfaces for customization:
public interface ObjectIdentityRetrievalStrategy {
ObjectIdentity getObjectIdentity(Object domainObject);
}
// Default implementation
public class ObjectIdentityRetrievalStrategyImpl implements ObjectIdentityRetrievalStrategy {
public ObjectIdentity getObjectIdentity(Object domainObject) {
return new ObjectIdentityImpl(domainObject);
}
}public interface SidRetrievalStrategy {
List<Sid> getSids(Authentication authentication);
}
// Default implementation
public class SidRetrievalStrategyImpl implements SidRetrievalStrategy {
public List<Sid> getSids(Authentication authentication) {
List<Sid> sids = new ArrayList<>();
sids.add(new PrincipalSid(authentication));
for (GrantedAuthority authority : authentication.getAuthorities()) {
sids.add(new GrantedAuthoritySid(authority));
}
return sids;
}
}public interface PermissionFactory {
Permission buildFromMask(int mask);
Permission buildFromName(String name);
List<Permission> buildFromNames(List<String> names);
}
// Usage example
@Bean
public PermissionFactory permissionFactory() {
DefaultPermissionFactory factory = new DefaultPermissionFactory();
// Register custom permissions
Map<String, Permission> customPermissions = new HashMap<>();
customPermissions.put("APPROVE", CustomPermission.APPROVE);
customPermissions.put("PUBLISH", CustomPermission.PUBLISH);
factory.registerPublicPermissions(CustomPermission.class);
return factory;
}The ACL module defines several specific exceptions:
// Base exception for ACL operations
public abstract class AclDataAccessException extends DataAccessException {
protected AclDataAccessException(String msg);
protected AclDataAccessException(String msg, Throwable cause);
}
// Specific exceptions
public class NotFoundException extends AclDataAccessException;
public class AlreadyExistsException extends AclDataAccessException;
public class ChildrenExistException extends AclDataAccessException;
public class UnloadedSidException extends AclDataAccessException;Exception Handling Example:
try {
Acl acl = aclService.readAclById(objectIdentity);
return acl.isGranted(permissions, sids, false);
} catch (NotFoundException e) {
// No ACL exists - apply default policy
return false;
} catch (UnloadedSidException e) {
// Requested SID not loaded - reload with all SIDs
Acl fullAcl = aclService.readAclById(objectIdentity);
return fullAcl.isGranted(permissions, sids, false);
}// Use PrincipalSid for user-specific permissions
Sid userSid = new PrincipalSid("john.doe");
// Use GrantedAuthoritySid for role-based permissions
Sid roleSid = new GrantedAuthoritySid("ROLE_DOCUMENT_ADMIN");// Set up hierarchical permissions
MutableAcl folderAcl = aclService.createAcl(folderIdentity);
folderAcl.insertAce(0, BasePermission.READ, managerSid, true);
MutableAcl documentAcl = aclService.createAcl(documentIdentity);
documentAcl.setParent(folderAcl);
documentAcl.setEntriesInheriting(true);
// Documents inherit folder permissions automatically// Load ACLs with specific SIDs for better performance
List<Sid> relevantSids = sidRetrievalStrategy.getSids(authentication);
Acl acl = aclService.readAclById(objectIdentity, relevantSids);// Define business-specific permissions
public class DocumentPermission extends AbstractPermission {
public static final Permission APPROVE = new DocumentPermission(1 << 5, 'P');
public static final Permission PUBLISH = new DocumentPermission(1 << 6, 'U');
public static final Permission ARCHIVE = new DocumentPermission(1 << 7, 'H');
}The domain model provides a flexible foundation for implementing fine-grained access control. The next step is understanding how to configure and use ACL services to work with this domain model.
Install with Tessl CLI
npx tessl i tessl/maven-org-springframework-security--spring-security-acl