import java.lang.reflect.Method;
import java.util.Collection;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.annotation.SecuredAnnotationSecurityMetadataSource;
import org.springframework.security.access.method.MethodSecurityMetadataSource;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
import org.springframework.util.CollectionUtils;

@Configuration(proxyBeanMethods = false)
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {

    @Override
    protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
        // override @EnableGlobalMethodSecurity(securedEnabled=true)
        return new SecuredAnnotationSecurityMetadataSource() {

            @Override
            public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
                Collection<ConfigAttribute> attr = super.getAttributes(method, targetClass);
                if (CollectionUtils.isEmpty(attr)) {
                    attr = findAttributes(targetClass);
                }
                return attr;
            }

        };
    }

}

GlobalMethodSecurityConfiguration is deprecated since 5.8.0 as

 * @deprecated Use {@link PrePostMethodSecurityConfiguration},
 * {@link SecuredMethodSecurityConfiguration}, or
 * {@link Jsr250MethodSecurityConfiguration} instead

please elaborate how to custom MethodSecurityMetadataSource.

Comment From: jzheaux

Hi, @quaff, thanks for reaching out.

What is your custom code trying to do? It appears similar in concept to what SecuredAuthorizationManager is doing, which is wired by doing @EnableMethodSecurity(securedEnabled = true):

Comment From: quaff

@jzheaux It is a workaround of https://github.com/spring-projects/spring-security/pull/10230

Comment From: jzheaux

Thanks, @quaff.

AuthorizationManager does not have a separate API for customizing the source of security metadata. Instead, consider replacing your custom MethodSecurityMetadataSource with a custom AuthorizationManager.

In the meantime, you can achieve it by calling your MethodSecurityMetadataSource implementation like so:

public class MyAuthorizationManager implements AuthorizationManager<MethodInvocation> {
    private final MethodSecurityMetadataSource metadata = // ... custom

    @Override 
    public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation mi) {
        Collection<String> attributes = toStringList(this.metadata.getAttributes(mi));
        Collection<String> authorities = toStringList(authentication.get().getAuthorities());
        return new AuthorizationDecision(!Collections.disjoint(attributes, authorities));
    }
}

And then publishing the corresponding method interceptor. You can represent your metadata source as a pointcut in the following way:

Pointcut pointcut = new StaticMethodMatcherPointcut() {
    @Override 
    public boolean matches(Method method, Class<?> targetClass) {
        return !CollectionUtils.isEmpty(this.metadata.getAttributes(m, targetClass));
    } 
};

This process is indirectly documented in the migration guide however, I think that you are right that the documentation should speak directly to custom SecurityMetadataSource implementations.

Comment From: quaff

  1. MethodSecurityMetadataSource is deprecated also.
  2. this way is not intuitive as previous.

@jzheaux Could you consider provide a separate API in next release?

Comment From: jzheaux

  1. MethodSecurityMetadataSource is deprecated also.

Correct, the point of the above snippet was to suggest how you could adapt your existing code. Certainly, you can write an AuthenticationManager from that looks up the values itself instead of delegating to your existing component.

  1. this way is not intuitive as previous.

What parts are less intuitive? I want to make sure we are simplifying the right thing.

Could you consider provide a separate API in next release?

It's worth considering, sure. I'm inclined to see what patterns emerge first before adding more abstractions.

Comment From: quaff

What parts are less intuitive? I want to make sure we are simplifying the right thing.

Overriding method customMethodSecurityMetadataSource is more intuitive.

It's worth considering, sure. I'm inclined to see what patterns emerge first before adding more abstractions.

Thanks for your considering.