63

私はJavaエンタープライズアプリケーションを開発しています.現在、特定の機能へのアクセスを特定のユーザーに制限するためにJava EEセキュリティを行っています. アプリケーションサーバーとすべてを構成し、現在、メソッドを保護するために RolesAllowed アノテーションを使用しています。

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

このような注釈を使用すると、正常に機能します。

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

しかし、これは私が望んでいるものではありません。ここでは文字列を使用する必要があり、リファクタリングが難しくなり、タイプミスが発生する可能性があります。したがって、文字列を使用する代わりに、この注釈のパラメーターとして 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 の値は、定数式でなければなりません`

次に試したのは、追加の最終文字列を Enum に追加することでした。

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 でこれを実行してみます。

4

5 に答える 5

37

列挙型を使用するというあなたのアプローチがうまくいくとは思いません。STUDENT_ROLE式ではなく、最後の例のフィールドを定数文字列に変更すると、コンパイラ エラーがなくなることがわかりました。

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

ただし、これは、代わりに注釈で文字列定数を使用するため、列挙値がどこでも使用されないことを意味します。

RoleTypeクラスに静的な最終文字列定数の束しか含まれていない場合は、より良い結果が得られるように思えます。


コードがコンパイルされない理由を確認するために、Java 言語仕様(JLS) を調べました。注釈の JLS は、型Tと値Vのパラメーターを持つ注釈の場合、

Tがプリミティブ型または の場合、StringV定数式です。

定数式には、とりわけ以下が含まれます。

TypeName形式の修飾名。 定数変数を参照する識別子

定数変数は次のように定義されます。

Stringfinal であり、コンパイル時の定数式で初期化されるプリミティブ型または type の変数

于 2010-07-17T19:01:29.807 に答える
11

これは、追加のインターフェースとメタアノテーションを使用したソリューションです。一連のアノテーションからロールタイプを取得するためのリフレクションを行うのに役立つユーティリティクラスと、それを少しテストするためのユーティリティクラスを含めました。

/**
 * empty interface which must be implemented by enums participating in
 * annotations of "type" @RolesAllowed.
 */
public interface RoleType {
    public String toString();
}

/** meta annotation to be applied to annotations that have enum values implementing RoleType. 
 *  the value() method should return an array of objects assignable to RoleType*.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ANNOTATION_TYPE})
public @interface RolesAllowed { 
    /* deliberately empty */ 
}

@RolesAllowed
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE, METHOD})
public @interface AcademicRolesAllowed {
    public AcademicRoleType[] value();
}

public enum AcademicRoleType implements RoleType {
    STUDENT, TEACHER, DEANERY;
    @Override
    public String toString() {
        return name();
    }
}


public class RolesAllowedUtil {

    /** get the array of allowed RoleTypes for a given class **/
    public static List<RoleType> getRoleTypesAllowedFromAnnotations(
            Annotation[] annotations) {
        List<RoleType> roleTypesAllowed = new ArrayList<RoleType>();
        for (Annotation annotation : annotations) {
            if (annotation.annotationType().isAnnotationPresent(
                    RolesAllowed.class)) {
                RoleType[] roleTypes = getRoleTypesFromAnnotation(annotation);
                if (roleTypes != null)
                    for (RoleType roleType : roleTypes)
                        roleTypesAllowed.add(roleType);
            }
        }
        return roleTypesAllowed;
    }

    public static RoleType[] getRoleTypesFromAnnotation(Annotation annotation) {
        Method[] methods = annotation.annotationType().getMethods();
        for (Method method : methods) {
            String name = method.getName();
            Class<?> returnType = method.getReturnType();
            Class<?> componentType = returnType.getComponentType();
            if (name.equals("value") && returnType.isArray()
                    && RoleType.class.isAssignableFrom(componentType)) {
                RoleType[] features;
                try {
                    features = (RoleType[]) (method.invoke(annotation,
                            new Object[] {}));
                } catch (Exception e) {
                    throw new RuntimeException(
                            "Error executing value() method in "
                                    + annotation.getClass().getCanonicalName(),
                            e);
                }
                return features;
            }
        }
        throw new RuntimeException(
                "No value() method returning a RoleType[] type "
                        + "was found in annotation "
                        + annotation.getClass().getCanonicalName());
    }

}

public class RoleTypeTest {

    @AcademicRolesAllowed({DEANERY})
    public class DeaneryDemo {

    }

    @Test
    public void testDeanery() {
        List<RoleType> roleTypes = RolesAllowedUtil.getRoleTypesAllowedFromAnnotations(DeaneryDemo.class.getAnnotations());
        assertEquals(1, roleTypes.size());
    }
}
于 2011-10-06T18:29:44.310 に答える