Method Security in GraalVM Native Image

虽然 GraalVM Native Image 中支持 Method Security,但有些用例需要应用程序提供的附加提示。

Although Method Security is supported in GraalVM Native Image, there are some use cases that need additional hints provided by the application.

Using @PreAuthorize and @PostAuthorize Annotations

如果您有 UserDetailsAuthentication 类 自定义实现,则使用 @PreAuthorize@PostAuthorize 注释需要附加提示信息。

Using @PreAuthorize and @PostAuthorize annotations require additional hints if you have a custom implementation of UserDetails or Authentication classes.

我们举一个示例,其中您使用自定义实现 UserDetails 类如下所示,并且此实现由您的 UserDetailsService 返回:

Let’s take an example where you have a custom implementation of UserDetails class as follows and that implementation is returned by your UserDetailsService:

Custom Implementation of 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() 方法,如下所示:

And you want to use the isAdmin() method inside a @PreAuthorize annotation as follows:

Using isAdmin() to secure a method
@PreAuthorize("principal?.isAdmin()")
public String hello() {
    return "Hello!";
}

请记住,您需要将 add @EnableMethodSecurity annotation 添加到配置类,以便启用方法安全注释。

Remember that you need to add @EnableMethodSecurity annotation to your configuration class to enable method security annotations.

如果您使用以上配置 run the native image 应用程序,那么在尝试调用 hello() 方法时,您将收到类似以下内容的错误:

If you run the native image of your application with the above configuration, you will get an error similar to the following when trying to invoke the hello() method:

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 本机图像在默认情况下不支持反射。

Which means that the isAdmin() method cannot be found on the CustomUserDetails class. This is because Spring Security uses reflection to invoke the isAdmin() method and GraalVM Native Image does not support reflection by default.

要解决这个问题,你需要给 GraalVM Native Image 提供提示以允许对 CustomUserDetails#isAdmin() 方法进行反射。我们可以通过提供一个 custom hint 来做到这一点。在此示例中,我们将使用 {spring-framework-reference-url}core.html#core.aot.hints.register-reflection-for-binding[@RegisterReflectionForBinding 注释]。

To fix this issue, you need to give hints to GraalVM Native Image to allow reflection on the CustomUserDetails#isAdmin() method. We can do that by providing a custom hint. In this example we are going to use {spring-framework-reference-url}core.html#core.aot.hints.register-reflection-for-binding[the @RegisterReflectionForBinding annotation].

您可能需要注册您想在 @PreAuthorize@PostAuthorize 注解中使用的所有类。

You might need to register all your classes that you want to use in your @PreAuthorize and @PostAuthorize annotations.

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

就是这样,现在您可以运行应用程序的本机映像了,并且它应该按预期工作。

And that’s it, now you can run the native image of your application and it should work as expected.