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 注解 以启用方法安全注解。

如果您运行应用程序的原生镜像 以及上述配置,当尝试调用 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 {
    //...
}

就是这样,现在您可以运行应用程序的原生镜像,它应该按预期工作。