Introduction to Spring Security ACL – Spring Security ACL简介

最后修改: 2017年 12月 7日

1. Introduction


Access Control List (ACL) is a list of permissions attached to an object. An ACL specifies which identities are granted which operations on a given object.


Spring Security Access Control List is a Spring component which supports Domain Object Security. Simply put, Spring ACL helps in defining permissions for specific user/role on a single domain object – instead of across the board, at the typical per-operation level.

Spring Security Access Control List一个Spring组件,它支持域对象安全。简单地说,Spring ACL有助于为单个域对象上的特定用户/角色定义权限–而不是在典型的每个操作级别上全面地定义权限。

For example, a user with the role Admin can see (READ) and edit (WRITE) all messages on a Central Notice Box, but the normal user only can see messages, relate to them and cannot edit. Meanwhile, others user with the role Editor can see and edit some specific messages.


Hence, different user/role has different permission for each specific object. In this case, Spring ACL is capable of achieving the task. We’ll explore how to set up basic permission checking with Spring ACL in this article.

因此,不同的用户/角色对每个特定对象有不同的权限。在这种情况下,Spring ACL能够实现这一任务。我们将在本文中探讨如何用Spring ACL设置基本的权限检查。

2. Configuration


2.1. ACL Database


To use Spring Security ACL, we need to create four mandatory tables in our database.

为了使用Spring Security ACL,我们需要在数据库中创建四个强制性表。

The first table is ACL_CLASS, which store class name of the domain object, columns include:


  • ID
  • CLASS: the class name of secured domain objects, for example: com.baeldung.acl.persistence.entity.NoticeMessage

Secondly, we need the ACL_SID table which allows us to universally identify any principle or authority in the system. The table needs:


  • ID
  • SID: which is the username or role name. SID stands for Security Identity
  • PRINCIPAL: 0 or 1, to indicate that the corresponding SID is a principal (user, such as mary, mike, jack…) or an authority (role, such as ROLE_ADMIN, ROLE_USER, ROLE_EDITOR…)

Next table is ACL_OBJECT_IDENTITY, which stores information for each unique domain object:


  • ID
  • OBJECT_ID_CLASS: define the domain object class, links to ACL_CLASS table
  • OBJECT_ID_IDENTITY: domain objects can be stored in many tables depending on the class. Hence, this field store the target object primary key
  • PARENT_OBJECT: specify parent of this Object Identity within this table
  • OWNER_SID: ID of the object owner, links to ACL_SID table
  • ENTRIES_INHERITING: whether ACL Entries of this object inherits from the parent object (ACL Entries are defined in ACL_ENTRY table)

Finally, the ACL_ENTRY store individual permission assigns to each SID on an Object Identity:

最后,ACL_ENTRY存储个人权限分配给Object Identity上的每个SID

  • ID
  • ACL_OBJECT_IDENTITY: specify the object identity, links to ACL_OBJECT_IDENTITY table
  • ACE_ORDER: the order of current entry in the ACL entries list of corresponding Object Identity
  • SID: the target SID which the permission is granted to or denied from, links to ACL_SID table
  • MASK: the integer bit mask that represents the actual permission being granted or denied
  • GRANTING: value 1 means granting, value 0 means denying
  • AUDIT_SUCCESS and AUDIT_FAILURE: for auditing purpose

2.2. Dependency


To be able to use Spring ACL in our project, let’s first define our dependencies:

为了能够在我们的项目中使用Spring ACL ,让我们首先定义我们的依赖性。


Spring ACL requires a cache to store Object Identity and ACL entries, so we’ll make use of Ehcache here. And, to support Ehcache in Spring, we also need the spring-context-support.

Spring ACL需要一个缓存来存储Object IdentityACL条目,所以我们将在这里使用Ehcache。而且,为了在Spring中支持Ehcache我们还需要spring-context-support。

When not working with Spring Boot, we need to add versions explicitly. Those can be checked on Maven Central: spring-security-acl, spring-security-config, spring-context-support, ehcache-core.

在不使用Spring Boot时,我们需要明确添加版本。这些可以在Maven中心检查。spring-security-aclspring-security-configspring-context-supportehcache-core

2.3. ACL-Related Configuration


We need to secure all methods which return secured domain objects, or make changes to the object, by enabling Global Method Security:


