什么进入Spring Security的ACL_OBJECT_IDENTITY表?

时间:2013-04-30 20:32:49

标签: java spring-security acl

我正在尝试将Spring Security和ACL支持集成到现有的GWT应用程序中,如果我点击另一个无用的链接,我发誓将需要一个新的鼠标和键盘。我已经完成了使用Spring通过LDAP对Active Directory进行身份验证所需的工作,并且我已经弄清楚如何根据AD属性(即组成员身份)分配自定义权限,我甚至已经弄清楚了使用自定义ACL架构对权限(真正的位掩码操作)执行自定义检查。我还没想到的只是ACL表中的内容。

ACL_SID

id:bigint (pk)
principal:boolean (ak)
sid:varchar (ak)

这张表格非常明显;我们只在这里使用非主要条目。

ACL_CLASS

id:bigint (pk)
class:varchar (ak)

这张表也很明显。据我了解,我们只是为我们希望保护的每个类/接口创建一个条目。

ACL_ENTRY

id:bigint (pk)
acl_object_identity:bigint (fak)
ace_order:int (ak)
sid:bigint (fk)
mask:bigint
granting:boolean
audit_success:boolean
audit_failure:boolean

这张表也大多不言自明;我们已在mask字段中使用bigint / long自定义架构,但问题源于acl_object_identity引用的内容。显然,它指向ACL_OBJECT_IDENTITY中的字段,但是......

ACL_OBJECT_IDENTITY

id:bigint (pk)
object_id_class:bigint (fak)
object_id_identity:bigint (ak)
parent_object_identity:bigint (fk)
owner_sid:bigint (fk)
entries_inheriting:boolean

object_id_identity是什么?一个方法?它是如何通过Spring Security的注释引用的?

MyClass.java

@PreAuthorize("hasPermission(#someInput, 'READ')")
public boolean myMethod(String someInput) {
    return true;
}

据推测,#someInput某种方式指的是ACL_OBJECT_IDENTITY.object_id_identity字段,但是如何?

3 个答案:

答案 0 :(得分:0)

查看Grails Spring Security ACL Plugin Documentation。 它解释了用于特定插件的域类,但它可能对您有所帮助。查找AclObjectIdentity和AbstractAclObjectIdentity。

ACL_OBJECT_IDENTITY.object_id_identity字段是实例ID,唯一标识实例(与object_id_class一起)

在您的示例中,'someInput'将是该实例。 这段代码取自前面提到的文档:

@PreAuthorize("hasPermission(#report, delete) or hasPermission(#report, admin)")
void deleteReport(Report report) {
    report.delete()
}

它解释了自己。如果当前经过身份验证的用户对给定的报表实例具有“admin”或“delete”权限,则会删除该报表。报告是代码段中的someInput。

希望这有帮助。

答案 1 :(得分:0)

首先,您需要某种域对象类来存储数据。该类必须具有getId()方法。例如:

public class DomainObject {
    private Long id;
    private String data;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getData() {
        return data;
    }
    public void setData(String data) {
        this.data = data;
    }
}

将您的安全方法更改为:

@PreAuthorize("hasPermission(#someInput, 'READ')")
public boolean myMethod(DomainObject someInput) {
    return true;
}

现在,someInput.getId()ACL_OBJECT_IDENTITY.object_id_identity相同。

答案 2 :(得分:0)

这个问题似乎暗示了对 Spring ACL 的轻微误解。在问题中,我们被问到如何为 String 对象注册 ACL,以便此方法保护起作用:

@PreAuthorize("hasPermission(#someInput, 'READ')")
public boolean myMethod(String someInput) {
    return true;
}

this answer 中所述,保护 String 对象实际上没有意义。当你想到它时,这是有道理的。粗略地说,我们可以将所有对象分为两类:

  1. 持久化到数据库的对象 - 我们称它们为实体
  2. 未持久化到数据库的对象 - 我们称它们为瞬态

在我能想到的任何现实用例中,只有保护对实体的访问才有意义,而不是对瞬态的访问;稍后我将对此案进行一般性辩论。不过,首先,让我们坚持一个与问题相关的用例,看看为什么在这种情况下可能不需要保护瞬态。

用例

问题中的用例不是 100% 清楚,例如someInput 代表什么。但我假设用例类似于以下内容。假设有 BankAccount 实体和对这些实体的一些操作,例如readAccount。只有对 BankAccount 具有读访问权限的用户才能调用 readAccount。现在,BankAccount 实体可以通过其类型为 accountNumberString 唯一标识。所以我们可能会被错误引导去尝试类似问题中的代码:

@PreAuthorize("hasPermission(#accountNumber, 'READ')")
public Account readAccount(String accountNumber) {
    //CODE THAT RETRIEVES THE ENTITY FROM THE DB AND RETURNS IT
}

好吧,这不是一个糟糕的假设。我想在这个阶段我们的想法是 Spring ACL 存储一个帐号表,并且对于每个帐号,都有一个对它具有 READ 访问权限的人的列表。问题是,Spring ACL 不能那样工作。如this answer中所述,Spring ACL 通过以下方式识别对象:

  1. 对象的类是什么?在这种情况下,它将是 java.lang.String
  2. 对象的 ID 是什么?在这种情况下,Spring ACL 要求对象需要一个 getId() 方法。幸运的是,如果您使用的是 Hibernate,默认情况下您的所有实体都将具有此功能,因此您无需执行任何额外操作即可实现它。但是字符串呢?嗯,这没有 getId() 方法。因此,Spring ACL 将无法为其注册 ACL,您也无法为字符串设置任何权限。

