如何在 Spring Security 3.1 中使用 acl_entry 表中的 mask 字段?
我使用 Spring Security 3.1 ACL 实现。因此,根据教程,我创建了一个包含下表的 acl 数据库:
CREATE TABLE IF NOT EXISTS `acl_class` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`class` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_uk_2` (`class`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `acl_entry` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`acl_object_identity` bigint(20) NOT NULL,
`ace_order` int(11) NOT NULL,
`sid` bigint(20) NOT NULL,
`mask` int(11) NOT NULL,
`granting` tinyint(1) NOT NULL,
`audit_success` tinyint(1) NOT NULL,
`audit_failure` tinyint(1) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_uk_4` (`acl_object_identity`,`ace_order`),
KEY `foreign_fk_5` (`sid`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `acl_object_identity` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`object_id_class` bigint(20) NOT NULL,
`object_id_identity` bigint(20) NOT NULL,
`parent_object` bigint(20) DEFAULT NULL,
`owner_sid` bigint(20) DEFAULT NULL,
`entries_inheriting` tinyint(1) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_uk_3` (`object_id_class`,`object_id_identity`),
KEY `foreign_fk_1` (`parent_object`),
KEY `foreign_fk_3` (`owner_sid`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `acl_sid` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`principal` tinyint(1) NOT NULL,
`sid` varchar(100) NOT NULL,
`password` varchar(255) NOT NULL,
`salt` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
这与以下注释配合使用效果很好:
@PreAuthorize("hasPermission(#element, 'WRITE')")
@PostAuthorize("hasPermission(returnObject, 'READ')")
“读取”和“写入”权限在表 acl_entry 中设置为字段掩码。据我了解,1表示“读取”,2表示“写入”,4表示“创建”,8表示“删除”,16表示“管理”,因为它似乎是一种按位身份验证方法。
- 问:我对权利授予的理解是否正确?
- 问题:如何指定“读/写”等组合权限?我可以“设置”位 0 (即 int 1)和 1 (即 int 2)以便得到掩码值 1+2=3 吗?
现在我必须为“读”和“写”权限创建单个条目,这不太方便。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
根据 PacktPub 的 Spring Security 3.1:
该书中的示例尝试完全按照您所描述的方式进行操作 - 它指定一个具有3角色的用户进行读/写,但该用户被拒绝访问权限掩码为1的对象,用于读取。
解决方案是编写您自己的自定义权限评估器。
MyPermissionEvaluator.java:
要实际使用此自定义权限评估器,请编辑您的
security.xml
文件:最后,每当方法或类需要特定权限级别时:
现在您可以设置将 ACL 表中的权限掩码更改为与您的组织需求相对应的内容(按位),并以您标识每个用户权限的任何方式执行相同的操作。例如,
Nancy 的权限掩码为 13(可能的 31),存储在您的用户详细信息实现中。如果她尝试访问权限要求为
edit_data
的对象,系统将根据掩码要求 4 检查她的权限,并进行按位或计算 (permissionMask | requestsRequired ==permissionMask
) 将计算为true
。据我估计,这是实现特定于组织的按位权限掩码的最简单方法(可以使用 32 位,我认为这应该足够了)。根据参考书,Spring 注解中使用的
hasPermission
SpEL 表达式将用户的权限作为一个完整的单元进行评估;如果用户的读/写权限设置为 3,但注释仅针对读 (1) 进行评估,则用户将拒绝访问。According to Spring Security 3.1 by PacktPub:
The example in that book tries to do exactly what you're describing -- it specifies a user with a role of 3 for read/write, but the user is denied access to an object with a permission mask of 1 for read.
The solution is to write your own custom permission evaluator.
MyPermissionEvaluator.java:
To actually use this custom permission evaluator, edit your
security.xml
file:Finally, whenever a method or class requires a certain permission level:
Now you can set the permission mask in the ACL table to whatever corresponds to your organizational needs (bitwise), and do the same in whatever way you identify each individual user's permissions. For example,
Nancy thus has a permission mask of 13 (out of a possible 31) as stored in your user details implementation. If she tries to access an object with a permission requirement of
edit_data
, her permissions would be checked against a mask requirement of 4, and a bitwise OR evaluation (permissionMask | permissionsRequired == permissionMask
) would evaluate totrue
.This is, in my estimation, the easiest way to implement an organization-specific bitwise permissions mask (with 32 bits to play with, which should be enough, I should think). According to the referenced book, the
hasPermission
SpEL expression used in Spring annotations evaluates the user's permissions as a complete unit; if the user has a permission set at 3 for read/write, but the annotation only evaluates against read (1), the user will be denied access.为了实现逐位权限评估,您可以用自己的 PermissionEvaluator 覆盖 DefaultPermissionGrantingStrategy,而不是实现 PermissionEvaluator(这可能相当困难)。
这可能是您的 Spring ACL 配置
1) 您的 ACL 服务
2) 您的查找策略(dataSource、aclCache 和 aclAuthorizationStrategy 是默认策略)
3) 这里是有趣的部分,PermissionGrantingStrategy (http://docs.spring.io/spring-security/site/docs/current/apidocs/org/springframework/security/acls/model/PermissionGrantingStrategy.html)
这是您的位置会实现逐位权限逻辑,然后覆盖Spring的默认权限逻辑。
我希望这可以帮助
In order to implement a bit-wise permission evaluation, instead of implementing a PermissionEvaluator, which can be quite difficult, you can override the DefaultPermissionGrantingStrategy with your own one.
This could be your Spring ACL configuration
1) Your ACL Service
2) Your lookup strategy (dataSource, aclCache and aclAuthorizationStrategy are the default ones)
3) Here comes the interesting part, the PermissionGrantingStrategy (http://docs.spring.io/spring-security/site/docs/current/apidocs/org/springframework/security/acls/model/PermissionGrantingStrategy.html)
Here is where you would implement the bit-wise permission logic, and then override the Spring's default one.
I hope this can help
来自 SpringSecurity
@question#1:是的,没错。
@question#2:你可以使用类似的东西:
new BasePermission(BasePermission.WRITE.getMask() | BasePermission.READ.getMask())
获得
READ
和WRITE
权限。来自春季文档:
From SpringSecurity
@question#1: yes that's right.
@question#2: you could use something like:
new BasePermission(BasePermission.WRITE.getMask() | BasePermission.READ.getMask())
to get a
READ
andWRITE
permission.From the spring docs:
这个答案指出Spring会比较ACE掩码和我们正在检查的权限之间的精确匹配。正如OP所说:
-是的。这似乎是默认行为。正如我们从提供的任何数据库模式中看到的 此处,我们可以为给定对象添加多个条目。因此,如果我们想授予 Alice 读取和写入访问权限,我们将添加一个读取条目,顺序可能为 1,然后添加一个写入条目,顺序为 2 或其他。
尽管默认行为似乎如上所示,但文档中提到的“按位”可能会让新手感到困惑。然而,我想,对于一个已经存在这么久并被这么多人依赖的框架的向后兼容性,改变默认行为可能已经太晚了。 Spring 开发人员似乎很清楚这个难题,因此他们提供了一种足够简单的方法来克服它。正如这个答案指出的,关键是提供自定义的
PermissionGrantingStrategy
。链接的答案显示了如何通过 XML 在类中进行挂钩。不过,我对基于代码的配置更满意,因此我遵循了 此链接,它解释了如何在代码中设置 Spring ACL。现在,您所要做的与该链接中描述的不同,就是替换此 Bean 定义:
并返回您自己制定的权限授予策略。不过别担心!您不必完全从头开始编写自己的类。然后,您的自定义权限授予策略只需创建
DefaultPermissionGrantingStrategy
类的子类型并仅重写isGranted
方法。默认实现确实只是进行完全匹配:但根据该方法文档中的这段代码,作者似乎很清楚需要进行覆盖:
该文档甚至进一步实际为您提供了进行覆盖的代码。我还没有测试过这个,但我相信这只是:
你应该使用按位 ACL 吗?
在覆盖 Spring ACL 默认使用按位存储之前,您应该首先问自己是否值得付出额外的努力以及因偏离 OOTB 行为而产生错误的风险。
而且,您还应该意识到,您将失去非按位 Spring ACL 中可用的一些行为:即删除 ACE 的能力。我将用一个例子来演示。假设有一个名为
C
的Company
对象和一个名为D
的Department
对象,该对象嵌套在C 下
在 Spring ACL 层次结构中。假设D
从C
继承了它的权限。现在假设 Alice 拥有整个公司
C
的 READ 访问权限。由此,她继承了对C
下所有部门(包括D
)的 READ 访问权限。有一天,Bob 决定只授予 Alice 对部门D
的 WRITE 访问权限,而不授予任何其他部门。通过按位逻辑,D
会添加什么 ACE?由于单个 ACE 必须包括编码到各个位中的所有读、写等权限,因此读位必须为 0 或 1。两者都可能存在问题:D
进行读取访问,因此我们必须假设 0 位并不意味着“被拒绝”,而是较弱的含义“未授予”。这意味着 ACL 逻辑仍将查找父对象来授予权限。这意味着我们失去了拒绝覆盖在层次结构中更高级别授予的权限的能力。C
的 READ 访问权限,会发生什么?没有办法知道对D
的 READ 访问是否也应该被删除,除非我们将该信息存储在某个地方,而这样做会破坏按位 ACL 的全部意义,即压缩存储。解决此问题的一种方法是为每个权限使用两位位,而不是 1。例如,您可以解释如下:00 表示未授予或拒绝 - 向父母查找值,01 表示拒绝,10 表示同意。在这种情况下,11 是免费的,但您稍后可能会将其用于其他用途,例如。但是,使用 2 位执行此操作会更复杂,因为 OOTB 权限只有 1 位,因此您必须编写自己的权限。尽管付出了所有这些努力,最终您只能获得一半的存储压缩。
This answer pointed out that Spring compares for an exact match between the ACE mask and the permission we're checking. As the OP says:
-Yes. This seems like the default behaviour. As we can see from any of the DB schemas presented here, we are allowed to add multiple entries for a given object. So if we want to grant Alice read and write access, we'd add an entry for read, maybe with order 1, and then an entry for write, with order 2 or something.
Although the default behaviour seems to be as above, the mention of "bitwise" in the documentation is probably confusing newcomers. However, I'd imagine that for backwards compatibility of a framework that's been around for so long and relied upon by so many, it's probably too late to change the default behaviour. The Spring developers seem to be well aware of this conundrum, and so they have provided a simple enough way to overcome it. As this answer points out, the key is to provide a custom
PermissionGrantingStrategy
. The linked answer shows how to do hook in the class via XML. I am more comfortable with code based configuration though, so I followed this link which explains how to do set up Spring ACL in code.Now, all you have to do that's different to what's described in that link, is to replace this Bean definition:
And instead return a permission granting strategy of your own making. Don't worry though! You don't have to write your own class completely from scratch. Your custom permission granting strategy can then just subtype the class
DefaultPermissionGrantingStrategy
and override only the methodisGranted
. The default implementation indeed just does an exact match:But the author seems well aware of the need for an override, based on this snippet from the method's doc:
The documentation goes even further to actually give you the code to do the override. I haven't tested this, but I believe it's just:
Should you Use Bitwise ACL?
Before overriding Spring ACL's default to use bitwise storage, you should first ask yourself is it worth the extra effort and the risk of bugs by deviating from OOTB behaviour.
But also, you should be aware that you will lose some behaviour that is available in non-bitwise Spring ACL: namely the ability to delete ACEs. I'll demonstrate with an example. Suppose there is a
Company
object calledC
and aDepartment
object calledD
which is nested underneathC
in the Spring ACL hierarchy. And supposeD
inherits its permissions fromC
.Now suppose Alice has READ access to the entire company
C
. From this, she inherits READ access to all departments underneathC
, includingD
. One day, Bob decides he wants to grant Alice WRITE access to the departmentD
only, but not any other departments. With bitwise logic, what ACE gets added forD
? Since the single ACE must include all permissions READ, WRITE etc. encoded into the various bits, the READ bit must be either 0 or 1. Both are potentially problematic:D
, so we'd have to assume that a 0 bit doesn't mean "denied" but the weaker meaning "not granted". Meaning the ACL logic will still look to parent objects to grant the permission. This means that we lose the ability to DENY-override permissions that were granted at a higher level in the hierarchy.C
and so sets the READ bit to 1 on the new ACE. But then what happens if we remove Alice's READ access toC
? There is no way to know that the READ access toD
should also be removed, unless we store that info somewhere, and doing so would defeat the whole point of bitwise ACLs which is to compress storage.One way around this would be to use two bits for every permission, instead of 1. For example, you could interpret as follows: 00 means not granted or denied - look to parents for a value, 01 means denied, 10 means granted. 11 would be free in that case, but you might use it for something else later, for example. However, doing it with 2 bits would be more complex because the OOTB permissions are only 1 bit, so you'd have to write your own. And for all that effort, you'd only get half the storage compression at the end of it.