@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class AclMethodSecurityConfiguration 
  extends GlobalMethodSecurityConfiguration {


    protected MethodSecurityExpressionHandler createExpressionHandler() {
        return defaultMethodSecurityExpressionHandler;

Let’s also enable Expression-Based Access Control by setting prePostEnabled to true to use Spring Expression Language (SpEL). Moreover, we need an expression handler with ACL support:

让我们也通过设置 prePostEnabled true来启用Spring Expression Language (SpEL)的基于表达式的访问控制此外我们需要一个支持ACL的表达式处理器。

public MethodSecurityExpressionHandler 
  defaultMethodSecurityExpressionHandler() {
    DefaultMethodSecurityExpressionHandler expressionHandler
      = new DefaultMethodSecurityExpressionHandler();
    AclPermissionEvaluator permissionEvaluator 
      = new AclPermissionEvaluator(aclService());
    return expressionHandler;

Hence, we assign AclPermissionEvaluator to the DefaultMethodSecurityExpressionHandler. The evaluator needs a MutableAclService to load permission settings and domain object’s definitions from the database.


For simplicity, we use the provided JdbcMutableAclService:


public JdbcMutableAclService aclService() { 
    return new JdbcMutableAclService(
      dataSource, lookupStrategy(), aclCache()); 

As its name, the JdbcMutableAclService uses JDBCTemplate to simplify database access. It needs a DataSource (for JDBCTemplate), LookupStrategy (provides an optimized lookup when querying the database), and an AclCache (caching ACL Entries and Object Identity).


Again, for simplicity, we use provided BasicLookupStrategy and EhCacheBasedAclCache.


DataSource dataSource;

public AclAuthorizationStrategy aclAuthorizationStrategy() {
    return new AclAuthorizationStrategyImpl(
      new SimpleGrantedAuthority("ROLE_ADMIN"));

public PermissionGrantingStrategy permissionGrantingStrategy() {
    return new DefaultPermissionGrantingStrategy(
      new ConsoleAuditLogger());

public EhCacheBasedAclCache aclCache() {
    return new EhCacheBasedAclCache(

public EhCacheFactoryBean aclEhCacheFactoryBean() {
    EhCacheFactoryBean ehCacheFactoryBean = new EhCacheFactoryBean();
    return ehCacheFactoryBean;

public EhCacheManagerFactoryBean aclCacheManager() {
    return new EhCacheManagerFactoryBean();

public LookupStrategy lookupStrategy() { 
    return new BasicLookupStrategy(
      new ConsoleAuditLogger()

Here, the AclAuthorizationStrategy is in charge of concluding whether a current user possesses all required permissions on certain objects or not.


It needs the support of PermissionGrantingStrategy, which defines the logic for determining whether a permission is granted to a particular SID.


3. Method Security With Spring ACL

3.使用Spring ACL的方法安全

So far, we’ve done all necessary configuration. Now we can put required checking rule on our secured methods.


By default, Spring ACL refers to BasePermission class for all available permissions. Basically, we have a READ, WRITE, CREATE, DELETE and ADMINISTRATION permission.

默认情况下,Spring ACL引用BasePermission类来获得所有可用的权限。基本上,我们有一个读、写、创建、删除管理权限。

Let’s try to define some security rules:


@PostFilter("hasPermission(filterObject, 'READ')")
List<NoticeMessage> findAll();
@PostAuthorize("hasPermission(returnObject, 'READ')")
NoticeMessage findById(Integer id);
@PreAuthorize("hasPermission(#noticeMessage, 'WRITE')")
NoticeMessage save(@Param("noticeMessage")NoticeMessage noticeMessage);

After the execution of findAll() method, @PostFilter will be triggered. The required rule hasPermission(filterObject, ‘READ’), means returning only those NoticeMessage which current user has READ permission on.

在执行findAll()方法后,@PostFilter将被触发。所需的规则hasPermission(filterObject, ‘READ’),意味着只返回那些当前用户有READ权限的NoticeMessage

Similarly, @PostAuthorize is triggered after the execution of findById() method, make sure only return the NoticeMessage object if the current user has READ permission on it. If not, the system will throw an AccessDeniedException.


On the other side, the system triggers the @PreAuthorize annotation before invoking the save() method. It will decide where the corresponding method is allowed to execute or not. If not, AccessDeniedException will be thrown.


4. In Action


Now we’re going to test all those configurations using JUnit. We’ll use H2 database to keep configuration as simple as possible.


We’ll need to add:





4.1. The Scenario


In this scenario, we’ll have two users (manager, hr) and a one user role (ROLE_EDITOR), so our acl_sid will be:

在这种情况下,我们将有两个用户(manager, hr)和一个用户角色(ROLE_EDITOR),所以我们的acl_sid将是。

INSERT INTO acl_sid (id, principal, sid) VALUES
  (1, 1, 'manager'),
  (2, 1, 'hr'),
  (3, 0, 'ROLE_EDITOR');

Then, we need to declare NoticeMessage class in acl_class. And three instances of NoticeMessage class will be inserted in system_message.


Moreover, corresponding records for those 3 instances must be declared in acl_object_identity:


INSERT INTO acl_class (id, class) VALUES
  (1, 'com.baeldung.acl.persistence.entity.NoticeMessage');

INSERT INTO system_message(id,content) VALUES 
  (1,'First Level Message'),
  (2,'Second Level Message'),
  (3,'Third Level Message');

INSERT INTO acl_object_identity 
  (id, object_id_class, object_id_identity, 
  parent_object, owner_sid, entries_inheriting) 
  (1, 1, 1, NULL, 3, 0),
  (2, 1, 2, NULL, 3, 0),
  (3, 1, 3, NULL, 3, 0);

Initially, we grant READ and WRITE permissions on the first object (id =1) to the user manager. Meanwhile, any user with ROLE_EDITOR will have READ permission on all three objects but only possess WRITE permission on the third object (id=3). Besides, user hr will have only READ permission on the second object.

最初,我们将第一个对象(id =1)的阅读写入权限授予用户manager。同时,任何拥有ROLE_EDITOR的用户将拥有所有三个对象的READ权限,但只拥有第三个对象的WRITE权限(id=3)。此外,用户hr在第二个对象上将只有READ权限。

Here, because we use default Spring ACL BasePermission class for permission checking, the mask value of the READ permission will be 1, and the mask value of WRITE permission will be 2. Our data in acl_entry will be:

在这里,因为我们使用默认的Spring ACL BasePermission类进行权限检查,READ权限的掩码值将是1,而WRITE权限的掩码值将是2。我们在acl_entry的数据将是。

INSERT INTO acl_entry 
  (id, acl_object_identity, ace_order, 
  sid, mask, granting, audit_success, audit_failure) 
  (1, 1, 1, 1, 1, 1, 1, 1),
  (2, 1, 2, 1, 2, 1, 1, 1),
  (3, 1, 3, 3, 1, 1, 1, 1),
  (4, 2, 1, 2, 1, 1, 1, 1),
  (5, 2, 2, 3, 1, 1, 1, 1),
  (6, 3, 1, 3, 1, 1, 1, 1),
  (7, 3, 2, 3, 2, 1, 1, 1);

4.2. Test Case


First of all, we try to call the findAll method.


As our configuration, the method returns only those NoticeMessage on which the user has READ permission.


Hence, we expect the result list contains only the first message:


@WithMockUser(username = "manager")
public void 
    List<NoticeMessage> details = repo.findAll();

Then we try to call the same method with any user which has the role – ROLE_EDITOR. Note that, in this case, these users have the READ permission on all three objects.


Hence, we expect the result list will contain all three messages:


@WithMockUser(roles = {"EDITOR"})
public void 
    List<NoticeMessage> details = repo.findAll();

Next, using the manager user, we’ll try to get the first message by id and update its content – which should all work fine:


@WithMockUser(username = "manager")
public void 
    NoticeMessage firstMessage = repo.findById(FIRST_MESSAGE_ID);
    NoticeMessage editedFirstMessage = repo.findById(FIRST_MESSAGE_ID);

But if any user with the ROLE_EDITOR role updates the content of the first message – our system will throw an AccessDeniedException:


@Test(expected = AccessDeniedException.class)
@WithMockUser(roles = {"EDITOR"})
public void 
    NoticeMessage firstMessage = repo.findById(FIRST_MESSAGE_ID);

Similarly, the hr user can find the second message by id, but will fail to update it:


@WithMockUser(username = "hr")
public void givenUsernameHr_whenFindMessageById2_thenOK(){
    NoticeMessage secondMessage = repo.findById(SECOND_MESSAGE_ID);

@Test(expected = AccessDeniedException.class)
@WithMockUser(username = "hr")
public void givenUsernameHr_whenUpdateMessageWithId2_thenFail(){
    NoticeMessage secondMessage = new NoticeMessage();

5. Conclusion


We’ve gone through basic configuration and usage of Spring ACL in this article.

在这篇文章中,我们已经经历了Spring ACL的基本配置和使用。

As we know, Spring ACL required specific tables for managing object, principle/authority, and permission setting. All interactions with those tables, especially updating action, must go through AclService. We’ll explore this service for basic CRUD actions in a future article.

正如我们所知,Spring ACL需要特定的表来管理对象、原则/权限和权限设置。所有与这些表的交互,尤其是更新动作,必须通过AclService。我们将在未来的文章中探讨这个服务的基本CRUD动作。

By default, we are restricted to predefined permission in BasePermission class.


Finally, the implementation of this tutorial can be found over on Github.