spring-projects / spring-security

Spring Security
http://spring.io/projects/spring-security
Apache License 2.0
8.71k stars 5.85k forks source link

Support Expressions in Method Authorization Denied Handlers #14857

Open marcusdacoregio opened 5 months ago

marcusdacoregio commented 5 months ago

We should consider supporting expressions in method authorization handlers for simple setups. Currently, if you want to handle authorization denied and map the return value to null, you must create a MethodAuthorizationDeniedHandler/PostProcessor class and expose it as a bean:

@HandleAuthorizationDenied(handlerClass = Null.class)
public String getUserEmail() {
    // ...
}

@Component
public class Null implements MethodAuthorizationDeniedHandler {
    @Override
    public Object handle(MethodInvocation methodInvocation, AuthorizationResult result) {
        return null;
    }
}

That is a little bit too complicated to just return null. A simpler setup could be:

@HandleAuthorizationDenied(handlerExpression = "null")
public String getUserEmail() {
    // ...
}

@HandleAuthorizationDenied(handlerExpression = "***")
public String getUserEmail() {
    // ...
}

Related:

marcusdacoregio commented 1 month ago

After talking with the team about this feature we are not sure that there should be an expression attribute in the annotation, doing two different things (expression and handlerClass) may be confusing.

Instead, @HandleAuthorizationDenied annotation should stay as it is and we should consider introducing new annotations for the most common fallback return values, like null or a masked String value. Meta-annotations can be used to achieve that goal, for example:

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@HandleAuthorizationDenied(handlerClass = NullMethodAuthorizationDeniedHandler.class);
public @interface NullWhenAuthorizationDenied { }

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@HandleAuthorizationDenied(handlerClass = StringMethodAuthorizationDeniedHandler.class);
public @interface StringWhenAuthorizationDenied {
    String value();
}

NullMethodAuthorizationDeniedHandler and StringMethodAuthorizationDeniedHandler would be provided by the framework, then, when resolving the handlerClass, there is no need to look for it in the ApplicationContext.

I believe that we should gather more feedback from the community before introducing those new annotations since they can be easily achieved in the current state.