使用枚举类型作为@RolesAllowed注释的值参数

2022-08-31 15:12:49

我正在开发一个Java企业应用程序,目前正在做Java EE安全工作,以限制特定用户对特定功能的访问。我配置了应用程序服务器和所有内容,现在我正在使用 RolesAllowed 注释来保护这些方法:

@Documented
@Retention (RUNTIME)
@Target({TYPE, METHOD})
public @interface RolesAllowed {
    String[] value();
}

当我像这样使用注释时,它工作正常:

@RolesAllowed("STUDENT")
public void update(User p) { ... }

但这不是我想要的,因为我必须在这里使用字符串,重构变得困难,并且可能会发生拼写错误。因此,我不想使用 String,而是使用 Enum 值作为此注释的参数。枚举如下所示:

public enum RoleType {
    STUDENT("STUDENT"),
    TEACHER("TEACHER"),
    DEANERY("DEANERY");

    private final String label;

    private RoleType(String label) {
        this.label = label;
    }

    public String toString() {
        return this.label;
    }
}

所以我尝试使用Enum作为参数,如下所示:

@RolesAllowed(RoleType.DEANERY.name())
public void update(User p) { ... }

但是后来我得到了下面的编译器错误,尽管 Enum.name 只返回一个字符串(它总是常量,不是吗?)。

注释属性 RolesAllowed.value 的值必须是常量表达式'

我尝试的下一件事是在我的枚举中添加一个额外的最终字符串:

public enum RoleType {
    ...
    public static final String STUDENT_ROLE = STUDENT.toString();
    ...
}

但这也不能用作参数,从而导致相同的编译器错误:

// The value for annotation attribute RolesAllowed.value must be a constant expression
@RolesAllowed(RoleType.STUDENT_ROLE)

如何实现我想要的行为?我甚至实现了我自己的拦截器来使用我自己的注释,这很漂亮,但是对于这样的小问题来说,代码行太多了。

免責聲明

这个问题最初是一个Scala问题。我发现Scala不是问题的根源,所以我首先尝试在Java中执行此操作。


答案 1

我不认为你使用枚举的方法会起作用。我发现,如果我将最终示例中的字段更改为常量字符串(而不是表达式),编译器错误就会消失:STUDENT_ROLE

public enum RoleType { 
  ...
  public static final String STUDENT_ROLE = "STUDENT";
  ...
}

但是,这意味着枚举值不会在任何地方使用,因为您将在批注中使用字符串常量。

在我看来,如果你的类只包含一堆静态的最终字符串常量,你会更好。RoleType


为了理解为什么你的代码没有编译,我看了一下Java语言规范(JLS)。用于注释的 JLS 指出,对于参数类型为 T 且值为 V 的注释,

如果 T 是基元类型或 ,则 V 是常量表达式。String

常量表达式包括,除其他外,

格式为 TypeName 的限定名称。引用常量变量的标识符

量变量定义为

基元类型或类型的变量,它是最终的,并使用编译时常量表达式进行初始化String


答案 2

怎么样?

public enum RoleType {
    STUDENT(Names.STUDENT),
    TEACHER(Names.TEACHER),
    DEANERY(Names.DEANERY);

    public class Names{
        public static final String STUDENT = "Student";
        public static final String TEACHER = "Teacher";
        public static final String DEANERY = "Deanery";
    }

    private final String label;

    private RoleType(String label) {
        this.label = label;
    }

    public String toString() {
        return this.label;
    }
}

在注释中,您可以使用它,例如

@RolesAllowed(RoleType.Names.DEANERY)
public void update(User p) { ... }

一个小小的担忧是,对于任何修改,我们都需要在两个地方改变。但是由于它们位于同一文件中,因此不太可能被遗漏。作为回报,我们得到了不使用原始字符串并避免复杂机制的好处。

或者这听起来完全愚蠢?:)