GraalVM Native Image 中的方法安全

儘管 方法安全 在 GraalVM Native Image 中受支援,但某些用例需要應用程式提供額外的提示。

使用 @PreAuthorize@PostAuthorize 註解

如果你有 UserDetailsAuthentication 類的自定義實現,使用 @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 註解來啟用方法安全註解。

如果你使用上述配置執行你的應用程式的 Native Image,當嘗試呼叫 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

這意味著在 CustomUserDetails 類上找不到 isAdmin() 方法。這是因為 Spring Security 使用反射來呼叫 isAdmin() 方法,而 GraalVM Native Image 預設不支援反射。

為了解決這個問題,你需要給 GraalVM Native Image 提供提示,以允許對 CustomUserDetails#isAdmin() 方法進行反射。我們可以透過提供自定義提示來實現這一點。在本例中,我們將使用@RegisterReflectionForBinding 註解

你可能需要註冊所有你想在 @PreAuthorize@PostAuthorize 註解中使用的類。

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

就是這樣,現在你可以執行你的應用程式的 Native Image,它應該按預期工作。