SecuredAuthorizationManager should cache annotation's value

Closes gh-12232
This commit is contained in:
Evgeniy Cheban 2022-12-03 01:08:11 +01:00 committed by Josh Cummings
parent 18a3ff2745
commit e0d676c03f

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,14 +17,18 @@
package org.springframework.security.authorization.method; package org.springframework.security.authorization.method;
import java.lang.reflect.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 java.util.function.Supplier;
import org.aopalliance.intercept.MethodInvocation; import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils; 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.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.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
@ -39,7 +43,9 @@ import org.springframework.security.core.Authentication;
*/ */
public final class SecuredAuthorizationManager implements AuthorizationManager<MethodInvocation> { public final class SecuredAuthorizationManager implements AuthorizationManager<MethodInvocation> {
private final SecuredAuthorizationManagerRegistry registry = new SecuredAuthorizationManagerRegistry(); private final AuthoritiesAuthorizationManager delegate = new AuthoritiesAuthorizationManager();
private final Map<MethodClassKey, Set<String>> cachedAuthorities = new ConcurrentHashMap<>();
/** /**
* Determine if an {@link Authentication} has access to a method by evaluating the * Determine if an {@link Authentication} has access to a method by evaluating the
@ -51,26 +57,28 @@ public final class SecuredAuthorizationManager implements AuthorizationManager<M
*/ */
@Override @Override
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation mi) { public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation mi) {
AuthorizationManager<MethodInvocation> delegate = this.registry.getManager(mi); Set<String> authorities = getAuthorities(mi);
return delegate.check(authentication, mi); return authorities.isEmpty() ? null : this.delegate.check(authentication, authorities);
} }
private static final class SecuredAuthorizationManagerRegistry extends AbstractAuthorizationManagerRegistry { private Set<String> 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 private Set<String> resolveAuthorities(Method method, Class<?> targetClass) {
@Override Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
AuthorizationManager<MethodInvocation> resolveManager(Method method, Class<?> targetClass) { Secured secured = findSecuredAnnotation(specificMethod);
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); return (secured != null) ? Set.of(secured.value()) : Collections.emptySet();
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 Secured findSecuredAnnotation(Method method) {
Secured secured = AuthorizationAnnotationUtils.findUniqueAnnotation(method, Secured.class);
return (secured != null) ? secured
: AuthorizationAnnotationUtils.findUniqueAnnotation(method.getDeclaringClass(), Secured.class);
} }
} }