3

我正在尝试将带有 ACL 支持的 Spring Security 集成到现有的 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领域,但如何?

4

3 回答 3

1

首先,您需要某种域对象类来存储您的数据。这个类必须有一个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

于 2013-05-08T13:14:58.933 回答
0

查看Grails Spring Security ACL 插件文档。它解释了用于特定插件的域类,但它可能会对您有所帮助。查找 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()
}

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

希望这可以帮助。

于 2013-05-08T12:33:50.787 回答
0

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

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

正如这个答案String中提到的,保护一个对象并没有什么意义。当您考虑它时,这是有道理的。松散地说,我们可以将所有对象分为两类:

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

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

用例

目前还不是 100% 清楚问题中的用例是什么,例如someInput代表什么。但我假设用例类似于以下内容。假设有BankAccount实体和对这些实体的一些操作,例如readAccount。只有对 a 具有读取权限的用户BankAccount才能调用readAccount. 现在,BankAccount实体可以通过它们accountNumber的类型来唯一标识String。所以我们可能会被错误地引导到尝试这样的事情,这类似于问题中的代码:

@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,并且您将无法为 Strings 设置任何权限。

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

使用预授权,我们需要完全限定的Account类路径。因此,假设它在 packageX.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. 假设我们想以与帐户类似的方式限制对客户的访问。那么如果巧合 acustomerNumber匹配 anaccountNumber呢?根据Spring 文档,在 Spring ACL 中,对于对象类和 ID 的每种组合,我们只允许在对象标识表中输入一个条目:

CONSTRAINT uk_acl_object_identity UNIQUE (object_id_class, object_id_identity)

因此,假设String "fadfads389"恰好是 a customerNumberfor someCustomer和 an accountNumberfor some Account。如果我们通过 Spring ACL 限制对它的访问,那意味着什么?这是否意味着用户可以访问该帐户?客户?两个都?

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

瞬态仍然可以受到保护

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

字符串仍然可以在 PreAuthorize 中使用

现在,虽然我们不应该真正使用Spring ACL来保护Strings,但这并不是说@PreAuthorizeStrings. 我们注意到它@PreAuthorize可以处理任何 SpEL表达式,与此处@PostAuthorize指出的 for 和其他方法注释相同。因此,例如,您可以执行以下操作:

@PreAuthorize("#user.accountNumbers.contains(#accountNumber)")
public Account readAccount(String accountNumber, User user) {
    //CODE THAT RETRIEVES THE ENTITY FROM THE DB AND RETURNS IT
}

以上假设User该类维护该用户有权访问的帐号列表,因此推测 aUser是一个实体,或者至少由一些数据库持久化数据支持。

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

于 2021-07-15T13:38:51.750 回答