在Spring注释中使用静态变量

时间:2013-07-03 09:52:45

标签: java spring spring-security spring-annotations

我正在使用spring的PreAuthorize注释,如下所示:

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

但是,我已经将'role'定义为另一个类上的静态String。如果我尝试使用此值:

@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注释来访问这样的静态变量?

5 个答案:

答案 0 :(得分:24)

尝试以下使用Spring Expression Language来评估类型:

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

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

Documentation

答案 1 :(得分:7)

为了能够编写没有包名称的表达式:

<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')")

答案 2 :(得分:4)

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

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

但是,我使用我的enums.Permissions类作为参数类型而不是String:

@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,在这种情况下,唯一的权衡是您正在为@mySecurityService和额外的单引号交易T(fully.qualified.path)。

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

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

答案 3 :(得分:3)

尝试这样的事情:

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

如果您的OtherClass枚举被声明为公共静态,那么您需要使用$ sign:

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

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

答案 4 :(得分:2)

您还可以创建具有以下角色的Bean容器:

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

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

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