仔细想想,Spring ACL 是这样设计的,这实际上是有道理的。 getId() 方法允许我们将持久化的 ACL 权限条目与持久化的实体相关联。这是典型的用例。所以在上面的例子中,我们真正想要做的是限制对 Account 对象的访问,而不是帐号。在这种情况下,我们有两种选择:预授权或后授权。

使用预授权,我们需要 Account 类的完全限定路径。所以假设它在包 X.Y 中,我们有:

@PreAuthorize("hasPermission(#accountId, 'X.Y.Account', 'READ')")
public Account readAccount(Long accountId) {
    //CODE THAT RETRIEVES THE ENTITY FROM THE DB AND RETURNS IT
}

请注意,在上文中,我们使用 ID 而不是帐号来标识帐户。这是允许您使用 Spring ACL 识别实体的唯一方法,因为 getId() 是 ACL 与其关联对象之间的链接。当然,您可以自由编写任何您喜欢的代码来检索给定 ID 的对象,例如你可以做一些愚蠢的事情,比如在检索之前增加 ID。因此,在这种情况下,不能保证返回的对象与授权的对象相同:这取决于您编写正确的检索代码。

我们可以保护方法的另一种方法是使用后授权,如下所示:

@PostAuthorize("hasPermission(returnObject, 'READ')")
public Account readAccount(String accountNumber) {
    //CODE THAT RETRIEVES THE ENTITY FROM THE DB AND RETURNS IT
}

在这种情况下,检索帐户实体的代码实际上被调用,然后只有在检索到它之后,ACL 框架才会根据当前用户和 READ 权限检查帐户,即返回的对象。这里的一个优势是我们可以以任何我们喜欢的方式检索帐户,例如在这种情况下由 accountNumber。另一个优点是保证授权的对象与返回的对象相同。缺点是我们必须实际进行检索,然后才能调用用户是否具有权限。如果他们没有权限,那么检索到的对象就会被有效地丢弃,因此如果检索代码的运行成本很高,它的性能可能会比 @PreAuthorize 稍差。

为什么用 Spring ACL 保护字符串无论如何都没有意义

从技术上讲,我想您可能能够保护字符串,或者实际上任何其他瞬态,只要它具有 getId() 方法。例如,对于字符串,我们可以添加一个扩展函数 getId()。但我想不出一个实际用例来说明我们为什么要这样做。例如,假设我们不仅有 Account 对象,而且还有 Customer 对象。假设 Customer 对象可通过 customerNumber 字段唯一标识,该字段是 String。假设我们想以与帐户类似的方式限制对客户的访问。那么,如果一个 customerNumber 恰好匹配一个 accountNumber 呢?在 Spring ACL 中,对于对象类和 ID 的每个组合,我们只允许在对象标识表中使用一个条目,如Spring docs

CONSTRAINT uk_acl_object_identity UNIQUE (object_id_class, object_id_identity)

因此,假设 String "fadfads389" 恰好既是 customerNumberCustomer,也是 accountNumberAccount。如果我们通过 Spring ACL 限制对它的访问,那意味着什么?这是否意味着用户可以访问该帐户?客户?两个?

希望这个例子能说明为什么当瞬态类用于识别实体时,用 Spring ACL 保护一些瞬态类(例如 String)没有真正意义:当我们对保护实体感兴趣时,我们只是保护实体本身,使用这些实体的隐式 ID,例如Hibernate 存储的 ID。

瞬态仍然可以受到保护

当然,没有什么可以阻止您将对象标识条目添加到 Spring ACL acl_object_identity 表中的任何您想要的类,只要该类具有 getId() 方法。因此,您当然可以添加与瞬态相关的权限,并且如果这些瞬态再次出现在内存中,那么 Spring ACL 就会启动。但这并不是 Spring ACL 的真正设计目的 - 它实际上是为了保护实体,而不是瞬态,通过 getId() 链接到 ACL 逻辑。

字符串仍可用于预授权

现在,虽然我们不应该真正使用 Spring ACL 来保护 String,但这并不是说 @PreAuthorize 在涉及 { {1}}。我们注意到 Strings 可以处理 any SpEL 表达式,@PreAuthorizehere 指出的其他方法注释也是如此。因此,例如,您可以执行以下操作:

@PostAuthorize

上面假设 @PreAuthorize("#user.accountNumbers.contains(#accountNumber)") public Account readAccount(String accountNumber, User user) { //CODE THAT RETRIEVES THE ENTITY FROM THE DB AND RETURNS IT } 类维护着一个用户有权访问的帐号列表,所以大概 User 是一个实体,或者至少由一些数据库持久化数据支持.

但是,如果您确实想走这条路,请注意。您不仅要冒着将访问控制逻辑与其他业务逻辑纠缠在一起的风险,还可能会在性能方面失去优势; Spring ACL 使用缓存来快速查找权限,而上述代码可能需要从数据库中获取 User 数据才能进行授权。