diff --git a/core/src/main/java/org/springframework/security/authorization/method/SecuredAuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/method/SecuredAuthorizationManager.java index 73ad02ce38..04938e8822 100644 --- a/core/src/main/java/org/springframework/security/authorization/method/SecuredAuthorizationManager.java +++ b/core/src/main/java/org/springframework/security/authorization/method/SecuredAuthorizationManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,14 +17,18 @@ package org.springframework.security.authorization.method; import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.support.AopUtils; -import org.springframework.lang.NonNull; +import org.springframework.core.MethodClassKey; import org.springframework.security.access.annotation.Secured; -import org.springframework.security.authorization.AuthorityAuthorizationManager; +import org.springframework.security.authorization.AuthoritiesAuthorizationManager; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.core.Authentication; @@ -39,7 +43,9 @@ import org.springframework.security.core.Authentication; */ public final class SecuredAuthorizationManager implements AuthorizationManager { - private final SecuredAuthorizationManagerRegistry registry = new SecuredAuthorizationManagerRegistry(); + private final AuthoritiesAuthorizationManager delegate = new AuthoritiesAuthorizationManager(); + + private final Map> cachedAuthorities = new ConcurrentHashMap<>(); /** * Determine if an {@link Authentication} has access to a method by evaluating the @@ -51,26 +57,28 @@ public final class SecuredAuthorizationManager implements AuthorizationManager authentication, MethodInvocation mi) { - AuthorizationManager delegate = this.registry.getManager(mi); - return delegate.check(authentication, mi); + Set authorities = getAuthorities(mi); + return authorities.isEmpty() ? null : this.delegate.check(authentication, authorities); } - private static final class SecuredAuthorizationManagerRegistry extends AbstractAuthorizationManagerRegistry { + private Set getAuthorities(MethodInvocation methodInvocation) { + Method method = methodInvocation.getMethod(); + Object target = methodInvocation.getThis(); + Class targetClass = (target != null) ? target.getClass() : null; + MethodClassKey cacheKey = new MethodClassKey(method, targetClass); + return this.cachedAuthorities.computeIfAbsent(cacheKey, (k) -> resolveAuthorities(method, targetClass)); + } - @NonNull - @Override - AuthorizationManager resolveManager(Method method, Class targetClass) { - Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); - Secured secured = findSecuredAnnotation(specificMethod); - return (secured != null) ? AuthorityAuthorizationManager.hasAnyAuthority(secured.value()) : NULL_MANAGER; - } - - private Secured findSecuredAnnotation(Method method) { - Secured secured = AuthorizationAnnotationUtils.findUniqueAnnotation(method, Secured.class); - return (secured != null) ? secured - : AuthorizationAnnotationUtils.findUniqueAnnotation(method.getDeclaringClass(), Secured.class); - } + private Set resolveAuthorities(Method method, Class targetClass) { + Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); + Secured secured = findSecuredAnnotation(specificMethod); + return (secured != null) ? Set.of(secured.value()) : Collections.emptySet(); + } + private Secured findSecuredAnnotation(Method method) { + Secured secured = AuthorizationAnnotationUtils.findUniqueAnnotation(method, Secured.class); + return (secured != null) ? secured + : AuthorizationAnnotationUtils.findUniqueAnnotation(method.getDeclaringClass(), Secured.class); } }