Java – 高效、数据库感知的实例级授权?
Posted
技术标签:
【中文标题】Java – 高效、数据库感知的实例级授权?【英文标题】:Java – efficient, database-aware instance-level authorization? 【发布时间】:2012-06-02 22:02:03 【问题描述】:在一个 JPA 应用程序中,我有一个应用程序的场景
列出给定用户有权退出的所有帐户
我有 Account 实体和一个多对多表,其中列出了每个用户对每个帐户的授权——为了实现上述场景,应用程序当前只是内连接两个表——这非常快。
现在,我正计划添加一个显式授权层(基于 apache shiro / spring security / other)以将授权相关逻辑与其余代码隔离,但是...
数据库中有大约 10k 个帐户,“普通”用户被授予“存款”所有帐户,“查看”其中一半帐户和“提款”帐户。
是否有任何安全框架允许有效地实施此方案?
即:他们中的任何一个是否能够“装饰”类型为“从帐户 a 中选择 a”(或等效的 SQL)的 JPA 查询,从而获得帐户列表而无需从数据库,并且无论如何,不必检索所有帐户?)
【问题讨论】:
...并且您没有与授权关联的用户实体,只能“选择 * FROM Account WHERE Account.userAuths.user = user”? 是的 - 这就是应用程序现在所做的。我想知道是否有任何身份验证框架允许我从代码中删除与授权相关的连接。 能否通过 NamedNativeQuery 查询返回 Account 对象,例如 'select acc.* from account acc, user_auth uauth where uauth.user_id=:login_userid` 帮助? 谢谢,但是...不:(这又是与授权相关的代码,我宁愿不在检索帐户的地方放它。假设在某些时候我必须添加“ root”用户,无论如何都可以从所有帐户中退出 - 加入该连接后,我将不得不更新查询(或者 - 可以说更好 - 我必须确保 USER_AUTH 表有一个“ root”和每个帐户) 【参考方案1】:看看Apache Shiro。
它允许您提取一次用户授权并在会话期间将其缓存。此外,如果所有用户都可以查看所有帐户,那么您不需要显式定义它,这将显着减少开销。
如果您的解决方案需要实时访问处理程序,Shiro 也可以在运行时动态重置权限。
Shiro 允许您实现典型的 RBAC 并像这样定义权限:
domain:action:instance
因此,在您的情况下,用户的权限可能如下所示:
account:deposit:* // deposit all accounts
account:view:1111
account:view:2222
account:view:3333 // view on these accounts
account:withdraw:5555
account:withdraw:6666 // withdraw on these accounts
然后你可以在代码中做这样的事情:
if (SecurityUtils.getSubject().isPermitted("account:withdraw:"+account.getAccountNumber() )
// handle withdraw
Shiro 还具有用于额外抽象的注释驱动权限。
编辑
Shiro 权限是最终结果,而不是您开始的地方。我使用了一组表来表示用户到角色和角色到权限的映射以及其他到实例的映射。在 AuthN 之后,它通常是由用户 PK 索引的一组简单查询,用于构建呈现权限所需的数据结构。
【讨论】:
当你拥有“account:view:1111,2222,3333,etc”权限时,如何构建进行数据库查询的请求?通过解析权限实例级部分?假设你有view 权限中有数千个 id。这似乎一点效率都没有。【参考方案2】:我希望这是使用 Spring-Security 实现您的要求的可能性之一。
写自定义org.springframework.security.acls.Permission
点赞
ViewAccount
,DepositToAccount
,WithDrawFromAccount
编写自定义
org.springframework.security.access.PermissionEvaluator
覆盖
hasPermission(Authentication userAuthentication,Object
accountObject,Object oneOfThePermission)
检查用户是否有
accountObject 上定义的权限
获取对 JPA 的引用 EntityManager 在您的自定义评估器中并在 DB 中进行交叉检查/验证 与 user_id,permission_id,account_id
如果用户是“root”,您可以
立即为 hasPermission
返回 true,无需验证
数据库。
@PreAuthorize("isAuthenticated() and hasPermission(#accountArgument,
'respectivePermission')")
请参阅 link 以了解 Permission
和 PermissionEvaluator
的自定义实现
【讨论】:
似乎需要在 each Account 实例上调用 hasPermission ...我担心如果不从数据库中检索所有行(即:加载 10k 个实体)就无法工作只显示几个) 参考这些链接 ***.com/questions/559480/… , jpasecurity.sourceforge.net JPAsecurity 的东西看起来很 alpha,但也是我能找到的唯一适用的库...... @Ahamed,如果你编辑你的答案,使其成为对 jpasecurity 的引用(cmets 会也受到潜在读者的赞赏)我会接受它。【参考方案3】:如果您使用的是 EclipseLink,则有一些功能可以做到这一点,
一个是@AdditionalCriteria 注释,它允许将过滤器应用于类的所有查询,
http://www.eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_additionalcriteria.htm#additionalcriteria
另一个是 EclipseLink 对 Oracle VPD(数据库中的行级安全性)的支持,
http://wiki.eclipse.org/EclipseLink/Examples/JPA/Auditing
最后 EclipseLink 支持 SessionEvents,它可以允许将过滤器附加到任何查询执行中,
http://www.eclipse.org/eclipselink/api/2.4/org/eclipse/persistence/sessions/SessionEventAdapter.html#preExecuteQuery%28org.eclipse.persistence.sessions.SessionEvent%29
【讨论】:
以上是关于Java – 高效、数据库感知的实例级授权?的主要内容,如果未能解决你的问题,请参考以下文章