Use Interceptors instead of Advice

- Interceptor is a more descriptive term for what
method security is doing
- This also allows the code to follow a delegate
pattern that unifies both before-method and after-
method authorization

Issue gh-9289
This commit is contained in:
Josh Cummings
2021-04-08 14:31:36 -06:00
parent 122346bd27
commit df8abcfae7
37 changed files with 1010 additions and 1244 deletions

View File

@@ -27,28 +27,25 @@ import org.springframework.lang.NonNull;
import org.springframework.security.authorization.AuthorizationManager;
/**
* An abstract registry which provides an {@link AuthorizationManager} for the
* {@link MethodInvocation}.
* For internal use only, as this contract is likely to change
*
* @author Evgeniy Cheban
* @since 5.5
*/
abstract class AbstractAuthorizationManagerRegistry {
static final AuthorizationManager<MethodAuthorizationContext> NULL_MANAGER = (a, o) -> null;
static final AuthorizationManager<MethodInvocation> NULL_MANAGER = (a, o) -> null;
private final Map<MethodClassKey, AuthorizationManager<MethodAuthorizationContext>> cachedManagers = new ConcurrentHashMap<>();
private final Map<MethodClassKey, AuthorizationManager<MethodInvocation>> cachedManagers = new ConcurrentHashMap<>();
/**
* Returns an {@link AuthorizationManager} for the {@link MethodAuthorizationContext}.
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to use
* Returns an {@link AuthorizationManager} for the
* {@link AuthorizationMethodInvocation}.
* @param methodInvocation the {@link AuthorizationMethodInvocation} to use
* @return an {@link AuthorizationManager} to use
*/
final AuthorizationManager<MethodAuthorizationContext> getManager(
MethodAuthorizationContext methodAuthorizationContext) {
MethodInvocation methodInvocation = methodAuthorizationContext.getMethodInvocation();
final AuthorizationManager<MethodInvocation> getManager(AuthorizationMethodInvocation methodInvocation) {
Method method = methodInvocation.getMethod();
Class<?> targetClass = methodAuthorizationContext.getTargetClass();
Class<?> targetClass = methodInvocation.getTargetClass();
MethodClassKey cacheKey = new MethodClassKey(method, targetClass);
return this.cachedManagers.computeIfAbsent(cacheKey, (k) -> resolveManager(method, targetClass));
}
@@ -61,6 +58,6 @@ abstract class AbstractAuthorizationManagerRegistry {
* @return the non-null {@link AuthorizationManager}
*/
@NonNull
abstract AuthorizationManager<MethodAuthorizationContext> resolveManager(Method method, Class<?> targetClass);
abstract AuthorizationManager<MethodInvocation> resolveManager(Method method, Class<?> targetClass);
}

View File

@@ -20,31 +20,27 @@ import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.core.MethodClassKey;
import org.springframework.lang.NonNull;
/**
* An abstract registry which provides an {@link ExpressionAttribute} for the
* {@link MethodInvocation}.
* For internal use only, as this contract is likely to change
*
* @author Evgeniy Cheban
* @since 5.5
*/
abstract class AbstractExpressionAttributeRegistry<T extends ExpressionAttribute> {
private final Map<MethodClassKey, T> cachedAttributes = new ConcurrentHashMap<>();
/**
* Returns an {@link ExpressionAttribute} for the {@link MethodAuthorizationContext}.
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to use
* Returns an {@link ExpressionAttribute} for the
* {@link AuthorizationMethodInvocation}.
* @param mi the {@link AuthorizationMethodInvocation} to use
* @return the {@link ExpressionAttribute} to use
*/
final T getAttribute(MethodAuthorizationContext methodAuthorizationContext) {
MethodInvocation methodInvocation = methodAuthorizationContext.getMethodInvocation();
Method method = methodInvocation.getMethod();
Class<?> targetClass = methodAuthorizationContext.getTargetClass();
final T getAttribute(AuthorizationMethodInvocation mi) {
Method method = mi.getMethod();
Class<?> targetClass = mi.getTargetClass();
return getAttribute(method, targetClass);
}

View File

@@ -18,7 +18,8 @@ package org.springframework.security.authorization.method;
import java.util.function.Supplier;
import org.springframework.aop.MethodMatcher;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Pointcut;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authorization.AuthorizationManager;
@@ -26,28 +27,27 @@ import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationMethodAfterAdvice} which can determine if an
* {@link Authentication} has access to the {@link T} object using an
* {@link AuthorizationManager} if a {@link MethodMatcher} matches.
* An {@link AuthorizationMethodInterceptor} which can determine if an
* {@link Authentication} has access to the result of an {@link MethodInvocation} using an
* {@link AuthorizationManager}
*
* @param <T> the type of object that the authorization check is being done one.
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.5
*/
public final class AuthorizationManagerMethodAfterAdvice<T> implements AuthorizationMethodAfterAdvice<T> {
public final class AuthorizationManagerAfterMethodInterceptor implements AuthorizationMethodInterceptor {
private final Pointcut pointcut;
private final AfterMethodAuthorizationManager<T> authorizationManager;
private final AfterMethodAuthorizationManager<MethodInvocation> authorizationManager;
/**
* Creates an instance.
* @param pointcut the {@link Pointcut} to use
* @param authorizationManager the {@link AuthorizationManager} to use
*/
public AuthorizationManagerMethodAfterAdvice(Pointcut pointcut,
AfterMethodAuthorizationManager<T> authorizationManager) {
public AuthorizationManagerAfterMethodInterceptor(Pointcut pointcut,
AfterMethodAuthorizationManager<MethodInvocation> authorizationManager) {
Assert.notNull(pointcut, "pointcut cannot be null");
Assert.notNull(authorizationManager, "authorizationManager cannot be null");
this.pointcut = pointcut;
@@ -55,16 +55,17 @@ public final class AuthorizationManagerMethodAfterAdvice<T> implements Authoriza
}
/**
* Determine if an {@link Authentication} has access to the {@link T} object using the
* {@link AuthorizationManager}.
* Determine if an {@link Authentication} has access to the {@link MethodInvocation}
* using the {@link AuthorizationManager}.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param object the {@link T} object to check
* @param mi the {@link MethodInvocation} to check
* @throws AccessDeniedException if access is not granted
*/
@Override
public Object after(Supplier<Authentication> authentication, T context, Object object) {
this.authorizationManager.verify(authentication, context, object);
return object;
public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable {
Object result = mi.proceed();
this.authorizationManager.verify(authentication, mi, result);
return result;
}
/**

View File

@@ -18,7 +18,8 @@ package org.springframework.security.authorization.method;
import java.util.function.Supplier;
import org.springframework.aop.MethodMatcher;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Pointcut;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authorization.AuthorizationManager;
@@ -26,27 +27,26 @@ import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationMethodBeforeAdvice} which can determine if an
* {@link Authentication} has access to the {@link T} object using an
* {@link AuthorizationManager} if a {@link MethodMatcher} matches.
* An {@link AuthorizationMethodInterceptor} which uses a {@link AuthorizationManager} to
* determine if an {@link Authentication} may invoke the given {@link MethodInvocation}
*
* @param <T> the type of object that the authorization check is being done one.
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.5
*/
public final class AuthorizationManagerMethodBeforeAdvice<T> implements AuthorizationMethodBeforeAdvice<T> {
public final class AuthorizationManagerBeforeMethodInterceptor implements AuthorizationMethodInterceptor {
private final Pointcut pointcut;
private final AuthorizationManager<T> authorizationManager;
private final AuthorizationManager<MethodInvocation> authorizationManager;
/**
* Creates an instance.
* @param pointcut the {@link Pointcut} to use
* @param authorizationManager the {@link AuthorizationManager} to use
*/
public AuthorizationManagerMethodBeforeAdvice(Pointcut pointcut, AuthorizationManager<T> authorizationManager) {
public AuthorizationManagerBeforeMethodInterceptor(Pointcut pointcut,
AuthorizationManager<MethodInvocation> authorizationManager) {
Assert.notNull(pointcut, "pointcut cannot be null");
Assert.notNull(authorizationManager, "authorizationManager cannot be null");
this.pointcut = pointcut;
@@ -54,15 +54,16 @@ public final class AuthorizationManagerMethodBeforeAdvice<T> implements Authoriz
}
/**
* Determine if an {@link Authentication} has access to the {@link T} object using the
* configured {@link AuthorizationManager}.
* Determine if an {@link Authentication} has access to the {@link MethodInvocation}
* using the configured {@link AuthorizationManager}.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param object the {@link T} object to check
* @param mi the {@link MethodInvocation} to check
* @throws AccessDeniedException if access is not granted
*/
@Override
public void before(Supplier<Authentication> authentication, T object) {
this.authorizationManager.verify(authentication, object);
public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable {
this.authorizationManager.verify(authentication, mi);
return mi.proceed();
}
/**

View File

@@ -1,70 +0,0 @@
/*
* Copyright 2002-2021 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.authorization.method;
import java.util.function.Supplier;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterAdvice;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.security.core.Authentication;
/**
* An {@link Advice} which can determine if an {@link Authentication} has access to the
* returned object from the {@link MethodInvocation}. {@link #getPointcut()} describes
* when the advice applies for the method.
*
* @param <T> the type of object that the authorization check is being done one.
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.5
*/
public interface AuthorizationMethodAfterAdvice<T> extends AfterAdvice, PointcutAdvisor, AopInfrastructureBean {
/**
* {@inheritDoc}
*/
@Override
default boolean isPerInstance() {
return true;
}
/**
* {@inheritDoc}
*/
@Override
default Advice getAdvice() {
return this;
}
/**
* Determine if an {@link Authentication} has access to a method invocation's return
* object.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param object the {@link T} object to check
* @param returnedObject the returned object from the method invocation to check
* @return the {@code Object} that will ultimately be returned to the caller (if an
* implementation does not wish to modify the object to be returned to the caller, the
* implementation should simply return the same object it was passed by the
* {@code returnedObject} method argument)
*/
Object after(Supplier<Authentication> authentication, T object, Object returnedObject);
}

View File

@@ -1,62 +0,0 @@
/*
* Copyright 2002-2021 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.authorization.method;
import java.util.function.Supplier;
import org.aopalliance.aop.Advice;
import org.springframework.aop.BeforeAdvice;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.security.core.Authentication;
/**
* An {@link Advice} which can determine if an {@link Authentication} has access to the
* {@link T} object. {@link #getPointcut()} describes when the advice applies.
*
* @param <T> the type of object that the authorization check is being done one.
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.5
*/
public interface AuthorizationMethodBeforeAdvice<T> extends BeforeAdvice, PointcutAdvisor, AopInfrastructureBean {
/**
* {@inheritDoc}
*/
@Override
default boolean isPerInstance() {
return true;
}
/**
* {@inheritDoc}
*/
@Override
default Advice getAdvice() {
return this;
}
/**
* Determine if an {@link Authentication} has access to the {@link T} object.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param object the {@link T} object to check
*/
void before(Supplier<Authentication> authentication, T object);
}

View File

@@ -16,65 +16,70 @@
package org.springframework.security.authorization.method;
import java.util.function.Supplier;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils;
import org.springframework.lang.NonNull;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* Provides security interception of AOP Alliance based method invocations.
* A {@link MethodInterceptor} which can determine if an {@link Authentication} has access
* to the {@link MethodInvocation}. {@link #getPointcut()} describes when the interceptor
* applies.
*
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.5
*/
public final class AuthorizationMethodInterceptor implements MethodInterceptor {
private final AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> beforeAdvice;
private final AuthorizationMethodAfterAdvice<MethodAuthorizationContext> afterAdvice;
public interface AuthorizationMethodInterceptor extends MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {
/**
* Creates an instance.
* @param beforeAdvice the {@link AuthorizationMethodBeforeAdvice} to use
* @param afterAdvice the {@link AuthorizationMethodAfterAdvice} to use
*/
public AuthorizationMethodInterceptor(AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> beforeAdvice,
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> afterAdvice) {
this.beforeAdvice = beforeAdvice;
this.afterAdvice = afterAdvice;
}
/**
* Enforce security on this {@link MethodInvocation}.
* @param mi the method being invoked which requires a security decision
* @return the returned value from the {@link MethodInvocation}, possibly altered by
* the configured {@link AuthorizationMethodAfterAdvice}
* {@inheritDoc}
*/
@Override
public Object invoke(@NonNull MethodInvocation mi) throws Throwable {
MethodAuthorizationContext methodAuthorizationContext = getMethodAuthorizationContext(mi);
this.beforeAdvice.before(this::getAuthentication, methodAuthorizationContext);
Object returnedObject = mi.proceed();
return this.afterAdvice.after(this::getAuthentication, methodAuthorizationContext, returnedObject);
default Advice getAdvice() {
return this;
}
private MethodAuthorizationContext getMethodAuthorizationContext(MethodInvocation mi) {
Object target = mi.getThis();
Class<?> targetClass = (target != null) ? AopUtils.getTargetClass(target) : null;
return new MethodAuthorizationContext(mi, targetClass);
/**
* {@inheritDoc}
*/
@Override
default boolean isPerInstance() {
return true;
}
private Authentication getAuthentication() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
throw new AuthenticationCredentialsNotFoundException(
"An Authentication object was not found in the SecurityContext");
}
return authentication;
/**
* Determine if an {@link Authentication} has access to the {@link MethodInvocation}
* @param mi the {@link MethodInvocation} to intercept and potentially invoke
* @return the result of the method invocation
* @throws Throwable if the interceptor or the target object throws an exception
*/
default Object invoke(MethodInvocation mi) throws Throwable {
Supplier<Authentication> supplier = () -> {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
throw new AuthenticationCredentialsNotFoundException(
"An Authentication object was not found in the SecurityContext");
}
return authentication;
};
return invoke(supplier, new AuthorizationMethodInvocation(supplier, mi));
}
/**
* Determine if an {@link Authentication} has access to the {@link MethodInvocation}
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param mi the {@link MethodInvocation} to intercept and potentially invoke
* @return the result of the method invocation
* @throws Throwable if the interceptor or the target object throws an exception
*/
Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable;
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2002-2021 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.authorization.method;
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;
/**
* A static factory for constructing common {@link AuthorizationMethodInterceptor}s
*
* @author Josh Cummings
* @since 5.5
* @see PreAuthorizeAuthorizationManager
* @see PostAuthorizeAuthorizationManager
* @see SecuredAuthorizationManager
* @see Jsr250AuthorizationManager
*/
public final class AuthorizationMethodInterceptors {
public static AuthorizationMethodInterceptor preAuthorize() {
return preAuthorize(new PreAuthorizeAuthorizationManager());
}
public static AuthorizationMethodInterceptor preAuthorize(PreAuthorizeAuthorizationManager manager) {
return new AuthorizationManagerBeforeMethodInterceptor(
AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class), manager);
}
public static AuthorizationMethodInterceptor postAuthorize() {
return postAuthorize(new PostAuthorizeAuthorizationManager());
}
public static AuthorizationMethodInterceptor postAuthorize(PostAuthorizeAuthorizationManager manager) {
return new AuthorizationManagerAfterMethodInterceptor(
AuthorizationMethodPointcuts.forAnnotations(PostAuthorize.class), manager);
}
public static AuthorizationMethodInterceptor secured() {
return secured(new SecuredAuthorizationManager());
}
public static AuthorizationMethodInterceptor secured(SecuredAuthorizationManager manager) {
return new AuthorizationManagerBeforeMethodInterceptor(
AuthorizationMethodPointcuts.forAnnotations(Secured.class), manager);
}
public static AuthorizationMethodInterceptor jsr250() {
return jsr250(new Jsr250AuthorizationManager());
}
public static AuthorizationMethodInterceptor jsr250(Jsr250AuthorizationManager manager) {
return new AuthorizationManagerBeforeMethodInterceptor(
AuthorizationMethodPointcuts.forAnnotations(DenyAll.class, PermitAll.class, RolesAllowed.class),
manager);
}
private AuthorizationMethodInterceptors() {
}
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright 2002-2021 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.authorization.method;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.log.LogMessage;
import org.springframework.security.core.Authentication;
/**
* @author Josh Cummings
*/
class AuthorizationMethodInvocation implements MethodInvocation {
private final Log logger = LogFactory.getLog(getClass());
private final Supplier<Authentication> authentication;
private final MethodInvocation methodInvocation;
private final Class<?> targetClass;
private final List<AuthorizationMethodInterceptor> interceptors;
private final int size;
private int currentPosition = 0;
AuthorizationMethodInvocation(Supplier<Authentication> authentication, MethodInvocation methodInvocation) {
this(authentication, methodInvocation, Collections.emptyList());
}
AuthorizationMethodInvocation(Supplier<Authentication> authentication, MethodInvocation methodInvocation,
List<AuthorizationMethodInterceptor> interceptors) {
this.authentication = authentication;
this.methodInvocation = methodInvocation;
this.interceptors = interceptors;
Object target = methodInvocation.getThis();
this.targetClass = (target != null) ? AopUtils.getTargetClass(target) : null;
this.size = interceptors.size();
}
@Override
public Method getMethod() {
return this.methodInvocation.getMethod();
}
@Override
public Object[] getArguments() {
return this.methodInvocation.getArguments();
}
/**
* Return the target class.
* @return the target class
*/
Class<?> getTargetClass() {
return this.targetClass;
}
@Override
public Object proceed() throws Throwable {
if (this.currentPosition == this.size) {
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.of(() -> "Pre-Authorized " + this.methodInvocation.getMethod()));
}
return this.methodInvocation.proceed();
}
AuthorizationMethodInterceptor interceptor = this.interceptors.get(this.currentPosition);
this.currentPosition++;
Pointcut pointcut = interceptor.getPointcut();
if (!pointcut.getClassFilter().matches(getTargetClass())) {
return proceed();
}
if (!pointcut.getMethodMatcher().matches(getMethod(), getTargetClass())) {
return proceed();
}
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.format("Applying %s (%d/%d)", interceptor.getClass().getSimpleName(),
this.currentPosition, this.size));
}
Object result = interceptor.invoke(this.authentication, this);
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.of(() -> "Post-Authorized " + this.methodInvocation.getMethod()));
}
return result;
}
@Override
public Object getThis() {
return this.methodInvocation.getThis();
}
@Override
public AccessibleObject getStaticPart() {
return this.methodInvocation.getStaticPart();
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2002-2021 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.authorization.method;
import java.lang.annotation.Annotation;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.Pointcuts;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
/**
* @author Josh Cummings
*/
final class AuthorizationMethodPointcuts {
@SafeVarargs
static Pointcut forAnnotations(Class<? extends Annotation>... annotations) {
ComposablePointcut pointcut = null;
for (Class<? extends Annotation> annotation : annotations) {
if (pointcut == null) {
pointcut = new ComposablePointcut(classOrMethod(annotation));
}
else {
pointcut.union(classOrMethod(annotation));
}
}
return pointcut;
}
private static Pointcut classOrMethod(Class<? extends Annotation> annotation) {
return Pointcuts.union(new AnnotationMatchingPointcut(null, annotation, true),
new AnnotationMatchingPointcut(annotation, true));
}
private AuthorizationMethodPointcuts() {
}
}

View File

@@ -1,106 +0,0 @@
/*
* Copyright 2002-2021 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.authorization.method;
import java.util.List;
import java.util.function.Supplier;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.core.log.LogMessage;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationMethodAfterAdvice} which delegates to specific
* {@link AuthorizationMethodAfterAdvice}s and returns the result (possibly modified) from
* the {@link MethodInvocation}.
*
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.5
*/
public final class DelegatingAuthorizationMethodAfterAdvice<T> implements AuthorizationMethodAfterAdvice<T> {
private final Log logger = LogFactory.getLog(getClass());
private final Pointcut pointcut;
private final List<AuthorizationMethodAfterAdvice<T>> delegates;
/**
* Creates an instance.
* @param delegates the {@link AuthorizationMethodAfterAdvice}s to use
*/
public DelegatingAuthorizationMethodAfterAdvice(List<AuthorizationMethodAfterAdvice<T>> delegates) {
Assert.notEmpty(delegates, "delegates cannot be empty");
this.delegates = delegates;
ComposablePointcut pointcut = null;
for (AuthorizationMethodAfterAdvice<?> advice : delegates) {
if (pointcut == null) {
pointcut = new ComposablePointcut(advice.getPointcut());
}
else {
pointcut.union(advice.getPointcut());
}
}
this.pointcut = pointcut;
}
/**
* {@inheritDoc}
*/
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
/**
* Delegate to a series of {@link AuthorizationMethodAfterAdvice}s, each of which may
* replace the {@code returnedObject} with its own
*
* Advices may be of type {@link AuthorizationManagerMethodAfterAdvice} in which case,
* they will throw an
* {@link org.springframework.security.access.AccessDeniedException} in the event that
* they deny access to the {@code returnedObject}.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param object the {@link MethodAuthorizationContext} to check
* @param returnedObject the returned object from the original method invocation
* @throws org.springframework.security.access.AccessDeniedException if any delegate
* advices deny access
*/
@Override
public Object after(Supplier<Authentication> authentication, T object, Object returnedObject) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.format("Post Authorizing %s from %s", returnedObject, object));
}
Object result = returnedObject;
for (AuthorizationMethodAfterAdvice<T> delegate : this.delegates) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(
LogMessage.format("Checking authorization on %s from %s using %s", result, object, delegate));
}
result = delegate.after(authentication, object, result);
}
return result;
}
}

View File

@@ -1,101 +0,0 @@
/*
* Copyright 2002-2021 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.authorization.method;
import java.util.List;
import java.util.function.Supplier;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.core.log.LogMessage;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationMethodBeforeAdvice} which delegates to a specific
* {@link AuthorizationMethodBeforeAdvice} and grants access if all
* {@link AuthorizationMethodBeforeAdvice}s granted or abstained. Denies access only if
* one of the {@link AuthorizationMethodBeforeAdvice}s denied.
*
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.5
*/
public final class DelegatingAuthorizationMethodBeforeAdvice<T> implements AuthorizationMethodBeforeAdvice<T> {
private final Log logger = LogFactory.getLog(getClass());
private final Pointcut pointcut;
private final List<AuthorizationMethodBeforeAdvice<T>> delegates;
/**
* Creates an instance.
* @param delegates the {@link AuthorizationMethodBeforeAdvice}s to use
*/
public DelegatingAuthorizationMethodBeforeAdvice(List<AuthorizationMethodBeforeAdvice<T>> delegates) {
Assert.notEmpty(delegates, "delegates cannot be empty");
this.delegates = delegates;
ComposablePointcut pointcut = null;
for (AuthorizationMethodBeforeAdvice<?> advice : delegates) {
if (pointcut == null) {
pointcut = new ComposablePointcut(advice.getPointcut());
}
else {
pointcut.union(advice.getPointcut());
}
}
this.pointcut = pointcut;
}
/**
* {@inheritDoc}
*/
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
/**
* Delegate to a series of {@link AuthorizationMethodBeforeAdvice}s
*
* Advices may be of type {@link AuthorizationManagerMethodBeforeAdvice} in which
* case, they will throw an
* {@link org.springframework.security.access.AccessDeniedException} in the event that
* they deny access.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param object the {@link MethodAuthorizationContext} to check
* @throws org.springframework.security.access.AccessDeniedException if any delegate
* advices deny access
*/
@Override
public void before(Supplier<Authentication> authentication, T object) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.format("Pre Authorizing %s", object));
}
for (AuthorizationMethodBeforeAdvice<T> delegate : this.delegates) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.format("Checking authorization on %s using %s", object, delegate));
}
delegate.before(authentication, object);
}
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright 2002-2021 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.authorization.method;
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.security.core.Authentication;
/**
* Provides security interception of AOP Alliance based method invocations.
*
* Delegates to a collection of {@link AuthorizationMethodInterceptor}s
*
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.5
*/
public final class DelegatingAuthorizationMethodInterceptor implements AuthorizationMethodInterceptor {
private final List<AuthorizationMethodInterceptor> interceptors;
private final Pointcut pointcut;
/**
* Creates an instance using the provided parameters
* @param interceptors the delegate {@link AuthorizationMethodInterceptor}s to use
*/
public DelegatingAuthorizationMethodInterceptor(AuthorizationMethodInterceptor... interceptors) {
this(Arrays.asList(interceptors));
}
/**
* Creates an instance using the provided parameters
* @param interceptors the delegate {@link AuthorizationMethodInterceptor}s to use
*/
public DelegatingAuthorizationMethodInterceptor(List<AuthorizationMethodInterceptor> interceptors) {
ComposablePointcut pointcut = null;
for (AuthorizationMethodInterceptor interceptor : interceptors) {
if (pointcut == null) {
pointcut = new ComposablePointcut(interceptor.getPointcut());
}
else {
pointcut.union(interceptor.getPointcut());
}
}
this.pointcut = pointcut;
this.interceptors = interceptors;
}
/**
* Enforce security on this {@link MethodInvocation}.
* @param mi the method being invoked which requires a security decision
* @return the returned value from the {@link MethodInvocation}, possibly altered by
* the configured {@link AuthorizationMethodInterceptor}s
*/
@Override
public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable {
return new AuthorizationMethodInvocation(authentication, mi, this.interceptors).proceed();
}
/**
* {@inheritDoc}
*/
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
}

View File

@@ -39,14 +39,14 @@ import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationManager} which can determine if an {@link Authentication} has
* access to the {@link MethodInvocation} by evaluating if the {@link Authentication}
* An {@link AuthorizationManager} which can determine if an {@link Authentication} may
* invoke the {@link MethodInvocation} by evaluating if the {@link Authentication}
* contains a specified authority from the JSR-250 security annotations.
*
* @author Evgeniy Cheban
* @since 5.5
*/
public final class Jsr250AuthorizationManager implements AuthorizationManager<MethodAuthorizationContext> {
public final class Jsr250AuthorizationManager implements AuthorizationManager<MethodInvocation> {
private static final Set<Class<? extends Annotation>> JSR250_ANNOTATIONS = new HashSet<>();
@@ -72,25 +72,24 @@ public final class Jsr250AuthorizationManager implements AuthorizationManager<Me
/**
* Determine if an {@link Authentication} has access to a method by evaluating the
* {@link DenyAll}, {@link PermitAll}, and {@link RolesAllowed} annotations that
* {@link MethodAuthorizationContext} specifies.
* {@link AuthorizationMethodInvocation} specifies.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
* @param methodInvocation the {@link AuthorizationMethodInvocation} to check
* @return an {@link AuthorizationDecision} or null if the JSR-250 security
* annotations is not present
*/
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication,
MethodAuthorizationContext methodAuthorizationContext) {
AuthorizationManager<MethodAuthorizationContext> delegate = this.registry
.getManager(methodAuthorizationContext);
return delegate.check(authentication, methodAuthorizationContext);
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation methodInvocation) {
AuthorizationManager<MethodInvocation> delegate = this.registry
.getManager((AuthorizationMethodInvocation) methodInvocation);
return delegate.check(authentication, methodInvocation);
}
private final class Jsr250AuthorizationManagerRegistry extends AbstractAuthorizationManagerRegistry {
@NonNull
@Override
AuthorizationManager<MethodAuthorizationContext> resolveManager(Method method, Class<?> targetClass) {
AuthorizationManager<MethodInvocation> resolveManager(Method method, Class<?> targetClass) {
for (Annotation annotation : findJsr250Annotations(method, targetClass)) {
if (annotation instanceof DenyAll) {
return (a, o) -> new AuthorizationDecision(false);

View File

@@ -1,66 +0,0 @@
/*
* Copyright 2002-2021 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.authorization.method;
import org.aopalliance.intercept.MethodInvocation;
/**
* An authorization context which is holds the {@link MethodInvocation} and the target
* class
*
* @author Evgeniy Cheban
* @since 5.5
*/
public final class MethodAuthorizationContext {
private final MethodInvocation methodInvocation;
private final Class<?> targetClass;
/**
* Creates an instance.
* @param methodInvocation the {@link MethodInvocation} to use
* @param targetClass the target class to use
*/
public MethodAuthorizationContext(MethodInvocation methodInvocation, Class<?> targetClass) {
this.methodInvocation = methodInvocation;
this.targetClass = targetClass;
}
/**
* Return the {@link MethodInvocation}.
* @return the {@link MethodInvocation}
*/
public MethodInvocation getMethodInvocation() {
return this.methodInvocation;
}
/**
* Return the target class.
* @return the target class
*/
public Class<?> getTargetClass() {
return this.targetClass;
}
@Override
public String toString() {
return "MethodAuthorizationContext[methodInvocation=" + this.methodInvocation + ", targetClass="
+ this.targetClass + ']';
}
}

View File

@@ -36,22 +36,21 @@ import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationManager} which can determine if an {@link Authentication} has
* access to the {@link MethodInvocation} by evaluating an expression from the
* {@link PostAuthorize} annotation.
* An {@link AuthorizationManager} which can determine if an {@link Authentication} may
* return the result from an invoked {@link MethodInvocation} by evaluating an expression
* from the {@link PostAuthorize} annotation.
*
* @author Evgeniy Cheban
* @since 5.5
*/
public final class PostAuthorizeAuthorizationManager
implements AfterMethodAuthorizationManager<MethodAuthorizationContext> {
public final class PostAuthorizeAuthorizationManager implements AfterMethodAuthorizationManager<MethodInvocation> {
private final PostAuthorizeExpressionAttributeRegistry registry = new PostAuthorizeExpressionAttributeRegistry();
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
/**
* Sets the {@link MethodSecurityExpressionHandler}.
* Use this the {@link MethodSecurityExpressionHandler}.
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use
*/
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
@@ -62,22 +61,21 @@ public final class PostAuthorizeAuthorizationManager
/**
* Determine if an {@link Authentication} has access to the returned object by
* evaluating the {@link PostAuthorize} annotation that the
* {@link MethodAuthorizationContext} specifies.
* {@link AuthorizationMethodInvocation} specifies.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
* @param mi the {@link AuthorizationMethodInvocation} to check
* @param returnedObject the returned object to check
* @return an {@link AuthorizationDecision} or {@code null} if the
* {@link PostAuthorize} annotation is not present
*/
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication,
MethodAuthorizationContext methodAuthorizationContext, Object returnedObject) {
ExpressionAttribute attribute = this.registry.getAttribute(methodAuthorizationContext);
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation mi,
Object returnedObject) {
ExpressionAttribute attribute = this.registry.getAttribute((AuthorizationMethodInvocation) mi);
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
return null;
}
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(),
methodAuthorizationContext.getMethodInvocation());
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi);
this.expressionHandler.setReturnObject(returnedObject, ctx);
boolean granted = ExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx);
return new AuthorizationDecision(granted);

View File

@@ -34,16 +34,15 @@ import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationMethodAfterAdvice} which filters a <code>returnedObject</code>
* from the {@link MethodInvocation} by evaluating an expression from the
* {@link PostFilter} annotation.
* An {@link AuthorizationMethodInterceptor} which filters a {@code returnedObject} from
* the {@link MethodInvocation} by evaluating an expression from the {@link PostFilter}
* annotation.
*
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.5
*/
public final class PostFilterAuthorizationMethodAfterAdvice
implements AuthorizationMethodAfterAdvice<MethodAuthorizationContext> {
public final class PostFilterAuthorizationMethodInterceptor implements AuthorizationMethodInterceptor {
private final PostFilterExpressionAttributeRegistry registry = new PostFilterExpressionAttributeRegistry();
@@ -52,16 +51,15 @@ public final class PostFilterAuthorizationMethodAfterAdvice
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
/**
* Create a {@link PostFilterAuthorizationMethodAfterAdvice} using the provided
* Creates a {@link PostFilterAuthorizationMethodInterceptor} using the provided
* parameters
* @param pointcut the {@link Pointcut} for when this advice applies
*/
public PostFilterAuthorizationMethodAfterAdvice(Pointcut pointcut) {
this.pointcut = pointcut;
public PostFilterAuthorizationMethodInterceptor() {
this.pointcut = AuthorizationMethodPointcuts.forAnnotations(PostFilter.class);
}
/**
* Sets the {@link MethodSecurityExpressionHandler}.
* Use this {@link MethodSecurityExpressionHandler}.
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use
*/
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
@@ -79,24 +77,19 @@ public final class PostFilterAuthorizationMethodAfterAdvice
/**
* Filter a {@code returnedObject} using the {@link PostFilter} annotation that the
* {@link MethodAuthorizationContext} specifies.
* {@link AuthorizationMethodInvocation} specifies.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
* check
* @param mi the {@link AuthorizationMethodInvocation} to check check
* @return filtered {@code returnedObject}
*/
@Override
public Object after(Supplier<Authentication> authentication, MethodAuthorizationContext methodAuthorizationContext,
Object returnedObject) {
if (returnedObject == null) {
return null;
}
ExpressionAttribute attribute = this.registry.getAttribute(methodAuthorizationContext);
public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable {
Object returnedObject = mi.proceed();
ExpressionAttribute attribute = this.registry.getAttribute((AuthorizationMethodInvocation) mi);
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
return returnedObject;
}
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(),
methodAuthorizationContext.getMethodInvocation());
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi);
return this.expressionHandler.filter(returnedObject, attribute.getExpression(), ctx);
}
@@ -111,7 +104,7 @@ public final class PostFilterAuthorizationMethodAfterAdvice
if (postFilter == null) {
return ExpressionAttribute.NULL_ATTRIBUTE;
}
Expression postFilterExpression = PostFilterAuthorizationMethodAfterAdvice.this.expressionHandler
Expression postFilterExpression = PostFilterAuthorizationMethodInterceptor.this.expressionHandler
.getExpressionParser().parseExpression(postFilter.value());
return new ExpressionAttribute(postFilterExpression);
}

View File

@@ -36,14 +36,14 @@ import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationManager} which can determine if an {@link Authentication} has
* access to the {@link MethodInvocation} by evaluating an expression from the
* An {@link AuthorizationManager} which can determine if an {@link Authentication} may
* invoke the {@link MethodInvocation} by evaluating an expression from the
* {@link PreAuthorize} annotation.
*
* @author Evgeniy Cheban
* @since 5.5
*/
public final class PreAuthorizeAuthorizationManager implements AuthorizationManager<MethodAuthorizationContext> {
public final class PreAuthorizeAuthorizationManager implements AuthorizationManager<MethodInvocation> {
private final PreAuthorizeExpressionAttributeRegistry registry = new PreAuthorizeExpressionAttributeRegistry();
@@ -61,21 +61,19 @@ public final class PreAuthorizeAuthorizationManager implements AuthorizationMana
/**
* Determine if an {@link Authentication} has access to a method by evaluating an
* expression from the {@link PreAuthorize} annotation that the
* {@link MethodAuthorizationContext} specifies.
* {@link AuthorizationMethodInvocation} specifies.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
* @param mi the {@link AuthorizationMethodInvocation} to check
* @return an {@link AuthorizationDecision} or {@code null} if the
* {@link PreAuthorize} annotation is not present
*/
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication,
MethodAuthorizationContext methodAuthorizationContext) {
ExpressionAttribute attribute = this.registry.getAttribute(methodAuthorizationContext);
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation mi) {
ExpressionAttribute attribute = this.registry.getAttribute((AuthorizationMethodInvocation) mi);
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
return null;
}
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(),
methodAuthorizationContext.getMethodInvocation());
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi);
boolean granted = ExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx);
return new AuthorizationDecision(granted);
}

