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:
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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 + ']';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user