Arrays of enums in security meta annotations have limited utility because there's no clean mechanism to convert the enum to a custom value.

If I have an enum that specifies my permissions, such as:

enum Permission {
  READ("permission.read"),
  WRITE("permission.write");

  private final value;

  // constructor implementation

  @Override
  public String toString() {
    return value;
  }
}

I cannot have a working meta annotation with an array of enums such as the following:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@PreAuthorize("hasAnyAuthority({permissions})")
@interface HasAnyPermission {
 Permission[] permissions();
}

The ExpressionTemplateSecurityAnnotationScanner.conversionService includes EnumToStringConverter that will always use Enum.name() instead of my overridden toString() method.

Modifying the static conversionService initializer in ExpressionTemplateSecurityAnnotationScanner to:

static {
    conversionService.addConverter(new ClassToStringConverter());
    conversionService.removeConvertible(Enum.class, String.class);
}

corrects the issue.

Since the default Enum.toString() returns the enum's name, removing the explicit Enum converter would be backwards compatible for most code. I'm sure there's a more clever solution that ensures backwards compatibility.