View File

@@ -35,15 +35,14 @@ import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* An {@link AuthorizationMethodBeforeAdvice} which filters a method argument by
* evaluating an expression from the {@link PreFilter} annotation.
* An {@link AuthorizationMethodInterceptor} which filters a method argument by evaluating
* an expression from the {@link PreFilter} annotation.
*
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.5
*/
public final class PreFilterAuthorizationMethodBeforeAdvice
implements AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> {
public final class PreFilterAuthorizationMethodInterceptor implements AuthorizationMethodInterceptor {
private final PreFilterExpressionAttributeRegistry registry = new PreFilterExpressionAttributeRegistry();
@@ -52,12 +51,11 @@ public final class PreFilterAuthorizationMethodBeforeAdvice
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
/**
* Creates a {@link PreFilterAuthorizationMethodBeforeAdvice} using the provided
* Creates a {@link PreFilterAuthorizationMethodInterceptor} using the provided
* parameters
* @param pointcut the {@link Pointcut} for when this advice applies
*/
public PreFilterAuthorizationMethodBeforeAdvice(Pointcut pointcut) {
this.pointcut = pointcut;
public PreFilterAuthorizationMethodInterceptor() {
this.pointcut = AuthorizationMethodPointcuts.forAnnotations(PreFilter.class);
}
/**
@@ -79,20 +77,20 @@ public final class PreFilterAuthorizationMethodBeforeAdvice
/**
* Filter the method argument specified in the {@link PreFilter} annotation that
* {@link MethodAuthorizationContext} specifies.
* {@link AuthorizationMethodInvocation} specifies.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
* @param mi the {@link AuthorizationMethodInvocation} to check
*/
@Override
public void before(Supplier<Authentication> authentication, MethodAuthorizationContext methodAuthorizationContext) {
PreFilterExpressionAttribute attribute = this.registry.getAttribute(methodAuthorizationContext);
public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable {
PreFilterExpressionAttribute attribute = this.registry.getAttribute((AuthorizationMethodInvocation) mi);
if (attribute == PreFilterExpressionAttribute.NULL_ATTRIBUTE) {
return;
return mi.proceed();
}
MethodInvocation mi = methodAuthorizationContext.getMethodInvocation();
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi);
Object filterTarget = findFilterTarget(attribute.filterTarget, ctx, mi);
this.expressionHandler.filter(filterTarget, attribute.getExpression(), ctx);
return mi.proceed();
}
private Object findFilterTarget(String filterTargetName, EvaluationContext ctx, MethodInvocation methodInvocation) {
@@ -126,7 +124,7 @@ public final class PreFilterAuthorizationMethodBeforeAdvice
if (preFilter == null) {
return PreFilterExpressionAttribute.NULL_ATTRIBUTE;
}
Expression preFilterExpression = PreFilterAuthorizationMethodBeforeAdvice.this.expressionHandler
Expression preFilterExpression = PreFilterAuthorizationMethodInterceptor.this.expressionHandler
.getExpressionParser().parseExpression(preFilter.value());
return new PreFilterExpressionAttribute(preFilterExpression, preFilter.filterTarget());
}

View File

@@ -31,38 +31,36 @@ import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
/**
* An {@link AuthorizationManager} which can determine if an {@link Authentication} has
* access to the {@link MethodInvocation} by evaluating if the {@link Authentication}
* An {@link AuthorizationManager} which can determine if an {@link Authentication} may
* invoke the {@link MethodInvocation} by evaluating if the {@link Authentication}
* contains a specified authority from the Spring Security's {@link Secured} annotation.
*
* @author Evgeniy Cheban
* @since 5.5
*/
public final class SecuredAuthorizationManager implements AuthorizationManager<MethodAuthorizationContext> {
public final class SecuredAuthorizationManager implements AuthorizationManager<MethodInvocation> {
private final SecuredAuthorizationManagerRegistry registry = new SecuredAuthorizationManagerRegistry();
/**
* Determine if an {@link Authentication} has access to a method by evaluating the
* {@link Secured} annotation that {@link MethodAuthorizationContext} specifies.
* {@link Secured} annotation that {@link AuthorizationMethodInvocation} specifies.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
* @param mi the {@link AuthorizationMethodInvocation} to check
* @return an {@link AuthorizationDecision} or null if the {@link Secured} annotation
* is not present
*/
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication,
MethodAuthorizationContext methodAuthorizationContext) {
AuthorizationManager<MethodAuthorizationContext> delegate = this.registry
.getManager(methodAuthorizationContext);
return delegate.check(authentication, methodAuthorizationContext);
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation mi) {
AuthorizationManager<MethodInvocation> delegate = this.registry.getManager((AuthorizationMethodInvocation) mi);
return delegate.check(authentication, mi);
}
private static final class SecuredAuthorizationManagerRegistry extends AbstractAuthorizationManagerRegistry {
@NonNull
@Override
AuthorizationManager<MethodAuthorizationContext> resolveManager(Method method, Class<?> targetClass) {
AuthorizationManager<MethodInvocation> resolveManager(Method method, Class<?> targetClass) {
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
Secured secured = findSecuredAnnotation(specificMethod);
return (secured != null) ? AuthorityAuthorizationManager.hasAnyAuthority(secured.value()) : NULL_MANAGER;