Current Behavior
I am just learning Spring Security and method security and reading about @PreAuthorize and @PostAuthorize with EL. Besides EL is used In all examples I found in internet. EL is great when we use it where we can't use Java code. For example in test data in excel file. But in java class it is an absolute evil.
Expected Behavior
I would suggest to give in annotation class literal with parameters. Of course it is not very good solution but I think it is much better than strings. So, something like this:
@FunctionalInterface
public interface AuthorizePredicate { // <!-- must be declared in spring security library
boolean test(Object... args);
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@NonELPreAuthorize() // <!-- must be declared in spring security library
public @interface CustomNonELPreAuthorize {
Class<? extends AuthorizePredicate> predicateClass();
String[] args() default {};
}
public class DefaultAuthorizePredicate implements AuthorizePredicate {
@Override
public boolean test(Object... args) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
@Controller
public class FooController {
@GetMapping(value = "/bar", produces = MediaType.TEXT_HTML_VALUE)
@CustomNonELPreAuthorize(args = {"one"}, predicateClass = DefaultAuthorizePredicate.class)
public String bar(Model model) {
return "index";
}
}
Comment From: sjohnr
@PavelTurkish thanks for your interest in the project and the suggestion.
Looking at your example, I'm not 100% sure I understand your goal, but it seems you are suggesting a solution that allows for a new annotation that can reference a class and invoke a functional interface (via reflection, but without requiring SpEL), passing it method arguments. Is that correct? If so, this is a very specific request that seems more appropriate as a customization on top of Spring Security.
Custom authorization is indeed a complex topic, but I believe that what you're requesting is already possible in a variety of ways. I won't provide a full example here as the purpose of this issue is to discuss an enhancement, but you can review the documentation on using a custom AuthorizationManager to get started. The docs discuss using pointcuts to provide custom authorization with code, including (but not limited to) the ability to replace the functionality of @PreAuthorize entirely.
While not exactly specific to your use case, you can see this helpful example from a recent talk at DevNexus 2023. You could also develop a completely custom annotation and use similar configuration to apply it.
If you'd like help working through how to achieve something similar to what you're requesting in this issue, please feel free to submit a question on StackOverflow and I'd be happy to take a look.
For now, I'm going to close this issue as I don't believe the specifics of your suggestion would make a good candidate for adding directly to the framework. However, if I seem to be misunderstanding your suggestion, please let me know.
Comment From: PavelTurk
@sjohnr Thank you for your answer.
Looking at your example, I'm not 100% sure I understand your goal, but it seems you are suggesting a solution that allows for a new annotation that can reference a
classand invoke a functional interface (via reflection, but without requiring SpEL), passing it method arguments. Is that correct? If so, this is a very specific request that seems more appropriate as a customization on top of Spring Security.
Yes, you are right. It is what I mean. By other words I suggest to put all authorization logic in functional interface implementation and define all arguments for this functional interface in annotation. Compare
//Current solution
@PreAuthorize("hasRole('ROLE_VIEWER') or hasRole('ROLE_EDITOR')")
//Suggested solution
@CustomNonELPreAuthorize(args = {"ROLE_VIEWER", "ROLE_EDITOR"}, predicateClass = CheckRolesAuthorizePredicate.class)
In annotation we can use for arguments all supported types - primitive, String, Enum, Annotation, Class, an array of any of them. So, it is not so bad. But my solution provides strong type checking, it is good readable and with code refactoring it won't be the reason of the problem.
But with EL - in any middle or complex application with many developers 100% somebody will to fix the EL string.
Thank you for the links you provided. I will study them.