CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-springframework-security--spring-security-acl

Spring Security ACL provides instance-based security for domain objects through a comprehensive Access Control List implementation

Pending
Overview
Eval results
Files

caching-performance.mddocs/

Caching & Performance Optimization

Spring Security ACL includes sophisticated caching and performance optimization features to ensure efficient permission checking even with complex ACL hierarchies and large datasets.

Overview

ACL performance optimization includes:

  • AclCache - Core caching interface for ACL data
  • SpringCacheBasedAclCache - Integration with Spring's caching framework
  • AclPermissionCacheOptimizer - Batch loading for collection filtering
  • Lookup Strategy Optimization - SQL query customization
  • SID Filtering - Reduce data transfer by loading only relevant entries

AclCache Interface

The AclCache interface provides caching abstraction for ACL data:

package org.springframework.security.acls.model;

public interface AclCache {
    
    // Clear entire cache
    void clearCache();
    
    // Remove specific ACL from cache
    void evictFromCache(ObjectIdentity objectIdentity);
    
    // Remove ACL for specific SIDs from cache
    void evictFromCache(Serializable pk);
    
    // Retrieve ACL from cache
    MutableAcl getFromCache(ObjectIdentity objectIdentity);
    
    // Retrieve ACL by primary key from cache
    MutableAcl getFromCache(Serializable pk);
    
    // Store ACL in cache
    void putInCache(MutableAcl acl);
}

SpringCacheBasedAclCache

Production-ready cache implementation using Spring's caching framework:

package org.springframework.security.acls.domain;

public class SpringCacheBasedAclCache implements AclCache {
    
    public SpringCacheBasedAclCache(
        Cache cache,
        PermissionGrantingStrategy permissionGrantingStrategy,
        AclAuthorizationStrategy aclAuthorizationStrategy
    );
    
    // Cache configuration methods
    public void clearCache();
    public void evictFromCache(ObjectIdentity objectIdentity);
    public void evictFromCache(Serializable pk);
    public MutableAcl getFromCache(ObjectIdentity objectIdentity);
    public MutableAcl getFromCache(Serializable pk);
    public void putInCache(MutableAcl acl);
}

Basic Cache Configuration

@Configuration
@EnableCaching
public class AclCacheConfig {
    
    @Bean
    public AclCache aclCache() {
        return new SpringCacheBasedAclCache(
            cacheManager().getCache("aclCache"),
            permissionGrantingStrategy(),
            aclAuthorizationStrategy()
        );
    }
    
    @Bean
    public CacheManager cacheManager() {
        ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager("aclCache");
        return cacheManager;
    }
    
    @Bean
    public PermissionGrantingStrategy permissionGrantingStrategy() {
        return new DefaultPermissionGrantingStrategy(auditLogger());
    }
    
    @Bean
    public AclAuthorizationStrategy aclAuthorizationStrategy() {
        return new AclAuthorizationStrategyImpl(
            new SimpleGrantedAuthority("ROLE_ADMIN")
        );
    }
}

Advanced Cache Configuration

@Configuration
@EnableCaching
public class AdvancedAclCacheConfig {
    
    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager("aclCache");
        cacheManager.setCaffeine(Caffeine.newBuilder()
            .maximumSize(10000)                    // Limit cache size
            .expireAfterWrite(Duration.ofMinutes(30))  // TTL for entries
            .expireAfterAccess(Duration.ofMinutes(10)) // Idle timeout
            .recordStats()                         // Enable metrics
            .removalListener((key, value, cause) -> {
                log.debug("Evicted ACL cache entry: key={}, cause={}", key, cause);
            }));
        return cacheManager;
    }
    
    @Bean
    public AclCache aclCache() {
        SpringCacheBasedAclCache cache = new SpringCacheBasedAclCache(
            cacheManager().getCache("aclCache"),
            permissionGrantingStrategy(),
            aclAuthorizationStrategy()
        );
        return cache;
    }
}

AclPermissionCacheOptimizer

Optimizes permission checking for collections by batching ACL lookups:

package org.springframework.security.acls;

public class AclPermissionCacheOptimizer implements PermissionCacheOptimizer {
    
    private final AclService aclService;
    private final ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy;
    private final SidRetrievalStrategy sidRetrievalStrategy;
    
    public AclPermissionCacheOptimizer(AclService aclService);
    
