GraalVM ネイティブイメージのメソッドセキュリティ

メソッドのセキュリティは GraalVM ネイティブイメージでサポートされていますが、アプリケーションによって提供される追加のヒントが必要なユースケースがいくつかあります。

@PreAuthorize および @PostAuthorize アノテーションの使用

UserDetails クラスまたは Authentication クラスのカスタム実装がある場合、@PreAuthorize および @PostAuthorize アノテーションを使用するには追加のヒントが必要です。

次のような UserDetails クラスのカスタム実装があり、その実装が UserDetailsService によって返される例を考えてみましょう。

UserDetails のカスタム実装
public class CustomUserDetails implements UserDetails {

    private final String username;

    private final String password;

    private final Collection<? extends GrantedAuthority> authorities;

    public boolean isAdmin() {
        return this.authorities.contains(new SimpleGrantedAuthority("ROLE_ADMIN"));
    }

    // constructors, getters and setters
}

そして、次のように @PreAuthorize アノテーション内で isAdmin() メソッドを使用したいとします。

isAdmin() を使用してメソッドを保護する
@PreAuthorize("principal?.isAdmin()")
public String hello() {
    return "Hello!";
}

メソッドのセキュリティアノテーションを有効にするには、構成クラスに @EnableMethodSecurity アノテーションを追加する必要があることに注意してください。

上記の構成でアプリケーションのネイティブイメージを実行すると、hello() メソッドを呼び出そうとしたときに次のようなエラーが発生します。

failed: java.lang.IllegalArgumentException: Failed to evaluate expression 'principal?.isAdmin()' with root cause
org.springframework.expression.spel.SpelEvaluationException: EL1004E: Method call: Method isAdmin() cannot be found on type com.mypackage.CustomUserDetails

これは、isAdmin() メソッドが CustomUserDetails クラスで見つからないことを意味します。これは、Spring Security がリフレクションを使用して isAdmin() メソッドを呼び出し、GraalVM Native Image がデフォルトでリフレクションをサポートしていないためです。

この課題を解決するには、GraalVM Native Image にヒントを与えて、CustomUserDetails#isAdmin() メソッドでの反映を可能にする必要があります。これは、カスタムヒントを提供することで実現できます。この例では、@RegisterReflectionForBinding アノテーションを使用します。

@PreAuthorize および @PostAuthorize アノテーションで使用するすべてのクラスを登録することが必要になる場合があります。

@RegisterReflectionForBinding の使用
@Configuration
@RegisterReflectionForBinding(CustomUserDetails.class)
public class MyConfiguration {
    //...
}

これで、アプリケーションのネイティブイメージを実行できるようになり、期待どおりに動作するはずです。