【问题标题】:Using static variables in Spring annotations在 Spring 注解中使用静态变量
【发布时间】:2013-06-30 22:59:33
【问题描述】:

我使用spring的PreAuthorize注解如下:

@PreAuthorize("hasRole('role')");

但是,我已经将“角色”定义为另一个类的静态字符串。如果我尝试使用这个值:

@PreAuthorize("hasRole(OtherClass.ROLE)");

我收到一个错误:

org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 14): Field or property 'OtherClass' cannot be found on object of type 'org.springframework.security.access.expression.method.MethodSecurityExpressionRoot'

有没有办法通过 PreAuthorize 注释访问这样的静态变量?

【问题讨论】:

    标签: java spring spring-security spring-annotations


    【解决方案1】:

    尝试以下使用 Spring 表达式语言来评估类型:

    @PreAuthorize("hasRole(T(fully.qualified.OtherClass).ROLE)");
    

    请务必指定完全限定的类名。

    Documentation

    【讨论】:

    • 有效,但它仍然是一个解释字符串,所以当你重构时它不会被 Eclipse “看到”,例如我猜的名字。
    【解决方案2】:

    试试这样的:

    @PreAuthorize("hasRole(T(com.company.enumpackage.OtherClass).ROLE.name())");
    

    如果您的 OtherClass 枚举被声明为 public static,那么您需要使用 $ 符号:

    @PreAuthorize("hasRole(T(com.company.ParentTopLevelClass$OtherClass).ROLE.name())");
    

    name() 以防止以后出现问题,如果 toString() 稍后将被覆盖

    【讨论】:

      【解决方案3】:

      Kevin Bowersox 接受的答案有效,但我不喜欢 T(fully.qualified.path) 的东西,所以我一直在寻找。我首先使用 James Watkins 的答案创建了一个自定义安全方法:

      How to create custom methods for use in spring security expression language annotations

      但是,我使用 enums.Permissions 类作为参数类型,而不是字符串:

      @Component
      public class MySecurityService {
          public boolean hasPermission(enums.Permissions permission) {
      
              ...do some work here...
      
              return true;
          }
      }
      

      现在最简洁的部分是,当我从注释中调用 hasPermission 时,我不必输入整个路径,但我必须用单引号将其括起来:

      @PreAuthorize("@mySecurityService.hasPermission('SOME_ROLE_NAME')")
      

      因为 hasPermission 方法需要一个 Enum,它会自动找到具有该名称的 Enum 值。如果它没有找到它,你会得到一个异常:

      org.springframework.expression.spel.SpelEvaluationException: Type conversion problem, cannot convert from java.lang.String to enums.Permissions
      

      您可以将 hasPermission 重命名为 hasRole,在这种情况下,唯一的权衡是您将 T(fully.qualified.path) 换成 @mySecurityService 和额外的单引号。

      不确定它是否更好,但确实如此。由于无论如何这些都不会在编译时验证这些值,所以我的下一步是制作一个注释处理器。

      我还要感谢 krosenvold 指出 spring 可以自动转换为枚举: https://stackoverflow.com/a/516899/618881

      【讨论】:

        【解决方案4】:

        使编写没有包名的表达式成为可能:

        <sec:global-method-security>
            <sec:expression-handler ref="methodSecurityExpressionHandler"/>
        </sec:global-method-security>
        
        <bean id="methodSecurityExpressionHandler" class="my.example.DefaultMethodSecurityExpressionHandler"/>
        

        然后扩展 DefaultMethodSecurityExpressionHandler:

        public class DefaultMethodSecurityExpressionHandler extends org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler {
        
            @Override
            public StandardEvaluationContext createEvaluationContextInternal(final Authentication auth, final MethodInvocation mi) {
                StandardEvaluationContext standardEvaluationContext = super.createEvaluationContextInternal(auth, mi);
                ((StandardTypeLocator) standardEvaluationContext.getTypeLocator()).registerImport("my.example");
                return standardEvaluationContext;
            }
        }
        

        现在创建 my.example.Roles.java :

        public class Roles {
        
            public static final String ROLE_UNAUTHENTICATED = "ROLE_UNAUTHENTICATED";
        
            public static final String ROLE_AUTHENTICATED = "ROLE_AUTHENTICATED";
        }
        

        并在注释中不带包名的方式引用它:

        @PreAuthorize("hasRole(T(Roles).ROLE_AUTHENTICATED)")
        

        代替:

        @PreAuthorize("hasRole(T(my.example.Roles).ROLE_AUTHENTICATED)")
        

        使其更具可读性恕我直言。现在还键入了角色。写:

        @PreAuthorize("hasRole(T(Roles).ROLE_AUTHENTICATEDDDD)")
        

        如果你写的话,你会得到不会出现的启动错误:

            @PreAuthorize("hasRole('ROLE_AUTHENTICATEDDDD')")
        

        【讨论】:

        • 很好的解决方案。有启动错误会很好,但我认为你不能像这样实现。评估仍然在运行时进行。还没有找到更好的解决方案,但很难......
        • 我认为 Kevin Bowersox 的答案是一个很好的答案,但 @MagnusHeino 的答案是一个更完整的解决方案,因为包含完整包的类看起来有点难看,但是,两个答案都是正确的。跨度>
        【解决方案5】:

        您还可以创建一个带有角色的 bean 容器,例如:

        @Component("R")
        public final class RoleContainer {
          public static final String ROLE_A = "ROLE_A";
        }
        

        然后你可以在控制器上使用:

        @PreAuthorize("hasRole(@R.ROLE_A)")
        

        【讨论】:

        • 最短最简洁的答案!
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-02-15
        • 1970-01-01
        • 1970-01-01
        • 2012-11-30
        • 2017-05-13
        • 2011-01-29
        • 2011-12-05
        相关资源
        最近更新 更多