    // Batch load ACLs for a collection of objects
    @Override
    public void cachePermissionsFor(Authentication authentication, 
                                   Collection<?> objects);
}

Using AclPermissionCacheOptimizer

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig {
    
    @Bean
    public PermissionEvaluator permissionEvaluator(AclService aclService) {
        return new AclPermissionEvaluator(aclService);
    }
    
    @Bean
    public PermissionCacheOptimizer permissionCacheOptimizer(AclService aclService) {
        return new AclPermissionCacheOptimizer(aclService);
    }
}

// Usage in service methods
@Service
public class DocumentService {
    
    @Autowired
    private PermissionCacheOptimizer cacheOptimizer;
    
    @PostFilter("hasPermission(filterObject, 'READ')")
    public List<Document> getAllDocuments(Authentication auth) {
        List<Document> documents = documentRepository.findAll();
        
        // Pre-load permissions for all documents in a single batch query
        cacheOptimizer.cachePermissionsFor(auth, documents);
        
        return documents; // PostFilter will use cached data
    }
}

Performance Optimization Strategies

1. SID Filtering

Always provide SID lists to reduce data transfer:

@Service
public class OptimizedSecurityService {
    
    @Autowired
    private AclService aclService;
    
    @Autowired 
    private SidRetrievalStrategy sidRetrievalStrategy;
    
    public boolean hasPermission(Object domainObject, Permission permission, Authentication auth) {
        ObjectIdentity identity = new ObjectIdentityImpl(domainObject);
        List<Sid> sids = sidRetrievalStrategy.getSids(auth);
        
        try {
            // Only load ACL entries for current user's SIDs
            Acl acl = aclService.readAclById(identity, sids);
            return acl.isGranted(Arrays.asList(permission), sids, false);
        } catch (NotFoundException e) {
            return false;
        }
    }
}

2. Batch Loading

Process multiple objects efficiently:

public Map<Document, Boolean> checkBulkPermissions(List<Document> documents, Authentication auth) {
    List<ObjectIdentity> identities = documents.stream()
        .map(doc -> new ObjectIdentityImpl(Document.class, doc.getId()))
        .collect(Collectors.toList());
    
    List<Sid> sids = sidRetrievalStrategy.getSids(auth);
    List<Permission> readPermission = Arrays.asList(BasePermission.READ);
    
    try {
        // Single batch query for all ACLs
        Map<ObjectIdentity, Acl> acls = aclService.readAclsById(identities, sids);
        
        return documents.stream()
            .collect(Collectors.toMap(
                Function.identity(),
                doc -> {
                    ObjectIdentity identity = new ObjectIdentityImpl(Document.class, doc.getId());
                    Acl acl = acls.get(identity);
                    return acl != null && acl.isGranted(readPermission, sids, false);
                }
            ));
    } catch (NotFoundException e) {
        return documents.stream()
            .collect(Collectors.toMap(Function.identity(), doc -> false));
    }
}

3. Custom Lookup Strategy

Optimize SQL queries for your specific database:

@Bean
public LookupStrategy lookupStrategy() {
    BasicLookupStrategy strategy = new BasicLookupStrategy(
        dataSource(),
        aclCache(),
        aclAuthorizationStrategy(),
        permissionGrantingStrategy()
    );
    
    // Customize SQL for better performance
    strategy.setSelectClause(
        "SELECT obj.object_id_identity, " +
        "class.class, " +
        "sid.sid, sid.principal, " +
        "acl.entries_inheriting, acl.id as acl_id, " +
        "acl.parent_object, acl.owner_sid, " +
        "entry.id, entry.mask, entry.granting, " +
        "entry.audit_success, entry.audit_failure "
    );
    
    // Add database-specific optimizations
    strategy.setLookupObjectIdentitiesWhereClause("(obj.object_id_identity = ? and class.class = ?)");
    
    return strategy;
}

4. Connection Pool Optimization

Configure database connections for ACL workloads:

@Configuration
public class DataSourceConfig {
    
    @Bean
    public DataSource dataSource() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:postgresql://localhost/acl_db");
        config.setUsername("acluser");
        config.setPassword("password");
        
        // Optimize for ACL read-heavy workloads
        config.setMaximumPoolSize(25);
        config.setMinimumIdle(10);
        config.setConnectionTimeout(20000);
        config.setIdleTimeout(300000);
        config.setMaxLifetime(1200000);
        
