如何从使用选民的AccessDecisionManager中抛出信息性异常

时间:2009-02-04 10:50:27

标签: java authorization spring-security

我有以下情况:我的应用程序的授权机制是使用Spring安全性实现的。中心类实现 AccessDecisionManager 并使用选民(每个实现 AccessDecisionVoter )来决定是否授予对某些方法的访问权限。计算得出的算法是自定义的:

public class PermissionManagerImpl extends AbstractAccessDecisionManager {

    public void decide(
            Authentication authentication,
            Object object,
            ConfigAttributeDefinition config) throws AccessDeniedException {
        Iterator<?> iter = getDecisionVoters().iterator();
        boolean wasDenied = false;

        while (iter.hasNext()) {
            AccessDecisionVoter voter = (AccessDecisionVoter) iter.next();
            int result = voter.vote(authentication, object, config);

            switch (result) {
                // Some tallying calculations
            }
        }

        if (wasDenied) {
            throw new AccessDeniedException("Access is denied");
        }               
    }

}

在拒绝访问某些方法时,应用程序的客户端有兴趣获取信息性异常,该异常确切地指定为什么拒绝访问。这意味着将一些信息从选民传递给决策经理。遗憾的是,标准 AccessDecisionVoter 传递回决策经理的唯一信息是可能的返回值之一( ACCESS_GRANTED ACCESS_ABSTAIN ACCESS_DENIED )。

最好的方法是什么?

感谢。

3 个答案:

答案 0 :(得分:3)

嗯,AccesssDecisionVoter接口在这种情况下实际返回int。当然,内置的选民实现总是只返回你提到的三个常量中的一个(这些是标准访问决策管理器检查的内容),但是它们实际上没有任何额外的返回 - {{1}例如,当且仅当主体没有所需角色时才会拒绝访问。

由于你使用自己的选民和访问决策经理,你可以看到几个选项:

  1. 将整数的其他值作为某种形式的错误代码返回;将RoleVoterACCESS_GRANTEDACCESS_ABSTAIN视为典型值,但将任何其他整数视为“拒绝访问”,并带有错误代码。理想情况下,有一个可用错误代码的查找表 - 基本上是一个穷人的枚举。
  2. 在您的选民中,像往常一样返回ACCESS_DENIED,并设置一些可公开访问的属性(在选民对象本身或可能是某些静态可访问的字段)中,并显示错误原因。在您的经理中,如果您的自定义选民拒绝访问,请检查该属性以获取详细信息。
  3. 如上所述,在选民中设置错误属性;但要确保传入的ACCESS_DENIED实例是您自己的自定义子类之一,它提供了一个好处 设置/检索此信息的位置。
  4. 从您的选民本身内投掷Authentication(或合适的子类)。这并不理想,因为它预先假定了访问决策管理器中的逻辑;但你可以让这个泡泡直接上升,或者如果需要在管理器中捕获它(自定义子类肯定会对此有好处)并重新抛出,如果访问确实被拒绝(类似于AccessDeniedException类所做的事情)它的ProviderManager变量。)
  5. 这些都不是明显正确和优雅的答案,但你应该能够从最适合的任何一个方面获得可行的东西。由于通信原因在选民框架内没有显式支持(从根本上说这是一个直接的布尔响应),我认为你不能做得更好。

答案 1 :(得分:2)

感谢回答的人。

我认为我已经找到了一种非常优雅的方式来做我想做的事情并且仍然使用标准选民API。 AccessDecisionVoter 投票方法的第二个参数是安全对象。我可以在决策管理者和选民之间创建一个契约,该对象是一个特定的类/接口,它是一个包装器,通过它可以获取原始的安全对象,还可以获得其他信息。选民们否认拒绝进入。

我在其他框架中也看到了这样的模式。与其他可能的解决方案相比,该解决方案具有以下优势:

  • 选民可以保持无国籍,所以他们可以成为单身人士
  • 使用AccessDecisionVoter的标准接口,不添加新的返回值
  • 附加信息保存在自动丢弃的对象中,因为在 AbstactDecisionManager 决定方法后没有人使用它,因此不需要清理代码

干杯。

答案 2 :(得分:1)

如果不使用选民,你不能直接实施AccessDecisionManager吗?然后,您可以使用正确的信息抛出AccessDeniedException。也许RoleVoter s不适合在你的情况下使用。