        // Optimize for batch queries
        config.addDataSourceProperty("cachePrepStmts", "true");
        config.addDataSourceProperty("prepStmtCacheSize", "250");
        config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
        config.addDataSourceProperty("useServerPrepStmts", "true");
        
        return new HikariDataSource(config);
    }
}

Monitoring and Metrics

Cache Metrics

Monitor cache performance:

@Component
public class AclCacheMetrics {
    
    private final CacheManager cacheManager;
    private final MeterRegistry meterRegistry;
    
    @EventListener
    @Async
    public void handleCacheEvictEvent(CacheEvictEvent event) {
        meterRegistry.counter("acl.cache.evictions", 
            "cache", event.getCacheName()).increment();
    }
    
    @Scheduled(fixedRate = 60000) // Every minute
    public void recordCacheStats() {
        Cache aclCache = cacheManager.getCache("aclCache");
        if (aclCache instanceof CaffeineCache) {
            com.github.benmanes.caffeine.cache.Cache<Object, Object> nativeCache = 
                ((CaffeineCache) aclCache).getNativeCache();
            
            CacheStats stats = nativeCache.stats();
            
            meterRegistry.gauge("acl.cache.size", nativeCache.estimatedSize());
            meterRegistry.gauge("acl.cache.hit.rate", stats.hitRate());
            meterRegistry.gauge("acl.cache.miss.rate", stats.missRate());
            meterRegistry.gauge("acl.cache.eviction.count", stats.evictionCount());
        }
    }
}

Performance Metrics

Track ACL operation performance:

@Component
public class AclPerformanceMonitor {
    
    private final MeterRegistry meterRegistry;
    
    @EventListener
    public void handleAclServiceCall(AclServiceCallEvent event) {
        Timer.Sample sample = Timer.start(meterRegistry);
        sample.stop(Timer.builder("acl.service.call")
            .tag("method", event.getMethodName())
            .tag("object.count", String.valueOf(event.getObjectCount()))
            .register(meterRegistry));
    }
}

Best Practices

1. Cache Warming

Pre-populate cache with frequently accessed ACLs:

@Component
public class AclCacheWarmer {
    
    @Autowired
    private AclService aclService;
    
    @EventListener(ApplicationReadyEvent.class)
    public void warmUpCache() {
        // Load frequently accessed ACLs on startup
        List<ObjectIdentity> frequentlyAccessed = getFrequentlyAccessedObjects();
        
        try {
            aclService.readAclsById(frequentlyAccessed);
        } catch (NotFoundException e) {
            log.warn("Some ACLs not found during cache warming", e);
        }
    }
}

2. Cache Invalidation Strategy

Invalidate cache appropriately when ACLs change:

@Service
public class CacheAwareMutableAclService {
    
    @Autowired
    private MutableAclService mutableAclService;
    
    @Autowired
    private AclCache aclCache;
    
    public MutableAcl updateAcl(MutableAcl acl) {
        MutableAcl updated = mutableAclService.updateAcl(acl);
        
        // Invalidate cache entry
        aclCache.putInCache(updated);
        
        // Also invalidate parent ACLs if inheritance is involved
        if (updated.getParentAcl() != null) {
            aclCache.evictFromCache(updated.getParentAcl().getObjectIdentity());
        }
        
        return updated;
    }
}

3. Memory Management

Monitor and manage cache memory usage:

@Configuration
public class AclCacheMemoryConfig {
    
    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager manager = new CaffeineCacheManager("aclCache");
        manager.setCaffeine(Caffeine.newBuilder()
            .maximumWeight(100_000_000) // 100MB
            .weigher((key, value) -> {
                // Custom weigher for ACL objects
                if (value instanceof AclImpl) {
                    AclImpl acl = (AclImpl) value;
                    return 1000 + (acl.getEntries().size() * 100);
                }
                return 1000;
            })
            .recordStats());
        return manager;
    }
}

Proper caching and performance optimization is essential for production ACL deployments. The next step is understanding configuration and setup to integrate these performance features into your application.

Install with Tessl CLI

npx tessl i tessl/maven-org-springframework-security--spring-security-acl

docs

acl-services.md

caching-performance.md

configuration.md

domain-model.md

index.md

permission-evaluation.md

strategy-interfaces.md

tile.json