mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-01 09:42:13 +00:00
Polish Method Authorization Denied Handling
- Renamed @AuthorizationDeniedHandler to @HandleAuthorizationDenied - Merged the post processor interface into MethodAuthorizationDeniedHandler , it now has two methods handleDeniedInvocation and handleDeniedInvocationResult - @HandleAuthorizationDenied now handles AuthorizationDeniedException thrown from the method Issue gh-14601
This commit is contained in:
parent
14da8f4fc8
commit
2fbbcc4bd0
@ -27,22 +27,18 @@ import org.springframework.security.authorization.AuthorizationManager;
|
|||||||
import org.springframework.security.authorization.AuthorizationResult;
|
import org.springframework.security.authorization.AuthorizationResult;
|
||||||
import org.springframework.security.authorization.ObservationAuthorizationManager;
|
import org.springframework.security.authorization.ObservationAuthorizationManager;
|
||||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
||||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedPostProcessor;
|
|
||||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||||
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedHandler;
|
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedHandler;
|
||||||
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedPostProcessor;
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.util.function.SingletonSupplier;
|
import org.springframework.util.function.SingletonSupplier;
|
||||||
|
|
||||||
final class DeferringObservationAuthorizationManager<T>
|
final class DeferringObservationAuthorizationManager<T>
|
||||||
implements AuthorizationManager<T>, MethodAuthorizationDeniedHandler, MethodAuthorizationDeniedPostProcessor {
|
implements AuthorizationManager<T>, MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
private final Supplier<AuthorizationManager<T>> delegate;
|
private final Supplier<AuthorizationManager<T>> delegate;
|
||||||
|
|
||||||
private MethodAuthorizationDeniedHandler handler = new ThrowingMethodAuthorizationDeniedHandler();
|
private MethodAuthorizationDeniedHandler handler = new ThrowingMethodAuthorizationDeniedHandler();
|
||||||
|
|
||||||
private MethodAuthorizationDeniedPostProcessor postProcessor = new ThrowingMethodAuthorizationDeniedPostProcessor();
|
|
||||||
|
|
||||||
DeferringObservationAuthorizationManager(ObjectProvider<ObservationRegistry> provider,
|
DeferringObservationAuthorizationManager(ObjectProvider<ObservationRegistry> provider,
|
||||||
AuthorizationManager<T> delegate) {
|
AuthorizationManager<T> delegate) {
|
||||||
this.delegate = SingletonSupplier.of(() -> {
|
this.delegate = SingletonSupplier.of(() -> {
|
||||||
@ -55,9 +51,6 @@ final class DeferringObservationAuthorizationManager<T>
|
|||||||
if (delegate instanceof MethodAuthorizationDeniedHandler h) {
|
if (delegate instanceof MethodAuthorizationDeniedHandler h) {
|
||||||
this.handler = h;
|
this.handler = h;
|
||||||
}
|
}
|
||||||
if (delegate instanceof MethodAuthorizationDeniedPostProcessor p) {
|
|
||||||
this.postProcessor = p;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -66,14 +59,14 @@ final class DeferringObservationAuthorizationManager<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
||||||
return this.handler.handle(methodInvocation, authorizationResult);
|
return this.handler.handleDeniedInvocation(methodInvocation, authorizationResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postProcessResult(MethodInvocationResult methodInvocationResult,
|
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
|
||||||
AuthorizationResult authorizationResult) {
|
AuthorizationResult authorizationResult) {
|
||||||
return this.postProcessor.postProcessResult(methodInvocationResult, authorizationResult);
|
return this.handler.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,22 +28,18 @@ import org.springframework.security.authorization.AuthorizationResult;
|
|||||||
import org.springframework.security.authorization.ObservationReactiveAuthorizationManager;
|
import org.springframework.security.authorization.ObservationReactiveAuthorizationManager;
|
||||||
import org.springframework.security.authorization.ReactiveAuthorizationManager;
|
import org.springframework.security.authorization.ReactiveAuthorizationManager;
|
||||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
||||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedPostProcessor;
|
|
||||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||||
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedHandler;
|
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedHandler;
|
||||||
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedPostProcessor;
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.util.function.SingletonSupplier;
|
import org.springframework.util.function.SingletonSupplier;
|
||||||
|
|
||||||
final class DeferringObservationReactiveAuthorizationManager<T> implements ReactiveAuthorizationManager<T>,
|
final class DeferringObservationReactiveAuthorizationManager<T>
|
||||||
MethodAuthorizationDeniedHandler, MethodAuthorizationDeniedPostProcessor {
|
implements ReactiveAuthorizationManager<T>, MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
private final Supplier<ReactiveAuthorizationManager<T>> delegate;
|
private final Supplier<ReactiveAuthorizationManager<T>> delegate;
|
||||||
|
|
||||||
private MethodAuthorizationDeniedHandler handler = new ThrowingMethodAuthorizationDeniedHandler();
|
private MethodAuthorizationDeniedHandler handler = new ThrowingMethodAuthorizationDeniedHandler();
|
||||||
|
|
||||||
private MethodAuthorizationDeniedPostProcessor postProcessor = new ThrowingMethodAuthorizationDeniedPostProcessor();
|
|
||||||
|
|
||||||
DeferringObservationReactiveAuthorizationManager(ObjectProvider<ObservationRegistry> provider,
|
DeferringObservationReactiveAuthorizationManager(ObjectProvider<ObservationRegistry> provider,
|
||||||
ReactiveAuthorizationManager<T> delegate) {
|
ReactiveAuthorizationManager<T> delegate) {
|
||||||
this.delegate = SingletonSupplier.of(() -> {
|
this.delegate = SingletonSupplier.of(() -> {
|
||||||
@ -56,9 +52,6 @@ final class DeferringObservationReactiveAuthorizationManager<T> implements React
|
|||||||
if (delegate instanceof MethodAuthorizationDeniedHandler h) {
|
if (delegate instanceof MethodAuthorizationDeniedHandler h) {
|
||||||
this.handler = h;
|
this.handler = h;
|
||||||
}
|
}
|
||||||
if (delegate instanceof MethodAuthorizationDeniedPostProcessor p) {
|
|
||||||
this.postProcessor = p;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -67,14 +60,14 @@ final class DeferringObservationReactiveAuthorizationManager<T> implements React
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
||||||
return this.handler.handle(methodInvocation, authorizationResult);
|
return this.handler.handleDeniedInvocation(methodInvocation, authorizationResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postProcessResult(MethodInvocationResult methodInvocationResult,
|
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
|
||||||
AuthorizationResult authorizationResult) {
|
AuthorizationResult authorizationResult) {
|
||||||
return this.postProcessor.postProcessResult(methodInvocationResult, authorizationResult);
|
return this.handler.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -39,10 +39,9 @@ import org.springframework.security.access.prepost.PostFilter;
|
|||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.security.access.prepost.PreFilter;
|
import org.springframework.security.access.prepost.PreFilter;
|
||||||
import org.springframework.security.authorization.AuthorizationResult;
|
import org.springframework.security.authorization.AuthorizationResult;
|
||||||
import org.springframework.security.authorization.method.AuthorizationDeniedHandler;
|
|
||||||
import org.springframework.security.authorization.method.AuthorizeReturnObject;
|
import org.springframework.security.authorization.method.AuthorizeReturnObject;
|
||||||
|
import org.springframework.security.authorization.method.HandleAuthorizationDenied;
|
||||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
||||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedPostProcessor;
|
|
||||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
@ -129,32 +128,32 @@ public interface MethodSecurityService {
|
|||||||
void repeatedAnnotations();
|
void repeatedAnnotations();
|
||||||
|
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
@AuthorizationDeniedHandler(handlerClass = StarMaskingHandler.class)
|
@HandleAuthorizationDenied(handlerClass = StarMaskingHandler.class)
|
||||||
String preAuthorizeGetCardNumberIfAdmin(String cardNumber);
|
String preAuthorizeGetCardNumberIfAdmin(String cardNumber);
|
||||||
|
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
@AuthorizationDeniedHandler(handlerClass = StartMaskingHandlerChild.class)
|
@HandleAuthorizationDenied(handlerClass = StartMaskingHandlerChild.class)
|
||||||
String preAuthorizeWithHandlerChildGetCardNumberIfAdmin(String cardNumber);
|
String preAuthorizeWithHandlerChildGetCardNumberIfAdmin(String cardNumber);
|
||||||
|
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
@AuthorizationDeniedHandler(handlerClass = StarMaskingHandler.class)
|
@HandleAuthorizationDenied(handlerClass = StarMaskingHandler.class)
|
||||||
String preAuthorizeThrowAccessDeniedManually();
|
String preAuthorizeThrowAccessDeniedManually();
|
||||||
|
|
||||||
@PostAuthorize("hasRole('ADMIN')")
|
@PostAuthorize("hasRole('ADMIN')")
|
||||||
@AuthorizationDeniedHandler(postProcessorClass = CardNumberMaskingPostProcessor.class)
|
@HandleAuthorizationDenied(handlerClass = CardNumberMaskingPostProcessor.class)
|
||||||
String postAuthorizeGetCardNumberIfAdmin(String cardNumber);
|
String postAuthorizeGetCardNumberIfAdmin(String cardNumber);
|
||||||
|
|
||||||
@PostAuthorize("hasRole('ADMIN')")
|
@PostAuthorize("hasRole('ADMIN')")
|
||||||
@AuthorizationDeniedHandler(postProcessorClass = PostMaskingPostProcessor.class)
|
@HandleAuthorizationDenied(handlerClass = PostMaskingPostProcessor.class)
|
||||||
String postAuthorizeThrowAccessDeniedManually();
|
String postAuthorizeThrowAccessDeniedManually();
|
||||||
|
|
||||||
@PreAuthorize("denyAll()")
|
@PreAuthorize("denyAll()")
|
||||||
@Mask("methodmask")
|
@Mask("methodmask")
|
||||||
@AuthorizationDeniedHandler(handlerClass = MaskAnnotationHandler.class)
|
@HandleAuthorizationDenied(handlerClass = MaskAnnotationHandler.class)
|
||||||
String preAuthorizeDeniedMethodWithMaskAnnotation();
|
String preAuthorizeDeniedMethodWithMaskAnnotation();
|
||||||
|
|
||||||
@PreAuthorize("denyAll()")
|
@PreAuthorize("denyAll()")
|
||||||
@AuthorizationDeniedHandler(handlerClass = MaskAnnotationHandler.class)
|
@HandleAuthorizationDenied(handlerClass = MaskAnnotationHandler.class)
|
||||||
String preAuthorizeDeniedMethodWithNoMaskAnnotation();
|
String preAuthorizeDeniedMethodWithNoMaskAnnotation();
|
||||||
|
|
||||||
@NullDenied(role = "ADMIN")
|
@NullDenied(role = "ADMIN")
|
||||||
@ -162,40 +161,39 @@ public interface MethodSecurityService {
|
|||||||
|
|
||||||
@PostAuthorize("denyAll()")
|
@PostAuthorize("denyAll()")
|
||||||
@Mask("methodmask")
|
@Mask("methodmask")
|
||||||
@AuthorizationDeniedHandler(postProcessorClass = MaskAnnotationPostProcessor.class)
|
@HandleAuthorizationDenied(handlerClass = MaskAnnotationPostProcessor.class)
|
||||||
String postAuthorizeDeniedMethodWithMaskAnnotation();
|
String postAuthorizeDeniedMethodWithMaskAnnotation();
|
||||||
|
|
||||||
@PostAuthorize("denyAll()")
|
@PostAuthorize("denyAll()")
|
||||||
@AuthorizationDeniedHandler(postProcessorClass = MaskAnnotationPostProcessor.class)
|
@HandleAuthorizationDenied(handlerClass = MaskAnnotationPostProcessor.class)
|
||||||
String postAuthorizeDeniedMethodWithNoMaskAnnotation();
|
String postAuthorizeDeniedMethodWithNoMaskAnnotation();
|
||||||
|
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
@Mask(expression = "@myMasker.getMask()")
|
@Mask(expression = "@myMasker.getMask()")
|
||||||
@AuthorizationDeniedHandler(handlerClass = MaskAnnotationHandler.class)
|
@HandleAuthorizationDenied(handlerClass = MaskAnnotationHandler.class)
|
||||||
String preAuthorizeWithMaskAnnotationUsingBean();
|
String preAuthorizeWithMaskAnnotationUsingBean();
|
||||||
|
|
||||||
@PostAuthorize("hasRole('ADMIN')")
|
@PostAuthorize("hasRole('ADMIN')")
|
||||||
@Mask(expression = "@myMasker.getMask(returnObject)")
|
@Mask(expression = "@myMasker.getMask(returnObject)")
|
||||||
@AuthorizationDeniedHandler(postProcessorClass = MaskAnnotationPostProcessor.class)
|
@HandleAuthorizationDenied(handlerClass = MaskAnnotationPostProcessor.class)
|
||||||
String postAuthorizeWithMaskAnnotationUsingBean();
|
String postAuthorizeWithMaskAnnotationUsingBean();
|
||||||
|
|
||||||
@AuthorizeReturnObject
|
@AuthorizeReturnObject
|
||||||
UserRecordWithEmailProtected getUserRecordWithEmailProtected();
|
UserRecordWithEmailProtected getUserRecordWithEmailProtected();
|
||||||
|
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
@AuthorizationDeniedHandler(handlerClass = UserFallbackDeniedHandler.class)
|
@HandleAuthorizationDenied(handlerClass = UserFallbackDeniedHandler.class)
|
||||||
UserRecordWithEmailProtected getUserWithFallbackWhenUnauthorized();
|
UserRecordWithEmailProtected getUserWithFallbackWhenUnauthorized();
|
||||||
|
|
||||||
@PreAuthorize("@authz.checkResult(#result)")
|
@PreAuthorize("@authz.checkResult(#result)")
|
||||||
@PostAuthorize("@authz.checkResult(!#result)")
|
@PostAuthorize("@authz.checkResult(!#result)")
|
||||||
@AuthorizationDeniedHandler(handlerClass = MethodAuthorizationDeniedHandler.class,
|
@HandleAuthorizationDenied(handlerClass = MethodAuthorizationDeniedHandler.class)
|
||||||
postProcessorClass = MethodAuthorizationDeniedPostProcessor.class)
|
|
||||||
String checkCustomResult(boolean result);
|
String checkCustomResult(boolean result);
|
||||||
|
|
||||||
class StarMaskingHandler implements MethodAuthorizationDeniedHandler {
|
class StarMaskingHandler implements MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult result) {
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult result) {
|
||||||
return "***";
|
return "***";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,8 +202,8 @@ public interface MethodSecurityService {
|
|||||||
class StartMaskingHandlerChild extends StarMaskingHandler {
|
class StartMaskingHandlerChild extends StarMaskingHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult result) {
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult result) {
|
||||||
return super.handle(methodInvocation, result) + "-child";
|
return super.handleDeniedInvocation(methodInvocation, result) + "-child";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -218,7 +216,6 @@ public interface MethodSecurityService {
|
|||||||
this.maskValueResolver = new MaskValueResolver(context);
|
this.maskValueResolver = new MaskValueResolver(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult result) {
|
public Object handle(MethodInvocation methodInvocation, AuthorizationResult result) {
|
||||||
Mask mask = AnnotationUtils.getAnnotation(methodInvocation.getMethod(), Mask.class);
|
Mask mask = AnnotationUtils.getAnnotation(methodInvocation.getMethod(), Mask.class);
|
||||||
if (mask == null) {
|
if (mask == null) {
|
||||||
@ -227,9 +224,15 @@ public interface MethodSecurityService {
|
|||||||
return this.maskValueResolver.resolveValue(mask, methodInvocation, null);
|
return this.maskValueResolver.resolveValue(mask, methodInvocation, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation,
|
||||||
|
AuthorizationResult authorizationResult) {
|
||||||
|
return handle(methodInvocation, authorizationResult);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MaskAnnotationPostProcessor implements MethodAuthorizationDeniedPostProcessor {
|
class MaskAnnotationPostProcessor implements MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
MaskValueResolver maskValueResolver;
|
MaskValueResolver maskValueResolver;
|
||||||
|
|
||||||
@ -238,7 +241,16 @@ public interface MethodSecurityService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postProcessResult(MethodInvocationResult methodInvocationResult,
|
public Object handleDeniedInvocation(MethodInvocation mi, AuthorizationResult authorizationResult) {
|
||||||
|
Mask mask = AnnotationUtils.getAnnotation(mi.getMethod(), Mask.class);
|
||||||
|
if (mask == null) {
|
||||||
|
mask = AnnotationUtils.getAnnotation(mi.getMethod().getDeclaringClass(), Mask.class);
|
||||||
|
}
|
||||||
|
return this.maskValueResolver.resolveValue(mask, mi, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
|
||||||
AuthorizationResult authorizationResult) {
|
AuthorizationResult authorizationResult) {
|
||||||
MethodInvocation mi = methodInvocationResult.getMethodInvocation();
|
MethodInvocation mi = methodInvocationResult.getMethodInvocation();
|
||||||
Mask mask = AnnotationUtils.getAnnotation(mi.getMethod(), Mask.class);
|
Mask mask = AnnotationUtils.getAnnotation(mi.getMethod(), Mask.class);
|
||||||
@ -274,31 +286,38 @@ public interface MethodSecurityService {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class PostMaskingPostProcessor implements MethodAuthorizationDeniedPostProcessor {
|
class PostMaskingPostProcessor implements MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postProcessResult(MethodInvocationResult contextObject, AuthorizationResult result) {
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation,
|
||||||
|
AuthorizationResult authorizationResult) {
|
||||||
return "***";
|
return "***";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class CardNumberMaskingPostProcessor implements MethodAuthorizationDeniedPostProcessor {
|
class CardNumberMaskingPostProcessor implements MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
static String MASK = "****-****-****-";
|
static String MASK = "****-****-****-";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postProcessResult(MethodInvocationResult contextObject, AuthorizationResult result) {
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation,
|
||||||
|
AuthorizationResult authorizationResult) {
|
||||||
|
return "***";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object handleDeniedInvocationResult(MethodInvocationResult contextObject, AuthorizationResult result) {
|
||||||
String cardNumber = (String) contextObject.getResult();
|
String cardNumber = (String) contextObject.getResult();
|
||||||
return MASK + cardNumber.substring(cardNumber.length() - 4);
|
return MASK + cardNumber.substring(cardNumber.length() - 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class NullPostProcessor implements MethodAuthorizationDeniedPostProcessor {
|
class NullPostProcessor implements MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postProcessResult(MethodInvocationResult methodInvocationResult,
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation,
|
||||||
AuthorizationResult authorizationResult) {
|
AuthorizationResult authorizationResult) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -320,7 +339,7 @@ public interface MethodSecurityService {
|
|||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Inherited
|
@Inherited
|
||||||
@PostAuthorize("hasRole('{role}')")
|
@PostAuthorize("hasRole('{role}')")
|
||||||
@AuthorizationDeniedHandler(postProcessorClass = NullPostProcessor.class)
|
@HandleAuthorizationDenied(handlerClass = NullPostProcessor.class)
|
||||||
@interface NullDenied {
|
@interface NullDenied {
|
||||||
|
|
||||||
String role();
|
String role();
|
||||||
@ -333,7 +352,8 @@ public interface MethodSecurityService {
|
|||||||
"Protected");
|
"Protected");
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation,
|
||||||
|
AuthorizationResult authorizationResult) {
|
||||||
return FALLBACK;
|
return FALLBACK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,8 @@ package org.springframework.security.config.annotation.method.configuration;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
|
import org.springframework.security.authorization.AuthorizationDeniedException;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
|
||||||
@ -144,12 +145,12 @@ public class MethodSecurityServiceImpl implements MethodSecurityService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String preAuthorizeThrowAccessDeniedManually() {
|
public String preAuthorizeThrowAccessDeniedManually() {
|
||||||
throw new AccessDeniedException("Access Denied");
|
throw new AuthorizationDeniedException("Access Denied", new AuthorizationDecision(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String postAuthorizeThrowAccessDeniedManually() {
|
public String postAuthorizeThrowAccessDeniedManually() {
|
||||||
throw new AccessDeniedException("Access Denied");
|
throw new AuthorizationDeniedException("Access Denied", new AuthorizationDecision(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -67,7 +67,6 @@ import org.springframework.security.authorization.method.AuthorizationIntercepto
|
|||||||
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
||||||
import org.springframework.security.authorization.method.AuthorizeReturnObject;
|
import org.springframework.security.authorization.method.AuthorizeReturnObject;
|
||||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
||||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedPostProcessor;
|
|
||||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||||
import org.springframework.security.authorization.method.PrePostTemplateDefaults;
|
import org.springframework.security.authorization.method.PrePostTemplateDefaults;
|
||||||
import org.springframework.security.config.Customizer;
|
import org.springframework.security.config.Customizer;
|
||||||
@ -92,10 +91,10 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
|||||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.atLeastOnce;
|
import static org.mockito.Mockito.atLeastOnce;
|
||||||
|
import static org.mockito.Mockito.clearInvocations;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.verifyNoInteractions;
|
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link PrePostMethodSecurityConfiguration}.
|
* Tests for {@link PrePostMethodSecurityConfiguration}.
|
||||||
@ -783,12 +782,21 @@ public class PrePostMethodSecurityConfigurationTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@WithMockUser(roles = "ADMIN")
|
@WithMockUser(roles = "ADMIN")
|
||||||
void preAuthorizeWhenHandlerAndAccessDeniedNotThrownFromPreAuthorizeThenNotHandled() {
|
void preAuthorizeWhenHandlerAndAccessDeniedNotThrownFromPreAuthorizeThenHandled() {
|
||||||
this.spring.register(MethodSecurityServiceEnabledConfig.class, MethodSecurityService.StarMaskingHandler.class)
|
this.spring.register(MethodSecurityServiceEnabledConfig.class, MethodSecurityService.StarMaskingHandler.class)
|
||||||
.autowire();
|
.autowire();
|
||||||
MethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);
|
MethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);
|
||||||
assertThatExceptionOfType(AccessDeniedException.class)
|
assertThat(service.preAuthorizeThrowAccessDeniedManually()).isEqualTo("***");
|
||||||
.isThrownBy(service::preAuthorizeThrowAccessDeniedManually);
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(roles = "ADMIN")
|
||||||
|
void postAuthorizeWhenHandlerAndAccessDeniedNotThrownFromPostAuthorizeThenHandled() {
|
||||||
|
this.spring
|
||||||
|
.register(MethodSecurityServiceEnabledConfig.class, MethodSecurityService.PostMaskingPostProcessor.class)
|
||||||
|
.autowire();
|
||||||
|
MethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);
|
||||||
|
assertThat(service.postAuthorizeThrowAccessDeniedManually()).isEqualTo("***");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -813,17 +821,6 @@ public class PrePostMethodSecurityConfigurationTests {
|
|||||||
assertThat(result).isEqualTo("classmask");
|
assertThat(result).isEqualTo("classmask");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithMockUser(roles = "ADMIN")
|
|
||||||
void postAuthorizeWhenHandlerAndAccessDeniedNotThrownFromPostAuthorizeThenNotHandled() {
|
|
||||||
this.spring
|
|
||||||
.register(MethodSecurityServiceEnabledConfig.class, MethodSecurityService.PostMaskingPostProcessor.class)
|
|
||||||
.autowire();
|
|
||||||
MethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);
|
|
||||||
assertThatExceptionOfType(AccessDeniedException.class)
|
|
||||||
.isThrownBy(service::postAuthorizeThrowAccessDeniedManually);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@WithMockUser
|
@WithMockUser
|
||||||
void postAuthorizeWhenNullDeniedMetaAnnotationThanWorks() {
|
void postAuthorizeWhenNullDeniedMetaAnnotationThanWorks() {
|
||||||
@ -938,14 +935,13 @@ public class PrePostMethodSecurityConfigurationTests {
|
|||||||
MethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);
|
MethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);
|
||||||
MethodAuthorizationDeniedHandler handler = this.spring.getContext()
|
MethodAuthorizationDeniedHandler handler = this.spring.getContext()
|
||||||
.getBean(MethodAuthorizationDeniedHandler.class);
|
.getBean(MethodAuthorizationDeniedHandler.class);
|
||||||
MethodAuthorizationDeniedPostProcessor postProcessor = this.spring.getContext()
|
|
||||||
.getBean(MethodAuthorizationDeniedPostProcessor.class);
|
|
||||||
assertThat(service.checkCustomResult(false)).isNull();
|
assertThat(service.checkCustomResult(false)).isNull();
|
||||||
verify(handler).handle(any(), any(Authz.AuthzResult.class));
|
verify(handler).handleDeniedInvocation(any(), any(Authz.AuthzResult.class));
|
||||||
verifyNoInteractions(postProcessor);
|
verify(handler, never()).handleDeniedInvocationResult(any(), any(Authz.AuthzResult.class));
|
||||||
|
clearInvocations(handler);
|
||||||
assertThat(service.checkCustomResult(true)).isNull();
|
assertThat(service.checkCustomResult(true)).isNull();
|
||||||
verify(postProcessor).postProcessResult(any(), any(Authz.AuthzResult.class));
|
verify(handler).handleDeniedInvocationResult(any(), any(Authz.AuthzResult.class));
|
||||||
verifyNoMoreInteractions(handler);
|
verify(handler, never()).handleDeniedInvocation(any(), any(Authz.AuthzResult.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Consumer<ConfigurableWebApplicationContext> disallowBeanOverriding() {
|
private static Consumer<ConfigurableWebApplicationContext> disallowBeanOverriding() {
|
||||||
@ -1477,18 +1473,11 @@ public class PrePostMethodSecurityConfigurationTests {
|
|||||||
|
|
||||||
MethodAuthorizationDeniedHandler handler = mock(MethodAuthorizationDeniedHandler.class);
|
MethodAuthorizationDeniedHandler handler = mock(MethodAuthorizationDeniedHandler.class);
|
||||||
|
|
||||||
MethodAuthorizationDeniedPostProcessor postProcessor = mock(MethodAuthorizationDeniedPostProcessor.class);
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
MethodAuthorizationDeniedHandler methodAuthorizationDeniedHandler() {
|
MethodAuthorizationDeniedHandler methodAuthorizationDeniedHandler() {
|
||||||
return this.handler;
|
return this.handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
|
||||||
MethodAuthorizationDeniedPostProcessor methodAuthorizationDeniedPostProcessor() {
|
|
||||||
return this.postProcessor;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@ import reactor.test.StepVerifier;
|
|||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
|
||||||
import org.springframework.security.config.test.SpringTestContext;
|
import org.springframework.security.config.test.SpringTestContext;
|
||||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||||
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
|
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
|
||||||
@ -73,18 +72,6 @@ public class PrePostReactiveMethodSecurityConfigurationTests {
|
|||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithMockUser(roles = "ADMIN")
|
|
||||||
void preAuthorizeWhenHandlerAndAccessDeniedNotThrownFromPreAuthorizeThenNotHandled() {
|
|
||||||
this.spring
|
|
||||||
.register(MethodSecurityServiceEnabledConfig.class, ReactiveMethodSecurityService.StarMaskingHandler.class)
|
|
||||||
.autowire();
|
|
||||||
ReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);
|
|
||||||
StepVerifier.create(service.preAuthorizeThrowAccessDeniedManually())
|
|
||||||
.expectError(AccessDeniedException.class)
|
|
||||||
.verify();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@WithMockUser
|
@WithMockUser
|
||||||
void preAuthorizeWhenDeniedAndHandlerWithCustomAnnotationThenHandlerCanUseMaskFromOtherAnnotation() {
|
void preAuthorizeWhenDeniedAndHandlerWithCustomAnnotationThenHandlerCanUseMaskFromOtherAnnotation() {
|
||||||
@ -119,9 +106,17 @@ public class PrePostReactiveMethodSecurityConfigurationTests {
|
|||||||
ReactiveMethodSecurityService.PostMaskingPostProcessor.class)
|
ReactiveMethodSecurityService.PostMaskingPostProcessor.class)
|
||||||
.autowire();
|
.autowire();
|
||||||
ReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);
|
ReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);
|
||||||
StepVerifier.create(service.postAuthorizeThrowAccessDeniedManually())
|
StepVerifier.create(service.postAuthorizeThrowAccessDeniedManually()).expectNext("***").verifyComplete();
|
||||||
.expectError(AccessDeniedException.class)
|
}
|
||||||
.verify();
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(roles = "ADMIN")
|
||||||
|
void preAuthorizeWhenHandlerAndAccessDeniedNotThrownFromPreAuthorizeThenHandled() {
|
||||||
|
this.spring
|
||||||
|
.register(MethodSecurityServiceEnabledConfig.class, ReactiveMethodSecurityService.StarMaskingHandler.class)
|
||||||
|
.autowire();
|
||||||
|
ReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);
|
||||||
|
StepVerifier.create(service.preAuthorizeThrowAccessDeniedManually()).expectNext("***").verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -48,7 +48,6 @@ import org.springframework.security.authorization.method.AuthorizationAdvisorPro
|
|||||||
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory.TargetVisitor;
|
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory.TargetVisitor;
|
||||||
import org.springframework.security.authorization.method.AuthorizeReturnObject;
|
import org.springframework.security.authorization.method.AuthorizeReturnObject;
|
||||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
||||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedPostProcessor;
|
|
||||||
import org.springframework.security.config.Customizer;
|
import org.springframework.security.config.Customizer;
|
||||||
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
||||||
import org.springframework.security.config.test.SpringTestContext;
|
import org.springframework.security.config.test.SpringTestContext;
|
||||||
@ -60,10 +59,10 @@ import org.springframework.security.test.context.support.WithMockUser;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.clearInvocations;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.verifyNoInteractions;
|
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Tadaya Tsuyukubo
|
* @author Tadaya Tsuyukubo
|
||||||
@ -227,14 +226,13 @@ public class ReactiveMethodSecurityConfigurationTests {
|
|||||||
ReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);
|
ReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);
|
||||||
MethodAuthorizationDeniedHandler handler = this.spring.getContext()
|
MethodAuthorizationDeniedHandler handler = this.spring.getContext()
|
||||||
.getBean(MethodAuthorizationDeniedHandler.class);
|
.getBean(MethodAuthorizationDeniedHandler.class);
|
||||||
MethodAuthorizationDeniedPostProcessor postProcessor = this.spring.getContext()
|
|
||||||
.getBean(MethodAuthorizationDeniedPostProcessor.class);
|
|
||||||
assertThat(service.checkCustomResult(false).block()).isNull();
|
assertThat(service.checkCustomResult(false).block()).isNull();
|
||||||
verify(handler).handle(any(), any(Authz.AuthzResult.class));
|
verify(handler).handleDeniedInvocation(any(), any(Authz.AuthzResult.class));
|
||||||
verifyNoInteractions(postProcessor);
|
verify(handler, never()).handleDeniedInvocationResult(any(), any(Authz.AuthzResult.class));
|
||||||
|
clearInvocations(handler);
|
||||||
assertThat(service.checkCustomResult(true).block()).isNull();
|
assertThat(service.checkCustomResult(true).block()).isNull();
|
||||||
verify(postProcessor).postProcessResult(any(), any(Authz.AuthzResult.class));
|
verify(handler).handleDeniedInvocationResult(any(), any(Authz.AuthzResult.class));
|
||||||
verifyNoMoreInteractions(handler);
|
verify(handler, never()).handleDeniedInvocation(any(), any(Authz.AuthzResult.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Consumer<User.UserBuilder> authorities(String... authorities) {
|
private static Consumer<User.UserBuilder> authorities(String... authorities) {
|
||||||
@ -383,18 +381,11 @@ public class ReactiveMethodSecurityConfigurationTests {
|
|||||||
|
|
||||||
MethodAuthorizationDeniedHandler handler = mock(MethodAuthorizationDeniedHandler.class);
|
MethodAuthorizationDeniedHandler handler = mock(MethodAuthorizationDeniedHandler.class);
|
||||||
|
|
||||||
MethodAuthorizationDeniedPostProcessor postProcessor = mock(MethodAuthorizationDeniedPostProcessor.class);
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
MethodAuthorizationDeniedHandler methodAuthorizationDeniedHandler() {
|
MethodAuthorizationDeniedHandler methodAuthorizationDeniedHandler() {
|
||||||
return this.handler;
|
return this.handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
|
||||||
MethodAuthorizationDeniedPostProcessor methodAuthorizationDeniedPostProcessor() {
|
|
||||||
return this.postProcessor;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,9 +33,8 @@ import org.springframework.security.access.expression.method.DefaultMethodSecuri
|
|||||||
import org.springframework.security.access.prepost.PostAuthorize;
|
import org.springframework.security.access.prepost.PostAuthorize;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.security.authorization.AuthorizationResult;
|
import org.springframework.security.authorization.AuthorizationResult;
|
||||||
import org.springframework.security.authorization.method.AuthorizationDeniedHandler;
|
import org.springframework.security.authorization.method.HandleAuthorizationDenied;
|
||||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
||||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedPostProcessor;
|
|
||||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
@ -47,32 +46,32 @@ import org.springframework.util.StringUtils;
|
|||||||
public interface ReactiveMethodSecurityService {
|
public interface ReactiveMethodSecurityService {
|
||||||
|
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
@AuthorizationDeniedHandler(handlerClass = StarMaskingHandler.class)
|
@HandleAuthorizationDenied(handlerClass = StarMaskingHandler.class)
|
||||||
Mono<String> preAuthorizeGetCardNumberIfAdmin(String cardNumber);
|
Mono<String> preAuthorizeGetCardNumberIfAdmin(String cardNumber);
|
||||||
|
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
@AuthorizationDeniedHandler(handlerClass = StartMaskingHandlerChild.class)
|
@HandleAuthorizationDenied(handlerClass = StartMaskingHandlerChild.class)
|
||||||
Mono<String> preAuthorizeWithHandlerChildGetCardNumberIfAdmin(String cardNumber);
|
Mono<String> preAuthorizeWithHandlerChildGetCardNumberIfAdmin(String cardNumber);
|
||||||
|
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
@AuthorizationDeniedHandler(handlerClass = StarMaskingHandler.class)
|
@HandleAuthorizationDenied(handlerClass = StarMaskingHandler.class)
|
||||||
Mono<String> preAuthorizeThrowAccessDeniedManually();
|
Mono<String> preAuthorizeThrowAccessDeniedManually();
|
||||||
|
|
||||||
@PostAuthorize("hasRole('ADMIN')")
|
@PostAuthorize("hasRole('ADMIN')")
|
||||||
@AuthorizationDeniedHandler(postProcessorClass = CardNumberMaskingPostProcessor.class)
|
@HandleAuthorizationDenied(handlerClass = CardNumberMaskingPostProcessor.class)
|
||||||
Mono<String> postAuthorizeGetCardNumberIfAdmin(String cardNumber);
|
Mono<String> postAuthorizeGetCardNumberIfAdmin(String cardNumber);
|
||||||
|
|
||||||
@PostAuthorize("hasRole('ADMIN')")
|
@PostAuthorize("hasRole('ADMIN')")
|
||||||
@AuthorizationDeniedHandler(postProcessorClass = PostMaskingPostProcessor.class)
|
@HandleAuthorizationDenied(handlerClass = PostMaskingPostProcessor.class)
|
||||||
Mono<String> postAuthorizeThrowAccessDeniedManually();
|
Mono<String> postAuthorizeThrowAccessDeniedManually();
|
||||||
|
|
||||||
@PreAuthorize("denyAll()")
|
@PreAuthorize("denyAll()")
|
||||||
@Mask("methodmask")
|
@Mask("methodmask")
|
||||||
@AuthorizationDeniedHandler(handlerClass = MaskAnnotationHandler.class)
|
@HandleAuthorizationDenied(handlerClass = MaskAnnotationHandler.class)
|
||||||
Mono<String> preAuthorizeDeniedMethodWithMaskAnnotation();
|
Mono<String> preAuthorizeDeniedMethodWithMaskAnnotation();
|
||||||
|
|
||||||
@PreAuthorize("denyAll()")
|
@PreAuthorize("denyAll()")
|
||||||
@AuthorizationDeniedHandler(handlerClass = MaskAnnotationHandler.class)
|
@HandleAuthorizationDenied(handlerClass = MaskAnnotationHandler.class)
|
||||||
Mono<String> preAuthorizeDeniedMethodWithNoMaskAnnotation();
|
Mono<String> preAuthorizeDeniedMethodWithNoMaskAnnotation();
|
||||||
|
|
||||||
@NullDenied(role = "ADMIN")
|
@NullDenied(role = "ADMIN")
|
||||||
@ -80,33 +79,32 @@ public interface ReactiveMethodSecurityService {
|
|||||||
|
|
||||||
@PostAuthorize("denyAll()")
|
@PostAuthorize("denyAll()")
|
||||||
@Mask("methodmask")
|
@Mask("methodmask")
|
||||||
@AuthorizationDeniedHandler(postProcessorClass = MaskAnnotationPostProcessor.class)
|
@HandleAuthorizationDenied(handlerClass = MaskAnnotationPostProcessor.class)
|
||||||
Mono<String> postAuthorizeDeniedMethodWithMaskAnnotation();
|
Mono<String> postAuthorizeDeniedMethodWithMaskAnnotation();
|
||||||
|
|
||||||
@PostAuthorize("denyAll()")
|
@PostAuthorize("denyAll()")
|
||||||
@AuthorizationDeniedHandler(postProcessorClass = MaskAnnotationPostProcessor.class)
|
@HandleAuthorizationDenied(handlerClass = MaskAnnotationPostProcessor.class)
|
||||||
Mono<String> postAuthorizeDeniedMethodWithNoMaskAnnotation();
|
Mono<String> postAuthorizeDeniedMethodWithNoMaskAnnotation();
|
||||||
|
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
@Mask(expression = "@myMasker.getMask()")
|
@Mask(expression = "@myMasker.getMask()")
|
||||||
@AuthorizationDeniedHandler(handlerClass = MaskAnnotationHandler.class)
|
@HandleAuthorizationDenied(handlerClass = MaskAnnotationHandler.class)
|
||||||
Mono<String> preAuthorizeWithMaskAnnotationUsingBean();
|
Mono<String> preAuthorizeWithMaskAnnotationUsingBean();
|
||||||
|
|
||||||
@PostAuthorize("hasRole('ADMIN')")
|
@PostAuthorize("hasRole('ADMIN')")
|
||||||
@Mask(expression = "@myMasker.getMask(returnObject)")
|
@Mask(expression = "@myMasker.getMask(returnObject)")
|
||||||
@AuthorizationDeniedHandler(postProcessorClass = MaskAnnotationPostProcessor.class)
|
@HandleAuthorizationDenied(handlerClass = MaskAnnotationPostProcessor.class)
|
||||||
Mono<String> postAuthorizeWithMaskAnnotationUsingBean();
|
Mono<String> postAuthorizeWithMaskAnnotationUsingBean();
|
||||||
|
|
||||||
@PreAuthorize("@authz.checkReactiveResult(#result)")
|
@PreAuthorize("@authz.checkReactiveResult(#result)")
|
||||||
@PostAuthorize("@authz.checkReactiveResult(!#result)")
|
@PostAuthorize("@authz.checkReactiveResult(!#result)")
|
||||||
@AuthorizationDeniedHandler(handlerClass = MethodAuthorizationDeniedHandler.class,
|
@HandleAuthorizationDenied(handlerClass = MethodAuthorizationDeniedHandler.class)
|
||||||
postProcessorClass = MethodAuthorizationDeniedPostProcessor.class)
|
|
||||||
Mono<String> checkCustomResult(boolean result);
|
Mono<String> checkCustomResult(boolean result);
|
||||||
|
|
||||||
class StarMaskingHandler implements MethodAuthorizationDeniedHandler {
|
class StarMaskingHandler implements MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult result) {
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult result) {
|
||||||
return "***";
|
return "***";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,8 +113,8 @@ public interface ReactiveMethodSecurityService {
|
|||||||
class StartMaskingHandlerChild extends StarMaskingHandler {
|
class StartMaskingHandlerChild extends StarMaskingHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult result) {
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult result) {
|
||||||
return super.handle(methodInvocation, result) + "-child";
|
return super.handleDeniedInvocation(methodInvocation, result) + "-child";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -130,7 +128,7 @@ public interface ReactiveMethodSecurityService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult result) {
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult result) {
|
||||||
Mask mask = AnnotationUtils.getAnnotation(methodInvocation.getMethod(), Mask.class);
|
Mask mask = AnnotationUtils.getAnnotation(methodInvocation.getMethod(), Mask.class);
|
||||||
if (mask == null) {
|
if (mask == null) {
|
||||||
mask = AnnotationUtils.getAnnotation(methodInvocation.getMethod().getDeclaringClass(), Mask.class);
|
mask = AnnotationUtils.getAnnotation(methodInvocation.getMethod().getDeclaringClass(), Mask.class);
|
||||||
@ -140,7 +138,7 @@ public interface ReactiveMethodSecurityService {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MaskAnnotationPostProcessor implements MethodAuthorizationDeniedPostProcessor {
|
class MaskAnnotationPostProcessor implements MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
MaskValueResolver maskValueResolver;
|
MaskValueResolver maskValueResolver;
|
||||||
|
|
||||||
@ -149,7 +147,16 @@ public interface ReactiveMethodSecurityService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postProcessResult(MethodInvocationResult methodInvocationResult,
|
public Object handleDeniedInvocation(MethodInvocation mi, AuthorizationResult authorizationResult) {
|
||||||
|
Mask mask = AnnotationUtils.getAnnotation(mi.getMethod(), Mask.class);
|
||||||
|
if (mask == null) {
|
||||||
|
mask = AnnotationUtils.getAnnotation(mi.getMethod().getDeclaringClass(), Mask.class);
|
||||||
|
}
|
||||||
|
return this.maskValueResolver.resolveValue(mask, mi, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
|
||||||
AuthorizationResult authorizationResult) {
|
AuthorizationResult authorizationResult) {
|
||||||
MethodInvocation mi = methodInvocationResult.getMethodInvocation();
|
MethodInvocation mi = methodInvocationResult.getMethodInvocation();
|
||||||
Mask mask = AnnotationUtils.getAnnotation(mi.getMethod(), Mask.class);
|
Mask mask = AnnotationUtils.getAnnotation(mi.getMethod(), Mask.class);
|
||||||
@ -185,31 +192,38 @@ public interface ReactiveMethodSecurityService {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class PostMaskingPostProcessor implements MethodAuthorizationDeniedPostProcessor {
|
class PostMaskingPostProcessor implements MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postProcessResult(MethodInvocationResult contextObject, AuthorizationResult result) {
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation,
|
||||||
|
AuthorizationResult authorizationResult) {
|
||||||
return "***";
|
return "***";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class CardNumberMaskingPostProcessor implements MethodAuthorizationDeniedPostProcessor {
|
class CardNumberMaskingPostProcessor implements MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
static String MASK = "****-****-****-";
|
static String MASK = "****-****-****-";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postProcessResult(MethodInvocationResult contextObject, AuthorizationResult result) {
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation,
|
||||||
|
AuthorizationResult authorizationResult) {
|
||||||
|
return "***";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object handleDeniedInvocationResult(MethodInvocationResult contextObject, AuthorizationResult result) {
|
||||||
String cardNumber = (String) contextObject.getResult();
|
String cardNumber = (String) contextObject.getResult();
|
||||||
return MASK + cardNumber.substring(cardNumber.length() - 4);
|
return MASK + cardNumber.substring(cardNumber.length() - 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class NullPostProcessor implements MethodAuthorizationDeniedPostProcessor {
|
class NullPostProcessor implements MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postProcessResult(MethodInvocationResult methodInvocationResult,
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation,
|
||||||
AuthorizationResult authorizationResult) {
|
AuthorizationResult authorizationResult) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -231,7 +245,7 @@ public interface ReactiveMethodSecurityService {
|
|||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Inherited
|
@Inherited
|
||||||
@PostAuthorize("hasRole('{value}')")
|
@PostAuthorize("hasRole('{value}')")
|
||||||
@AuthorizationDeniedHandler(postProcessorClass = NullPostProcessor.class)
|
@HandleAuthorizationDenied(handlerClass = NullPostProcessor.class)
|
||||||
@interface NullDenied {
|
@interface NullDenied {
|
||||||
|
|
||||||
String role();
|
String role();
|
||||||
|
@ -18,7 +18,8 @@ package org.springframework.security.config.annotation.method.configuration;
|
|||||||
|
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
|
import org.springframework.security.authorization.AuthorizationDeniedException;
|
||||||
|
|
||||||
public class ReactiveMethodSecurityServiceImpl implements ReactiveMethodSecurityService {
|
public class ReactiveMethodSecurityServiceImpl implements ReactiveMethodSecurityService {
|
||||||
|
|
||||||
@ -34,7 +35,7 @@ public class ReactiveMethodSecurityServiceImpl implements ReactiveMethodSecurity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<String> preAuthorizeThrowAccessDeniedManually() {
|
public Mono<String> preAuthorizeThrowAccessDeniedManually() {
|
||||||
return Mono.error(new AccessDeniedException("Access Denied"));
|
return Mono.error(new AuthorizationDeniedException("Access Denied", new AuthorizationDecision(false)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -44,7 +45,7 @@ public class ReactiveMethodSecurityServiceImpl implements ReactiveMethodSecurity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<String> postAuthorizeThrowAccessDeniedManually() {
|
public Mono<String> postAuthorizeThrowAccessDeniedManually() {
|
||||||
return Mono.error(new AccessDeniedException("Access Denied"));
|
return Mono.error(new AuthorizationDeniedException("Access Denied", new AuthorizationDecision(false)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -16,10 +16,12 @@
|
|||||||
|
|
||||||
package org.springframework.security.config.annotation.method.configuration;
|
package org.springframework.security.config.annotation.method.configuration;
|
||||||
|
|
||||||
|
import org.aopalliance.intercept.MethodInvocation;
|
||||||
|
|
||||||
import org.springframework.security.access.prepost.PostAuthorize;
|
import org.springframework.security.access.prepost.PostAuthorize;
|
||||||
import org.springframework.security.authorization.AuthorizationResult;
|
import org.springframework.security.authorization.AuthorizationResult;
|
||||||
import org.springframework.security.authorization.method.AuthorizationDeniedHandler;
|
import org.springframework.security.authorization.method.HandleAuthorizationDenied;
|
||||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedPostProcessor;
|
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
||||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||||
|
|
||||||
public class UserRecordWithEmailProtected {
|
public class UserRecordWithEmailProtected {
|
||||||
@ -38,15 +40,21 @@ public class UserRecordWithEmailProtected {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostAuthorize("hasRole('ADMIN')")
|
@PostAuthorize("hasRole('ADMIN')")
|
||||||
@AuthorizationDeniedHandler(postProcessorClass = EmailMaskingPostProcessor.class)
|
@HandleAuthorizationDenied(handlerClass = EmailMaskingPostProcessor.class)
|
||||||
public String email() {
|
public String email() {
|
||||||
return this.email;
|
return this.email;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class EmailMaskingPostProcessor implements MethodAuthorizationDeniedPostProcessor {
|
public static class EmailMaskingPostProcessor implements MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postProcessResult(MethodInvocationResult methodInvocationResult,
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation,
|
||||||
|
AuthorizationResult authorizationResult) {
|
||||||
|
return "***";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
|
||||||
AuthorizationResult authorizationResult) {
|
AuthorizationResult authorizationResult) {
|
||||||
String email = (String) methodInvocationResult.getResult();
|
String email = (String) methodInvocationResult.getResult();
|
||||||
return email.replaceAll("(^[^@]{3}|(?!^)\\G)[^@]", "$1*");
|
return email.replaceAll("(^[^@]{3}|(?!^)\\G)[^@]", "$1*");
|
||||||
|
@ -28,10 +28,8 @@ import org.springframework.context.MessageSourceAware;
|
|||||||
import org.springframework.context.support.MessageSourceAccessor;
|
import org.springframework.context.support.MessageSourceAccessor;
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
||||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedPostProcessor;
|
|
||||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||||
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedHandler;
|
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedHandler;
|
||||||
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedPostProcessor;
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
@ -42,8 +40,8 @@ import org.springframework.util.Assert;
|
|||||||
* @author Josh Cummings
|
* @author Josh Cummings
|
||||||
* @since 6.0
|
* @since 6.0
|
||||||
*/
|
*/
|
||||||
public final class ObservationAuthorizationManager<T> implements AuthorizationManager<T>, MessageSourceAware,
|
public final class ObservationAuthorizationManager<T>
|
||||||
MethodAuthorizationDeniedHandler, MethodAuthorizationDeniedPostProcessor {
|
implements AuthorizationManager<T>, MessageSourceAware, MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
private final ObservationRegistry registry;
|
private final ObservationRegistry registry;
|
||||||
|
|
||||||
@ -55,17 +53,12 @@ public final class ObservationAuthorizationManager<T> implements AuthorizationMa
|
|||||||
|
|
||||||
private MethodAuthorizationDeniedHandler handler = new ThrowingMethodAuthorizationDeniedHandler();
|
private MethodAuthorizationDeniedHandler handler = new ThrowingMethodAuthorizationDeniedHandler();
|
||||||
|
|
||||||
private MethodAuthorizationDeniedPostProcessor postProcessor = new ThrowingMethodAuthorizationDeniedPostProcessor();
|
|
||||||
|
|
||||||
public ObservationAuthorizationManager(ObservationRegistry registry, AuthorizationManager<T> delegate) {
|
public ObservationAuthorizationManager(ObservationRegistry registry, AuthorizationManager<T> delegate) {
|
||||||
this.registry = registry;
|
this.registry = registry;
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
if (delegate instanceof MethodAuthorizationDeniedHandler h) {
|
if (delegate instanceof MethodAuthorizationDeniedHandler h) {
|
||||||
this.handler = h;
|
this.handler = h;
|
||||||
}
|
}
|
||||||
if (delegate instanceof MethodAuthorizationDeniedPostProcessor p) {
|
|
||||||
this.postProcessor = p;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -116,14 +109,14 @@ public final class ObservationAuthorizationManager<T> implements AuthorizationMa
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
||||||
return this.handler.handle(methodInvocation, authorizationResult);
|
return this.handler.handleDeniedInvocation(methodInvocation, authorizationResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postProcessResult(MethodInvocationResult methodInvocationResult,
|
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
|
||||||
AuthorizationResult authorizationResult) {
|
AuthorizationResult authorizationResult) {
|
||||||
return this.postProcessor.postProcessResult(methodInvocationResult, authorizationResult);
|
return this.handler.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,10 +25,8 @@ import reactor.core.publisher.Mono;
|
|||||||
|
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
||||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedPostProcessor;
|
|
||||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||||
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedHandler;
|
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedHandler;
|
||||||
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedPostProcessor;
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
@ -38,8 +36,8 @@ import org.springframework.util.Assert;
|
|||||||
* @author Josh Cummings
|
* @author Josh Cummings
|
||||||
* @since 6.0
|
* @since 6.0
|
||||||
*/
|
*/
|
||||||
public final class ObservationReactiveAuthorizationManager<T> implements ReactiveAuthorizationManager<T>,
|
public final class ObservationReactiveAuthorizationManager<T>
|
||||||
MethodAuthorizationDeniedHandler, MethodAuthorizationDeniedPostProcessor {
|
implements ReactiveAuthorizationManager<T>, MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
private final ObservationRegistry registry;
|
private final ObservationRegistry registry;
|
||||||
|
|
||||||
@ -49,8 +47,6 @@ public final class ObservationReactiveAuthorizationManager<T> implements Reactiv
|
|||||||
|
|
||||||
private MethodAuthorizationDeniedHandler handler = new ThrowingMethodAuthorizationDeniedHandler();
|
private MethodAuthorizationDeniedHandler handler = new ThrowingMethodAuthorizationDeniedHandler();
|
||||||
|
|
||||||
private MethodAuthorizationDeniedPostProcessor postProcessor = new ThrowingMethodAuthorizationDeniedPostProcessor();
|
|
||||||
|
|
||||||
public ObservationReactiveAuthorizationManager(ObservationRegistry registry,
|
public ObservationReactiveAuthorizationManager(ObservationRegistry registry,
|
||||||
ReactiveAuthorizationManager<T> delegate) {
|
ReactiveAuthorizationManager<T> delegate) {
|
||||||
this.registry = registry;
|
this.registry = registry;
|
||||||
@ -58,9 +54,6 @@ public final class ObservationReactiveAuthorizationManager<T> implements Reactiv
|
|||||||
if (delegate instanceof MethodAuthorizationDeniedHandler h) {
|
if (delegate instanceof MethodAuthorizationDeniedHandler h) {
|
||||||
this.handler = h;
|
this.handler = h;
|
||||||
}
|
}
|
||||||
if (delegate instanceof MethodAuthorizationDeniedPostProcessor p) {
|
|
||||||
this.postProcessor = p;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -99,14 +92,14 @@ public final class ObservationReactiveAuthorizationManager<T> implements Reactiv
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
||||||
return this.handler.handle(methodInvocation, authorizationResult);
|
return this.handler.handleDeniedInvocation(methodInvocation, authorizationResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postProcessResult(MethodInvocationResult methodInvocationResult,
|
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
|
||||||
AuthorizationResult authorizationResult) {
|
AuthorizationResult authorizationResult) {
|
||||||
return this.postProcessor.postProcessResult(methodInvocationResult, authorizationResult);
|
return this.handler.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,6 @@ import org.springframework.security.authorization.AuthorizationDecision;
|
|||||||
import org.springframework.security.authorization.AuthorizationDeniedException;
|
import org.springframework.security.authorization.AuthorizationDeniedException;
|
||||||
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||||
import org.springframework.security.authorization.AuthorizationManager;
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
import org.springframework.security.authorization.AuthorizationResult;
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||||
@ -57,7 +56,7 @@ public final class AuthorizationManagerAfterMethodInterceptor implements Authori
|
|||||||
|
|
||||||
private final AuthorizationManager<MethodInvocationResult> authorizationManager;
|
private final AuthorizationManager<MethodInvocationResult> authorizationManager;
|
||||||
|
|
||||||
private final MethodAuthorizationDeniedPostProcessor defaultPostProcessor = new ThrowingMethodAuthorizationDeniedPostProcessor();
|
private final MethodAuthorizationDeniedHandler defaultHandler = new ThrowingMethodAuthorizationDeniedHandler();
|
||||||
|
|
||||||
private int order;
|
private int order;
|
||||||
|
|
||||||
@ -119,7 +118,16 @@ public final class AuthorizationManagerAfterMethodInterceptor implements Authori
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Object invoke(MethodInvocation mi) throws Throwable {
|
public Object invoke(MethodInvocation mi) throws Throwable {
|
||||||
Object result = mi.proceed();
|
Object result;
|
||||||
|
try {
|
||||||
|
result = mi.proceed();
|
||||||
|
}
|
||||||
|
catch (AuthorizationDeniedException ex) {
|
||||||
|
if (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {
|
||||||
|
return handler.handleDeniedInvocation(mi, ex);
|
||||||
|
}
|
||||||
|
return this.defaultHandler.handleDeniedInvocation(mi, ex);
|
||||||
|
}
|
||||||
return attemptAuthorization(mi, result);
|
return attemptAuthorization(mi, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,28 +182,22 @@ public final class AuthorizationManagerAfterMethodInterceptor implements Authori
|
|||||||
private Object attemptAuthorization(MethodInvocation mi, Object result) {
|
private Object attemptAuthorization(MethodInvocation mi, Object result) {
|
||||||
this.logger.debug(LogMessage.of(() -> "Authorizing method invocation " + mi));
|
this.logger.debug(LogMessage.of(() -> "Authorizing method invocation " + mi));
|
||||||
MethodInvocationResult object = new MethodInvocationResult(mi, result);
|
MethodInvocationResult object = new MethodInvocationResult(mi, result);
|
||||||
AuthorizationDecision decision;
|
AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, object);
|
||||||
try {
|
|
||||||
decision = this.authorizationManager.check(this::getAuthentication, object);
|
|
||||||
}
|
|
||||||
catch (AuthorizationDeniedException denied) {
|
|
||||||
return postProcess(object, denied);
|
|
||||||
}
|
|
||||||
this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, object, decision);
|
this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, object, decision);
|
||||||
if (decision != null && !decision.isGranted()) {
|
if (decision != null && !decision.isGranted()) {
|
||||||
this.logger.debug(LogMessage.of(() -> "Failed to authorize " + mi + " with authorization manager "
|
this.logger.debug(LogMessage.of(() -> "Failed to authorize " + mi + " with authorization manager "
|
||||||
+ this.authorizationManager + " and decision " + decision));
|
+ this.authorizationManager + " and decision " + decision));
|
||||||
return postProcess(object, decision);
|
return handlePostInvocationDenied(object, decision);
|
||||||
}
|
}
|
||||||
this.logger.debug(LogMessage.of(() -> "Authorized method invocation " + mi));
|
this.logger.debug(LogMessage.of(() -> "Authorized method invocation " + mi));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object postProcess(MethodInvocationResult mi, AuthorizationResult decision) {
|
private Object handlePostInvocationDenied(MethodInvocationResult mi, AuthorizationDecision decision) {
|
||||||
if (this.authorizationManager instanceof MethodAuthorizationDeniedPostProcessor postProcessableDecision) {
|
if (this.authorizationManager instanceof MethodAuthorizationDeniedHandler deniedHandler) {
|
||||||
return postProcessableDecision.postProcessResult(mi, decision);
|
return deniedHandler.handleDeniedInvocationResult(mi, decision);
|
||||||
}
|
}
|
||||||
return this.defaultPostProcessor.postProcessResult(mi, decision);
|
return this.defaultHandler.handleDeniedInvocationResult(mi, decision);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Authentication getAuthentication() {
|
private Authentication getAuthentication() {
|
||||||
|
@ -26,6 +26,7 @@ import org.aopalliance.intercept.MethodInvocation;
|
|||||||
import org.reactivestreams.Publisher;
|
import org.reactivestreams.Publisher;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
import reactor.core.publisher.Signal;
|
||||||
|
|
||||||
import org.springframework.aop.Pointcut;
|
import org.springframework.aop.Pointcut;
|
||||||
import org.springframework.core.KotlinDetector;
|
import org.springframework.core.KotlinDetector;
|
||||||
@ -60,7 +61,7 @@ public final class AuthorizationManagerAfterReactiveMethodInterceptor implements
|
|||||||
|
|
||||||
private int order = AuthorizationInterceptorsOrder.LAST.getOrder();
|
private int order = AuthorizationInterceptorsOrder.LAST.getOrder();
|
||||||
|
|
||||||
private final MethodAuthorizationDeniedPostProcessor defaultPostProcessor = new ThrowingMethodAuthorizationDeniedPostProcessor();
|
private final MethodAuthorizationDeniedHandler defaultHandler = new ThrowingMethodAuthorizationDeniedHandler();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance for the {@link PostAuthorize} annotation.
|
* Creates an instance for the {@link PostAuthorize} annotation.
|
||||||
@ -118,27 +119,39 @@ public final class AuthorizationManagerAfterReactiveMethodInterceptor implements
|
|||||||
+ "(for example, a Mono or Flux) or the function must be a Kotlin coroutine "
|
+ "(for example, a Mono or Flux) or the function must be a Kotlin coroutine "
|
||||||
+ "in order to support Reactor Context");
|
+ "in order to support Reactor Context");
|
||||||
Mono<Authentication> authentication = ReactiveAuthenticationUtils.getAuthentication();
|
Mono<Authentication> authentication = ReactiveAuthenticationUtils.getAuthentication();
|
||||||
Function<Object, Mono<?>> postAuthorize = (result) -> postAuthorize(authentication, mi, result);
|
Function<Signal<?>, Mono<?>> postAuthorize = (signal) -> {
|
||||||
|
if (signal.isOnComplete()) {
|
||||||
|
return Mono.empty();
|
||||||
|
}
|
||||||
|
if (!signal.hasError()) {
|
||||||
|
return postAuthorize(authentication, mi, signal.get());
|
||||||
|
}
|
||||||
|
if (signal.getThrowable() instanceof AuthorizationDeniedException denied) {
|
||||||
|
return postProcess(denied, mi);
|
||||||
|
}
|
||||||
|
return Mono.error(signal.getThrowable());
|
||||||
|
};
|
||||||
ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(type);
|
ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(type);
|
||||||
if (hasFlowReturnType) {
|
if (hasFlowReturnType) {
|
||||||
if (isSuspendingFunction) {
|
if (isSuspendingFunction) {
|
||||||
Publisher<?> publisher = ReactiveMethodInvocationUtils.proceed(mi);
|
Publisher<?> publisher = ReactiveMethodInvocationUtils.proceed(mi);
|
||||||
return Flux.from(publisher).flatMap(postAuthorize);
|
return Flux.from(publisher).materialize().flatMap(postAuthorize);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Assert.state(adapter != null, () -> "The returnType " + type + " on " + method
|
Assert.state(adapter != null, () -> "The returnType " + type + " on " + method
|
||||||
+ " must have a org.springframework.core.ReactiveAdapter registered");
|
+ " must have a org.springframework.core.ReactiveAdapter registered");
|
||||||
Flux<?> response = Flux.defer(() -> adapter.toPublisher(ReactiveMethodInvocationUtils.proceed(mi)))
|
Flux<?> response = Flux.defer(() -> adapter.toPublisher(ReactiveMethodInvocationUtils.proceed(mi)))
|
||||||
|
.materialize()
|
||||||
.flatMap(postAuthorize);
|
.flatMap(postAuthorize);
|
||||||
return KotlinDelegate.asFlow(response);
|
return KotlinDelegate.asFlow(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Publisher<?> publisher = ReactiveMethodInvocationUtils.proceed(mi);
|
Publisher<?> publisher = ReactiveMethodInvocationUtils.proceed(mi);
|
||||||
if (isMultiValue(type, adapter)) {
|
if (isMultiValue(type, adapter)) {
|
||||||
Flux<?> flux = Flux.from(publisher).flatMap(postAuthorize);
|
Flux<?> flux = Flux.from(publisher).materialize().flatMap(postAuthorize);
|
||||||
return (adapter != null) ? adapter.fromPublisher(flux) : flux;
|
return (adapter != null) ? adapter.fromPublisher(flux) : flux;
|
||||||
}
|
}
|
||||||
Mono<?> mono = Mono.from(publisher).flatMap(postAuthorize);
|
Mono<?> mono = Mono.from(publisher).materialize().flatMap(postAuthorize);
|
||||||
return (adapter != null) ? adapter.fromPublisher(mono) : mono;
|
return (adapter != null) ? adapter.fromPublisher(mono) : mono;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,17 +166,7 @@ public final class AuthorizationManagerAfterReactiveMethodInterceptor implements
|
|||||||
MethodInvocationResult invocationResult = new MethodInvocationResult(mi, result);
|
MethodInvocationResult invocationResult = new MethodInvocationResult(mi, result);
|
||||||
return this.authorizationManager.check(authentication, invocationResult)
|
return this.authorizationManager.check(authentication, invocationResult)
|
||||||
.switchIfEmpty(Mono.just(new AuthorizationDecision(false)))
|
.switchIfEmpty(Mono.just(new AuthorizationDecision(false)))
|
||||||
.materialize()
|
.flatMap((decision) -> postProcess(decision, invocationResult));
|
||||||
.flatMap((signal) -> {
|
|
||||||
if (!signal.hasError()) {
|
|
||||||
AuthorizationDecision decision = signal.get();
|
|
||||||
return postProcess(decision, invocationResult);
|
|
||||||
}
|
|
||||||
if (signal.getThrowable() instanceof AuthorizationDeniedException denied) {
|
|
||||||
return postProcess(denied, invocationResult);
|
|
||||||
}
|
|
||||||
return Mono.error(signal.getThrowable());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<Object> postProcess(AuthorizationResult decision, MethodInvocationResult methodInvocationResult) {
|
private Mono<Object> postProcess(AuthorizationResult decision, MethodInvocationResult methodInvocationResult) {
|
||||||
@ -171,10 +174,24 @@ public final class AuthorizationManagerAfterReactiveMethodInterceptor implements
|
|||||||
return Mono.just(methodInvocationResult.getResult());
|
return Mono.just(methodInvocationResult.getResult());
|
||||||
}
|
}
|
||||||
return Mono.fromSupplier(() -> {
|
return Mono.fromSupplier(() -> {
|
||||||
if (this.authorizationManager instanceof MethodAuthorizationDeniedPostProcessor postProcessableDecision) {
|
if (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {
|
||||||
return postProcessableDecision.postProcessResult(methodInvocationResult, decision);
|
return handler.handleDeniedInvocationResult(methodInvocationResult, decision);
|
||||||
}
|
}
|
||||||
return this.defaultPostProcessor.postProcessResult(methodInvocationResult, decision);
|
return this.defaultHandler.handleDeniedInvocationResult(methodInvocationResult, decision);
|
||||||
|
}).flatMap((processedResult) -> {
|
||||||
|
if (Mono.class.isAssignableFrom(processedResult.getClass())) {
|
||||||
|
return (Mono<?>) processedResult;
|
||||||
|
}
|
||||||
|
return Mono.justOrEmpty(processedResult);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Mono<Object> postProcess(AuthorizationResult decision, MethodInvocation methodInvocation) {
|
||||||
|
return Mono.fromSupplier(() -> {
|
||||||
|
if (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {
|
||||||
|
return handler.handleDeniedInvocation(methodInvocation, decision);
|
||||||
|
}
|
||||||
|
return this.defaultHandler.handleDeniedInvocation(methodInvocation, decision);
|
||||||
}).flatMap((processedResult) -> {
|
}).flatMap((processedResult) -> {
|
||||||
if (Mono.class.isAssignableFrom(processedResult.getClass())) {
|
if (Mono.class.isAssignableFrom(processedResult.getClass())) {
|
||||||
return (Mono<?>) processedResult;
|
return (Mono<?>) processedResult;
|
||||||
|
@ -261,14 +261,33 @@ public final class AuthorizationManagerBeforeMethodInterceptor implements Author
|
|||||||
return handle(mi, decision);
|
return handle(mi, decision);
|
||||||
}
|
}
|
||||||
this.logger.debug(LogMessage.of(() -> "Authorized method invocation " + mi));
|
this.logger.debug(LogMessage.of(() -> "Authorized method invocation " + mi));
|
||||||
return mi.proceed();
|
return proceed(mi);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object proceed(MethodInvocation mi) throws Throwable {
|
||||||
|
try {
|
||||||
|
return mi.proceed();
|
||||||
|
}
|
||||||
|
catch (AuthorizationDeniedException ex) {
|
||||||
|
if (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {
|
||||||
|
return handler.handleDeniedInvocation(mi, ex);
|
||||||
|
}
|
||||||
|
return this.defaultHandler.handleDeniedInvocation(mi, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object handle(MethodInvocation mi, AuthorizationDeniedException denied) {
|
||||||
|
if (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {
|
||||||
|
return handler.handleDeniedInvocation(mi, denied);
|
||||||
|
}
|
||||||
|
return this.defaultHandler.handleDeniedInvocation(mi, denied);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object handle(MethodInvocation mi, AuthorizationResult decision) {
|
private Object handle(MethodInvocation mi, AuthorizationResult decision) {
|
||||||
if (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {
|
if (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {
|
||||||
return handler.handle(mi, decision);
|
return handler.handleDeniedInvocation(mi, decision);
|
||||||
}
|
}
|
||||||
return this.defaultHandler.handle(mi, decision);
|
return this.defaultHandler.handleDeniedInvocation(mi, decision);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Authentication getAuthentication() {
|
private Authentication getAuthentication() {
|
||||||
|
@ -142,19 +142,12 @@ public final class AuthorizationManagerBeforeReactiveMethodInterceptor implement
|
|||||||
Mono<Authentication> authentication = ReactiveAuthenticationUtils.getAuthentication();
|
Mono<Authentication> authentication = ReactiveAuthenticationUtils.getAuthentication();
|
||||||
return this.authorizationManager.check(authentication, mi)
|
return this.authorizationManager.check(authentication, mi)
|
||||||
.switchIfEmpty(Mono.just(new AuthorizationDecision(false)))
|
.switchIfEmpty(Mono.just(new AuthorizationDecision(false)))
|
||||||
.materialize()
|
.flatMapMany((decision) -> {
|
||||||
.flatMapMany((signal) -> {
|
if (decision.isGranted()) {
|
||||||
if (!signal.hasError()) {
|
return mapping.onErrorResume(AuthorizationDeniedException.class,
|
||||||
AuthorizationDecision decision = signal.get();
|
(deniedEx) -> postProcess(deniedEx, mi));
|
||||||
if (decision.isGranted()) {
|
|
||||||
return mapping;
|
|
||||||
}
|
|
||||||
return postProcess(decision, mi);
|
|
||||||
}
|
}
|
||||||
if (signal.getThrowable() instanceof AuthorizationDeniedException denied) {
|
return postProcess(decision, mi);
|
||||||
return postProcess(denied, mi);
|
|
||||||
}
|
|
||||||
return Mono.error(signal.getThrowable());
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,28 +155,21 @@ public final class AuthorizationManagerBeforeReactiveMethodInterceptor implement
|
|||||||
Mono<Authentication> authentication = ReactiveAuthenticationUtils.getAuthentication();
|
Mono<Authentication> authentication = ReactiveAuthenticationUtils.getAuthentication();
|
||||||
return this.authorizationManager.check(authentication, mi)
|
return this.authorizationManager.check(authentication, mi)
|
||||||
.switchIfEmpty(Mono.just(new AuthorizationDecision(false)))
|
.switchIfEmpty(Mono.just(new AuthorizationDecision(false)))
|
||||||
.materialize()
|
.flatMap((decision) -> {
|
||||||
.flatMap((signal) -> {
|
if (decision.isGranted()) {
|
||||||
if (!signal.hasError()) {
|
return mapping.onErrorResume(AuthorizationDeniedException.class,
|
||||||
AuthorizationDecision decision = signal.get();
|
(deniedEx) -> postProcess(deniedEx, mi));
|
||||||
if (decision.isGranted()) {
|
|
||||||
return mapping;
|
|
||||||
}
|
|
||||||
return postProcess(decision, mi);
|
|
||||||
}
|
}
|
||||||
if (signal.getThrowable() instanceof AuthorizationDeniedException denied) {
|
return postProcess(decision, mi);
|
||||||
return postProcess(denied, mi);
|
|
||||||
}
|
|
||||||
return Mono.error(signal.getThrowable());
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<Object> postProcess(AuthorizationResult decision, MethodInvocation mi) {
|
private Mono<Object> postProcess(AuthorizationResult decision, MethodInvocation mi) {
|
||||||
return Mono.fromSupplier(() -> {
|
return Mono.fromSupplier(() -> {
|
||||||
if (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {
|
if (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {
|
||||||
return handler.handle(mi, decision);
|
return handler.handleDeniedInvocation(mi, decision);
|
||||||
}
|
}
|
||||||
return this.defaultHandler.handle(mi, decision);
|
return this.defaultHandler.handleDeniedInvocation(mi, decision);
|
||||||
}).flatMap((result) -> {
|
}).flatMap((result) -> {
|
||||||
if (Mono.class.isAssignableFrom(result.getClass())) {
|
if (Mono.class.isAssignableFrom(result.getClass())) {
|
||||||
return (Mono<?>) result;
|
return (Mono<?>) result;
|
||||||
|
@ -25,32 +25,26 @@ import java.lang.annotation.Target;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Annotation for specifying handling behavior when an authorization denied happens in
|
* Annotation for specifying handling behavior when an authorization denied happens in
|
||||||
* method security
|
* method security or an
|
||||||
|
* {@link org.springframework.security.authorization.AuthorizationDeniedException} is
|
||||||
|
* thrown during method invocation
|
||||||
*
|
*
|
||||||
* @author Marcus da Coregio
|
* @author Marcus da Coregio
|
||||||
* @since 6.3
|
* @since 6.3
|
||||||
* @see org.springframework.security.access.prepost.PreAuthorize
|
* @see AuthorizationManagerAfterMethodInterceptor
|
||||||
* @see org.springframework.security.access.prepost.PostAuthorize
|
* @see AuthorizationManagerBeforeMethodInterceptor
|
||||||
*/
|
*/
|
||||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Inherited
|
@Inherited
|
||||||
@Documented
|
@Documented
|
||||||
public @interface AuthorizationDeniedHandler {
|
public @interface HandleAuthorizationDenied {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link MethodAuthorizationDeniedHandler} used to handle denied authorizations
|
* The {@link MethodAuthorizationDeniedHandler} used to handle denied authorization
|
||||||
* from {@link org.springframework.security.access.prepost.PreAuthorize}
|
* results
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
Class<? extends MethodAuthorizationDeniedHandler> handlerClass() default ThrowingMethodAuthorizationDeniedHandler.class;
|
Class<? extends MethodAuthorizationDeniedHandler> handlerClass() default ThrowingMethodAuthorizationDeniedHandler.class;
|
||||||
|
|
||||||
/**
|
|
||||||
* The {@link MethodAuthorizationDeniedPostProcessor} used to post process denied
|
|
||||||
* authorizations from
|
|
||||||
* {@link org.springframework.security.access.prepost.PostAuthorize}
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
Class<? extends MethodAuthorizationDeniedPostProcessor> postProcessorClass() default ThrowingMethodAuthorizationDeniedPostProcessor.class;
|
|
||||||
|
|
||||||
}
|
}
|
@ -27,13 +27,14 @@ import org.springframework.security.authorization.AuthorizationResult;
|
|||||||
* @author Marcus da Coregio
|
* @author Marcus da Coregio
|
||||||
* @since 6.3
|
* @since 6.3
|
||||||
* @see org.springframework.security.access.prepost.PreAuthorize
|
* @see org.springframework.security.access.prepost.PreAuthorize
|
||||||
|
* @see org.springframework.security.access.prepost.PostAuthorize
|
||||||
*/
|
*/
|
||||||
public interface MethodAuthorizationDeniedHandler {
|
public interface MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle denied method invocations, implementations might either throw an
|
* Handle denied method invocations, implementations might either throw an
|
||||||
* {@link org.springframework.security.access.AccessDeniedException} or a replacement
|
* {@link org.springframework.security.authorization.AuthorizationDeniedException} or
|
||||||
* result instead of invoking the method, e.g. a masked value.
|
* a replacement result instead of invoking the method, e.g. a masked value.
|
||||||
* @param methodInvocation the {@link MethodInvocation} related to the authorization
|
* @param methodInvocation the {@link MethodInvocation} related to the authorization
|
||||||
* denied
|
* denied
|
||||||
* @param authorizationResult the authorization denied result
|
* @param authorizationResult the authorization denied result
|
||||||
@ -41,6 +42,24 @@ public interface MethodAuthorizationDeniedHandler {
|
|||||||
* {@link reactor.core.publisher.Mono} for reactive applications
|
* {@link reactor.core.publisher.Mono} for reactive applications
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
Object handle(MethodInvocation methodInvocation, AuthorizationResult authorizationResult);
|
Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle denied method invocations, implementations might either throw an
|
||||||
|
* {@link org.springframework.security.authorization.AuthorizationDeniedException} or
|
||||||
|
* a replacement result instead of invoking the method, e.g. a masked value. By
|
||||||
|
* default, this method invokes
|
||||||
|
* {@link #handleDeniedInvocation(MethodInvocation, AuthorizationResult)}.
|
||||||
|
* @param methodInvocationResult the object containing the {@link MethodInvocation}
|
||||||
|
* and the result produced
|
||||||
|
* @param authorizationResult the authorization denied result
|
||||||
|
* @return a replacement result for the denied method invocation, or null, or a
|
||||||
|
* {@link reactor.core.publisher.Mono} for reactive applications
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
default Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
|
||||||
|
AuthorizationResult authorizationResult) {
|
||||||
|
return handleDeniedInvocation(methodInvocationResult.getMethodInvocation(), authorizationResult);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2002-2024 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.springframework.lang.Nullable;
|
|
||||||
import org.springframework.security.authorization.AuthorizationResult;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface to define a strategy to handle denied method invocation results
|
|
||||||
*
|
|
||||||
* @author Marcus da Coregio
|
|
||||||
* @since 6.3
|
|
||||||
* @see org.springframework.security.access.prepost.PostAuthorize
|
|
||||||
*/
|
|
||||||
public interface MethodAuthorizationDeniedPostProcessor {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Post-process the denied result produced by a method invocation, implementations
|
|
||||||
* might either throw an
|
|
||||||
* {@link org.springframework.security.access.AccessDeniedException} or return a
|
|
||||||
* replacement result instead of the denied result, e.g. a masked value.
|
|
||||||
* @param methodInvocationResult the object containing the method invocation and the
|
|
||||||
* result produced
|
|
||||||
* @param authorizationResult the {@link AuthorizationResult} containing the
|
|
||||||
* authorization denied details
|
|
||||||
* @return a replacement result for the denied result, or null, or a
|
|
||||||
* {@link reactor.core.publisher.Mono} for reactive applications
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
Object postProcessResult(MethodInvocationResult methodInvocationResult, AuthorizationResult authorizationResult);
|
|
||||||
|
|
||||||
}
|
|
@ -38,7 +38,7 @@ import org.springframework.security.core.Authentication;
|
|||||||
* @since 5.6
|
* @since 5.6
|
||||||
*/
|
*/
|
||||||
public final class PostAuthorizeAuthorizationManager
|
public final class PostAuthorizeAuthorizationManager
|
||||||
implements AuthorizationManager<MethodInvocationResult>, MethodAuthorizationDeniedPostProcessor {
|
implements AuthorizationManager<MethodInvocationResult>, MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
private PostAuthorizeExpressionAttributeRegistry registry = new PostAuthorizeExpressionAttributeRegistry();
|
private PostAuthorizeExpressionAttributeRegistry registry = new PostAuthorizeExpressionAttributeRegistry();
|
||||||
|
|
||||||
@ -96,11 +96,19 @@ public final class PostAuthorizeAuthorizationManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postProcessResult(MethodInvocationResult methodInvocationResult,
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
||||||
|
ExpressionAttribute attribute = this.registry.getAttribute(methodInvocation);
|
||||||
|
PostAuthorizeExpressionAttribute postAuthorizeAttribute = (PostAuthorizeExpressionAttribute) attribute;
|
||||||
|
return postAuthorizeAttribute.getHandler().handleDeniedInvocation(methodInvocation, authorizationResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
|
||||||
AuthorizationResult authorizationResult) {
|
AuthorizationResult authorizationResult) {
|
||||||
ExpressionAttribute attribute = this.registry.getAttribute(methodInvocationResult.getMethodInvocation());
|
ExpressionAttribute attribute = this.registry.getAttribute(methodInvocationResult.getMethodInvocation());
|
||||||
PostAuthorizeExpressionAttribute postAuthorizeAttribute = (PostAuthorizeExpressionAttribute) attribute;
|
PostAuthorizeExpressionAttribute postAuthorizeAttribute = (PostAuthorizeExpressionAttribute) attribute;
|
||||||
return postAuthorizeAttribute.getPostProcessor().postProcessResult(methodInvocationResult, authorizationResult);
|
return postAuthorizeAttribute.getHandler()
|
||||||
|
.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,16 +27,16 @@ import org.springframework.util.Assert;
|
|||||||
*/
|
*/
|
||||||
class PostAuthorizeExpressionAttribute extends ExpressionAttribute {
|
class PostAuthorizeExpressionAttribute extends ExpressionAttribute {
|
||||||
|
|
||||||
private final MethodAuthorizationDeniedPostProcessor postProcessor;
|
private final MethodAuthorizationDeniedHandler handler;
|
||||||
|
|
||||||
PostAuthorizeExpressionAttribute(Expression expression, MethodAuthorizationDeniedPostProcessor postProcessor) {
|
PostAuthorizeExpressionAttribute(Expression expression, MethodAuthorizationDeniedHandler handler) {
|
||||||
super(expression);
|
super(expression);
|
||||||
Assert.notNull(postProcessor, "postProcessor cannot be null");
|
Assert.notNull(handler, "handler cannot be null");
|
||||||
this.postProcessor = postProcessor;
|
this.handler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodAuthorizationDeniedPostProcessor getPostProcessor() {
|
MethodAuthorizationDeniedHandler getHandler() {
|
||||||
return this.postProcessor;
|
return this.handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -38,12 +38,12 @@ import org.springframework.util.Assert;
|
|||||||
*/
|
*/
|
||||||
final class PostAuthorizeExpressionAttributeRegistry extends AbstractExpressionAttributeRegistry<ExpressionAttribute> {
|
final class PostAuthorizeExpressionAttributeRegistry extends AbstractExpressionAttributeRegistry<ExpressionAttribute> {
|
||||||
|
|
||||||
private final MethodAuthorizationDeniedPostProcessor defaultPostProcessor = new ThrowingMethodAuthorizationDeniedPostProcessor();
|
private final MethodAuthorizationDeniedHandler defaultHandler = new ThrowingMethodAuthorizationDeniedHandler();
|
||||||
|
|
||||||
private Function<Class<? extends MethodAuthorizationDeniedPostProcessor>, MethodAuthorizationDeniedPostProcessor> postProcessorResolver;
|
private Function<Class<? extends MethodAuthorizationDeniedHandler>, MethodAuthorizationDeniedHandler> handlerResolver;
|
||||||
|
|
||||||
PostAuthorizeExpressionAttributeRegistry() {
|
PostAuthorizeExpressionAttributeRegistry() {
|
||||||
this.postProcessorResolver = (clazz) -> this.defaultPostProcessor;
|
this.handlerResolver = (clazz) -> this.defaultHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@ -55,22 +55,22 @@ final class PostAuthorizeExpressionAttributeRegistry extends AbstractExpressionA
|
|||||||
return ExpressionAttribute.NULL_ATTRIBUTE;
|
return ExpressionAttribute.NULL_ATTRIBUTE;
|
||||||
}
|
}
|
||||||
Expression expression = getExpressionHandler().getExpressionParser().parseExpression(postAuthorize.value());
|
Expression expression = getExpressionHandler().getExpressionParser().parseExpression(postAuthorize.value());
|
||||||
MethodAuthorizationDeniedPostProcessor postProcessor = resolvePostProcessor(method, targetClass);
|
MethodAuthorizationDeniedHandler deniedHandler = resolveHandler(method, targetClass);
|
||||||
return new PostAuthorizeExpressionAttribute(expression, postProcessor);
|
return new PostAuthorizeExpressionAttribute(expression, deniedHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodAuthorizationDeniedPostProcessor resolvePostProcessor(Method method, Class<?> targetClass) {
|
private MethodAuthorizationDeniedHandler resolveHandler(Method method, Class<?> targetClass) {
|
||||||
Function<AnnotatedElement, AuthorizationDeniedHandler> lookup = AuthorizationAnnotationUtils
|
Function<AnnotatedElement, HandleAuthorizationDenied> lookup = AuthorizationAnnotationUtils
|
||||||
.withDefaults(AuthorizationDeniedHandler.class);
|
.withDefaults(HandleAuthorizationDenied.class);
|
||||||
AuthorizationDeniedHandler deniedHandler = lookup.apply(method);
|
HandleAuthorizationDenied deniedHandler = lookup.apply(method);
|
||||||
if (deniedHandler != null) {
|
if (deniedHandler != null) {
|
||||||
return this.postProcessorResolver.apply(deniedHandler.postProcessorClass());
|
return this.handlerResolver.apply(deniedHandler.handlerClass());
|
||||||
}
|
}
|
||||||
deniedHandler = lookup.apply(targetClass(method, targetClass));
|
deniedHandler = lookup.apply(targetClass(method, targetClass));
|
||||||
if (deniedHandler != null) {
|
if (deniedHandler != null) {
|
||||||
return this.postProcessorResolver.apply(deniedHandler.postProcessorClass());
|
return this.handlerResolver.apply(deniedHandler.handlerClass());
|
||||||
}
|
}
|
||||||
return this.defaultPostProcessor;
|
return this.defaultHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PostAuthorize findPostAuthorizeAnnotation(Method method, Class<?> targetClass) {
|
private PostAuthorize findPostAuthorizeAnnotation(Method method, Class<?> targetClass) {
|
||||||
@ -86,23 +86,23 @@ final class PostAuthorizeExpressionAttributeRegistry extends AbstractExpressionA
|
|||||||
*/
|
*/
|
||||||
void setApplicationContext(ApplicationContext context) {
|
void setApplicationContext(ApplicationContext context) {
|
||||||
Assert.notNull(context, "context cannot be null");
|
Assert.notNull(context, "context cannot be null");
|
||||||
this.postProcessorResolver = (postProcessorClass) -> resolvePostProcessor(context, postProcessorClass);
|
this.handlerResolver = (clazz) -> resolveHandler(context, clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodAuthorizationDeniedPostProcessor resolvePostProcessor(ApplicationContext context,
|
private MethodAuthorizationDeniedHandler resolveHandler(ApplicationContext context,
|
||||||
Class<? extends MethodAuthorizationDeniedPostProcessor> postProcessorClass) {
|
Class<? extends MethodAuthorizationDeniedHandler> handlerClass) {
|
||||||
if (postProcessorClass == this.defaultPostProcessor.getClass()) {
|
if (handlerClass == this.defaultHandler.getClass()) {
|
||||||
return this.defaultPostProcessor;
|
return this.defaultHandler;
|
||||||
}
|
}
|
||||||
String[] beanNames = context.getBeanNamesForType(postProcessorClass);
|
String[] beanNames = context.getBeanNamesForType(handlerClass);
|
||||||
if (beanNames.length == 0) {
|
if (beanNames.length == 0) {
|
||||||
throw new IllegalStateException("Could not find a bean of type " + postProcessorClass.getName());
|
throw new IllegalStateException("Could not find a bean of type " + handlerClass.getName());
|
||||||
}
|
}
|
||||||
if (beanNames.length > 1) {
|
if (beanNames.length > 1) {
|
||||||
throw new IllegalStateException("Expected to find a single bean of type " + postProcessorClass.getName()
|
throw new IllegalStateException("Expected to find a single bean of type " + handlerClass.getName()
|
||||||
+ " but found " + Arrays.toString(beanNames));
|
+ " but found " + Arrays.toString(beanNames));
|
||||||
}
|
}
|
||||||
return context.getBean(beanNames[0], postProcessorClass);
|
return context.getBean(beanNames[0], handlerClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ import org.springframework.util.Assert;
|
|||||||
* @since 5.8
|
* @since 5.8
|
||||||
*/
|
*/
|
||||||
public final class PostAuthorizeReactiveAuthorizationManager
|
public final class PostAuthorizeReactiveAuthorizationManager
|
||||||
implements ReactiveAuthorizationManager<MethodInvocationResult>, MethodAuthorizationDeniedPostProcessor {
|
implements ReactiveAuthorizationManager<MethodInvocationResult>, MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
private final PostAuthorizeExpressionAttributeRegistry registry = new PostAuthorizeExpressionAttributeRegistry();
|
private final PostAuthorizeExpressionAttributeRegistry registry = new PostAuthorizeExpressionAttributeRegistry();
|
||||||
|
|
||||||
@ -95,11 +95,19 @@ public final class PostAuthorizeReactiveAuthorizationManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postProcessResult(MethodInvocationResult methodInvocationResult,
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
||||||
|
ExpressionAttribute attribute = this.registry.getAttribute(methodInvocation);
|
||||||
|
PostAuthorizeExpressionAttribute postAuthorizeAttribute = (PostAuthorizeExpressionAttribute) attribute;
|
||||||
|
return postAuthorizeAttribute.getHandler().handleDeniedInvocation(methodInvocation, authorizationResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
|
||||||
AuthorizationResult authorizationResult) {
|
AuthorizationResult authorizationResult) {
|
||||||
ExpressionAttribute attribute = this.registry.getAttribute(methodInvocationResult.getMethodInvocation());
|
ExpressionAttribute attribute = this.registry.getAttribute(methodInvocationResult.getMethodInvocation());
|
||||||
PostAuthorizeExpressionAttribute postAuthorizeAttribute = (PostAuthorizeExpressionAttribute) attribute;
|
PostAuthorizeExpressionAttribute postAuthorizeAttribute = (PostAuthorizeExpressionAttribute) attribute;
|
||||||
return postAuthorizeAttribute.getPostProcessor().postProcessResult(methodInvocationResult, authorizationResult);
|
return postAuthorizeAttribute.getHandler()
|
||||||
|
.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -86,10 +86,10 @@ public final class PreAuthorizeAuthorizationManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
||||||
ExpressionAttribute attribute = this.registry.getAttribute(methodInvocation);
|
ExpressionAttribute attribute = this.registry.getAttribute(methodInvocation);
|
||||||
PreAuthorizeExpressionAttribute postAuthorizeAttribute = (PreAuthorizeExpressionAttribute) attribute;
|
PreAuthorizeExpressionAttribute preAuthorizeAttribute = (PreAuthorizeExpressionAttribute) attribute;
|
||||||
return postAuthorizeAttribute.getHandler().handle(methodInvocation, authorizationResult);
|
return preAuthorizeAttribute.getHandler().handleDeniedInvocation(methodInvocation, authorizationResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -60,9 +60,9 @@ final class PreAuthorizeExpressionAttributeRegistry extends AbstractExpressionAt
|
|||||||
}
|
}
|
||||||
|
|
||||||
private MethodAuthorizationDeniedHandler resolveHandler(Method method, Class<?> targetClass) {
|
private MethodAuthorizationDeniedHandler resolveHandler(Method method, Class<?> targetClass) {
|
||||||
Function<AnnotatedElement, AuthorizationDeniedHandler> lookup = AuthorizationAnnotationUtils
|
Function<AnnotatedElement, HandleAuthorizationDenied> lookup = AuthorizationAnnotationUtils
|
||||||
.withDefaults(AuthorizationDeniedHandler.class);
|
.withDefaults(HandleAuthorizationDenied.class);
|
||||||
AuthorizationDeniedHandler deniedHandler = lookup.apply(method);
|
HandleAuthorizationDenied deniedHandler = lookup.apply(method);
|
||||||
if (deniedHandler != null) {
|
if (deniedHandler != null) {
|
||||||
return this.handlerResolver.apply(deniedHandler.handlerClass());
|
return this.handlerResolver.apply(deniedHandler.handlerClass());
|
||||||
}
|
}
|
||||||
|
@ -90,10 +90,10 @@ public final class PreAuthorizeReactiveAuthorizationManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
||||||
ExpressionAttribute attribute = this.registry.getAttribute(methodInvocation);
|
ExpressionAttribute attribute = this.registry.getAttribute(methodInvocation);
|
||||||
PreAuthorizeExpressionAttribute preAuthorizeAttribute = (PreAuthorizeExpressionAttribute) attribute;
|
PreAuthorizeExpressionAttribute preAuthorizeAttribute = (PreAuthorizeExpressionAttribute) attribute;
|
||||||
return preAuthorizeAttribute.getHandler().handle(methodInvocation, authorizationResult);
|
return preAuthorizeAttribute.getHandler().handleDeniedInvocation(methodInvocation, authorizationResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,11 +31,20 @@ import org.springframework.security.authorization.AuthorizationResult;
|
|||||||
public final class ThrowingMethodAuthorizationDeniedHandler implements MethodAuthorizationDeniedHandler {
|
public final class ThrowingMethodAuthorizationDeniedHandler implements MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult result) {
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
||||||
if (result instanceof AuthorizationDeniedException denied) {
|
if (authorizationResult instanceof AuthorizationDeniedException denied) {
|
||||||
throw denied;
|
throw denied;
|
||||||
}
|
}
|
||||||
throw new AuthorizationDeniedException("Access Denied", result);
|
throw new AuthorizationDeniedException("Access Denied", authorizationResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
|
||||||
|
AuthorizationResult authorizationResult) {
|
||||||
|
if (authorizationResult instanceof AuthorizationDeniedException denied) {
|
||||||
|
throw denied;
|
||||||
|
}
|
||||||
|
throw new AuthorizationDeniedException("Access Denied", authorizationResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2002-2024 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.springframework.security.authorization.AuthorizationDeniedException;
|
|
||||||
import org.springframework.security.authorization.AuthorizationResult;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An implementation of {@link MethodAuthorizationDeniedPostProcessor} that throws
|
|
||||||
* {@link AuthorizationDeniedException}
|
|
||||||
*
|
|
||||||
* @author Marcus da Coregio
|
|
||||||
* @since 6.3
|
|
||||||
*/
|
|
||||||
public final class ThrowingMethodAuthorizationDeniedPostProcessor implements MethodAuthorizationDeniedPostProcessor {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object postProcessResult(MethodInvocationResult methodInvocationResult, AuthorizationResult result) {
|
|
||||||
if (result instanceof AuthorizationDeniedException denied) {
|
|
||||||
throw denied;
|
|
||||||
}
|
|
||||||
throw new AuthorizationDeniedException("Access Denied", result);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -127,7 +127,7 @@ public class AuthorizationManagerAfterReactiveMethodInterceptorTests {
|
|||||||
given(mockMethodInvocation.proceed()).willReturn(Flux.just("john", "bob"));
|
given(mockMethodInvocation.proceed()).willReturn(Flux.just("john", "bob"));
|
||||||
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
|
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
|
||||||
HandlingReactiveAuthorizationManager.class);
|
HandlingReactiveAuthorizationManager.class);
|
||||||
given(mockReactiveAuthorizationManager.postProcessResult(any(), any(AuthorizationResult.class)))
|
given(mockReactiveAuthorizationManager.handleDeniedInvocationResult(any(), any(AuthorizationResult.class)))
|
||||||
.willAnswer(this::masking);
|
.willAnswer(this::masking);
|
||||||
given(mockReactiveAuthorizationManager.check(any(), any())).willReturn(Mono.empty());
|
given(mockReactiveAuthorizationManager.check(any(), any())).willReturn(Mono.empty());
|
||||||
AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(
|
AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(
|
||||||
@ -147,7 +147,7 @@ public class AuthorizationManagerAfterReactiveMethodInterceptorTests {
|
|||||||
given(mockMethodInvocation.proceed()).willReturn(Flux.just("john", "bob"));
|
given(mockMethodInvocation.proceed()).willReturn(Flux.just("john", "bob"));
|
||||||
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
|
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
|
||||||
HandlingReactiveAuthorizationManager.class);
|
HandlingReactiveAuthorizationManager.class);
|
||||||
given(mockReactiveAuthorizationManager.postProcessResult(any(), any(AuthorizationResult.class)))
|
given(mockReactiveAuthorizationManager.handleDeniedInvocationResult(any(), any(AuthorizationResult.class)))
|
||||||
.willAnswer((invocation) -> {
|
.willAnswer((invocation) -> {
|
||||||
MethodInvocationResult argument = invocation.getArgument(0);
|
MethodInvocationResult argument = invocation.getArgument(0);
|
||||||
if (!"john".equals(argument.getResult())) {
|
if (!"john".equals(argument.getResult())) {
|
||||||
@ -173,7 +173,7 @@ public class AuthorizationManagerAfterReactiveMethodInterceptorTests {
|
|||||||
given(mockMethodInvocation.proceed()).willReturn(Mono.just("john"));
|
given(mockMethodInvocation.proceed()).willReturn(Mono.just("john"));
|
||||||
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
|
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
|
||||||
HandlingReactiveAuthorizationManager.class);
|
HandlingReactiveAuthorizationManager.class);
|
||||||
given(mockReactiveAuthorizationManager.postProcessResult(any(), any(AuthorizationResult.class)))
|
given(mockReactiveAuthorizationManager.handleDeniedInvocationResult(any(), any(AuthorizationResult.class)))
|
||||||
.willAnswer(this::masking);
|
.willAnswer(this::masking);
|
||||||
given(mockReactiveAuthorizationManager.check(any(), any())).willReturn(Mono.empty());
|
given(mockReactiveAuthorizationManager.check(any(), any())).willReturn(Mono.empty());
|
||||||
AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(
|
AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(
|
||||||
@ -192,7 +192,7 @@ public class AuthorizationManagerAfterReactiveMethodInterceptorTests {
|
|||||||
given(mockMethodInvocation.proceed()).willReturn(Mono.just("john"));
|
given(mockMethodInvocation.proceed()).willReturn(Mono.just("john"));
|
||||||
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
|
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
|
||||||
HandlingReactiveAuthorizationManager.class);
|
HandlingReactiveAuthorizationManager.class);
|
||||||
given(mockReactiveAuthorizationManager.postProcessResult(any(), any(AuthorizationResult.class)))
|
given(mockReactiveAuthorizationManager.handleDeniedInvocationResult(any(), any(AuthorizationResult.class)))
|
||||||
.willAnswer(this::monoMasking);
|
.willAnswer(this::monoMasking);
|
||||||
given(mockReactiveAuthorizationManager.check(any(), any())).willReturn(Mono.empty());
|
given(mockReactiveAuthorizationManager.check(any(), any())).willReturn(Mono.empty());
|
||||||
AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(
|
AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(
|
||||||
@ -211,7 +211,7 @@ public class AuthorizationManagerAfterReactiveMethodInterceptorTests {
|
|||||||
given(mockMethodInvocation.proceed()).willReturn(Mono.just("john"));
|
given(mockMethodInvocation.proceed()).willReturn(Mono.just("john"));
|
||||||
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
|
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
|
||||||
HandlingReactiveAuthorizationManager.class);
|
HandlingReactiveAuthorizationManager.class);
|
||||||
given(mockReactiveAuthorizationManager.postProcessResult(any(), any(AuthorizationResult.class)))
|
given(mockReactiveAuthorizationManager.handleDeniedInvocationResult(any(), any(AuthorizationResult.class)))
|
||||||
.willReturn(null);
|
.willReturn(null);
|
||||||
given(mockReactiveAuthorizationManager.check(any(), any())).willReturn(Mono.empty());
|
given(mockReactiveAuthorizationManager.check(any(), any())).willReturn(Mono.empty());
|
||||||
AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(
|
AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(
|
||||||
@ -266,7 +266,7 @@ public class AuthorizationManagerAfterReactiveMethodInterceptorTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface HandlingReactiveAuthorizationManager
|
interface HandlingReactiveAuthorizationManager
|
||||||
extends ReactiveAuthorizationManager<MethodInvocationResult>, MethodAuthorizationDeniedPostProcessor {
|
extends ReactiveAuthorizationManager<MethodInvocationResult>, MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +127,8 @@ public class AuthorizationManagerBeforeReactiveMethodInterceptorTests {
|
|||||||
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
|
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
|
||||||
HandlingReactiveAuthorizationManager.class);
|
HandlingReactiveAuthorizationManager.class);
|
||||||
given(mockReactiveAuthorizationManager.check(any(), eq(mockMethodInvocation))).willReturn(Mono.empty());
|
given(mockReactiveAuthorizationManager.check(any(), eq(mockMethodInvocation))).willReturn(Mono.empty());
|
||||||
given(mockReactiveAuthorizationManager.handle(any(), any(AuthorizationResult.class))).willReturn("***");
|
given(mockReactiveAuthorizationManager.handleDeniedInvocation(any(), any(AuthorizationResult.class)))
|
||||||
|
.willReturn("***");
|
||||||
AuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor(
|
AuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor(
|
||||||
Pointcut.TRUE, mockReactiveAuthorizationManager);
|
Pointcut.TRUE, mockReactiveAuthorizationManager);
|
||||||
Object result = interceptor.invoke(mockMethodInvocation);
|
Object result = interceptor.invoke(mockMethodInvocation);
|
||||||
@ -145,7 +146,7 @@ public class AuthorizationManagerBeforeReactiveMethodInterceptorTests {
|
|||||||
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
|
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
|
||||||
HandlingReactiveAuthorizationManager.class);
|
HandlingReactiveAuthorizationManager.class);
|
||||||
given(mockReactiveAuthorizationManager.check(any(), eq(mockMethodInvocation))).willReturn(Mono.empty());
|
given(mockReactiveAuthorizationManager.check(any(), eq(mockMethodInvocation))).willReturn(Mono.empty());
|
||||||
given(mockReactiveAuthorizationManager.handle(any(), any(AuthorizationResult.class)))
|
given(mockReactiveAuthorizationManager.handleDeniedInvocation(any(), any(AuthorizationResult.class)))
|
||||||
.willReturn(Mono.just("***"));
|
.willReturn(Mono.just("***"));
|
||||||
AuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor(
|
AuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor(
|
||||||
Pointcut.TRUE, mockReactiveAuthorizationManager);
|
Pointcut.TRUE, mockReactiveAuthorizationManager);
|
||||||
@ -164,7 +165,7 @@ public class AuthorizationManagerBeforeReactiveMethodInterceptorTests {
|
|||||||
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
|
HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(
|
||||||
HandlingReactiveAuthorizationManager.class);
|
HandlingReactiveAuthorizationManager.class);
|
||||||
given(mockReactiveAuthorizationManager.check(any(), eq(mockMethodInvocation))).willReturn(Mono.empty());
|
given(mockReactiveAuthorizationManager.check(any(), eq(mockMethodInvocation))).willReturn(Mono.empty());
|
||||||
given(mockReactiveAuthorizationManager.handle(any(), any(AuthorizationResult.class)))
|
given(mockReactiveAuthorizationManager.handleDeniedInvocation(any(), any(AuthorizationResult.class)))
|
||||||
.willReturn(Mono.just("***"));
|
.willReturn(Mono.just("***"));
|
||||||
AuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor(
|
AuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor(
|
||||||
Pointcut.TRUE, mockReactiveAuthorizationManager);
|
Pointcut.TRUE, mockReactiveAuthorizationManager);
|
||||||
|
@ -2256,14 +2256,13 @@ You can also add the Spring Boot property `spring.jackson.default-property-inclu
|
|||||||
[[fallback-values-authorization-denied]]
|
[[fallback-values-authorization-denied]]
|
||||||
== Providing Fallback Values When Authorization is Denied
|
== Providing Fallback Values When Authorization is Denied
|
||||||
|
|
||||||
There are some scenarios where you may not wish to throw an `AccessDeniedException` when a method is invoked without the required permissions.
|
There are some scenarios where you may not wish to throw an `AuthorizationDeniedException` when a method is invoked without the required permissions.
|
||||||
Instead, you might wish to return a post-processed result, like a masked result, or a default value in cases where access denied happened before invoking the method.
|
Instead, you might wish to return a post-processed result, like a masked result, or a default value in cases where authorization denied happened before invoking the method.
|
||||||
|
|
||||||
Spring Security provides support for handling and post-processing method access denied by combining {security-api-url}org/springframework/security/authorization/method/AuthorizationDeniedHandler.html[`@AuthorizationDeniedHandler`] with the <<authorizing-with-annotations,`@PreAuthorize` and `@PostAuthorize` annotations>> respectively.
|
Spring Security provides support for handling authorization denied on method invocation by using the {security-api-url}org/springframework/security/authorization/method/HandleAuthorizationDenied.html[`@HandleAuthorizationDenied`].
|
||||||
|
The handler works for denied authorizations that happened in the <<authorizing-with-annotations,`@PreAuthorize` and `@PostAuthorize` annotations>> as well as {security-api-url}org/springframework/security/authorization/AuthorizationDeniedException.html[`AuthorizationDeniedException`] thrown from the method invocation itself.
|
||||||
|
|
||||||
=== Using with `@PreAuthorize`
|
Let's consider the example from the <<authorize-object,previous section>>, but instead of creating the `AccessDeniedExceptionInterceptor` to transform an `AccessDeniedException` to a `null` return value, we will use the `handlerClass` attribute from `@HandleAuthorizationDenied`:
|
||||||
|
|
||||||
Let's consider the example from the <<authorize-object,previous section>>, but instead of creating the `AccessDeniedExceptionInterceptor` to transform an `AccessDeniedException` to a `null` return value, we will use the `handlerClass` attribute from `@AuthorizationDeniedHandler`:
|
|
||||||
|
|
||||||
[tabs]
|
[tabs]
|
||||||
======
|
======
|
||||||
@ -2274,7 +2273,7 @@ Java::
|
|||||||
public class NullMethodAuthorizationDeniedHandler implements MethodAuthorizationDeniedHandler { <1>
|
public class NullMethodAuthorizationDeniedHandler implements MethodAuthorizationDeniedHandler { <1>
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2295,7 +2294,7 @@ public class User {
|
|||||||
// ...
|
// ...
|
||||||
|
|
||||||
@PreAuthorize(value = "hasAuthority('user:read')")
|
@PreAuthorize(value = "hasAuthority('user:read')")
|
||||||
@AuthorizationDeniedHandler(handlerClass = NullMethodAuthorizationDeniedHandler.class)
|
@HandleAuthorizationDenied(handlerClass = NullMethodAuthorizationDeniedHandler.class)
|
||||||
public String getEmail() {
|
public String getEmail() {
|
||||||
return this.email;
|
return this.email;
|
||||||
}
|
}
|
||||||
@ -2308,7 +2307,7 @@ Kotlin::
|
|||||||
----
|
----
|
||||||
class NullMethodAuthorizationDeniedHandler : MethodAuthorizationDeniedHandler { <1>
|
class NullMethodAuthorizationDeniedHandler : MethodAuthorizationDeniedHandler { <1>
|
||||||
|
|
||||||
override fun handle(methodInvocation: MethodInvocation, authorizationResult: AuthorizationResult): Any {
|
override fun handleDeniedInvocation(methodInvocation: MethodInvocation, authorizationResult: AuthorizationResult): Any {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2325,13 +2324,13 @@ class SecurityConfig {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class User (val name:String, @PreAuthorize(value = "hasAuthority('user:read')") @AuthorizationDeniedHandler(handlerClass = NullMethodAuthorizationDeniedHandler::class) val email:String) <3>
|
class User (val name:String, @PreAuthorize(value = "hasAuthority('user:read')") @HandleAuthorizationDenied(handlerClass = NullMethodAuthorizationDeniedHandler::class) val email:String) <3>
|
||||||
----
|
----
|
||||||
======
|
======
|
||||||
|
|
||||||
<1> Create an implementation of `MethodAuthorizationDeniedHandler` that returns a `null` value
|
<1> Create an implementation of `MethodAuthorizationDeniedHandler` that returns a `null` value
|
||||||
<2> Register the `NullMethodAuthorizationDeniedHandler` as a bean
|
<2> Register the `NullMethodAuthorizationDeniedHandler` as a bean
|
||||||
<3> Annotate the method with `@AuthorizationDeniedHandler` and pass the `NullMethodAuthorizationDeniedHandler` to the `handlerClass` attribute
|
<3> Annotate the method with `@HandleAuthorizationDenied` and pass the `NullMethodAuthorizationDeniedHandler` to the `handlerClass` attribute
|
||||||
|
|
||||||
And then you can verify that a `null` value is returned instead of the `AccessDeniedException`:
|
And then you can verify that a `null` value is returned instead of the `AccessDeniedException`:
|
||||||
|
|
||||||
@ -2371,9 +2370,12 @@ fun getEmailWhenProxiedThenNullEmail() {
|
|||||||
----
|
----
|
||||||
======
|
======
|
||||||
|
|
||||||
=== Using with `@PostAuthorize`
|
=== Using the Denied Result From the Method Invocation
|
||||||
|
|
||||||
The same can be achieved with `@PostAuthorize`, however, since `@PostAuthorize` checks are performed after the method is invoked, we have access to the resulting value of the invocation, allowing you to provide fallback values based on the unauthorized results.
|
There are some scenarios where you might want to return a secure result derived from the denied result.
|
||||||
|
For example, if a user is not authorized to see email addresses, you might want to apply some masking on the original email address, i.e. _useremail@example.com_ would become _use\\******@example.com_.
|
||||||
|
|
||||||
|
For those scenarios, you can override the `handleDeniedInvocationResult` from the `MethodAuthorizationDeniedHandler`, which has the {security-api-url}org/springframework/security/authorization/method/MethodInvocationResult.html[`MethodInvocationResult`] as an argument.
|
||||||
Let's continue with the previous example, but instead of returning `null`, we will return a masked value of the email:
|
Let's continue with the previous example, but instead of returning `null`, we will return a masked value of the email:
|
||||||
|
|
||||||
[tabs]
|
[tabs]
|
||||||
@ -2382,10 +2384,15 @@ Java::
|
|||||||
+
|
+
|
||||||
[source,java,role="primary"]
|
[source,java,role="primary"]
|
||||||
----
|
----
|
||||||
public class EmailMaskingMethodAuthorizationDeniedPostProcessor implements MethodAuthorizationDeniedPostProcessor { <1>
|
public class EmailMaskingMethodAuthorizationDeniedHandler implements MethodAuthorizationDeniedHandler { <1>
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postProcessResult(MethodInvocationResult methodInvocationResult, AuthorizationResult authorizationResult) {
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
||||||
|
return "***";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult, AuthorizationResult authorizationResult) {
|
||||||
String email = (String) methodInvocationResult.getResult();
|
String email = (String) methodInvocationResult.getResult();
|
||||||
return email.replaceAll("(^[^@]{3}|(?!^)\\G)[^@]", "$1*");
|
return email.replaceAll("(^[^@]{3}|(?!^)\\G)[^@]", "$1*");
|
||||||
}
|
}
|
||||||
@ -2397,8 +2404,8 @@ public class EmailMaskingMethodAuthorizationDeniedPostProcessor implements Metho
|
|||||||
public class SecurityConfig {
|
public class SecurityConfig {
|
||||||
|
|
||||||
@Bean <2>
|
@Bean <2>
|
||||||
public EmailMaskingMethodAuthorizationDeniedPostProcessor emailMaskingMethodAuthorizationDeniedPostProcessor() {
|
public EmailMaskingMethodAuthorizationDeniedHandler emailMaskingMethodAuthorizationDeniedHandler() {
|
||||||
return new EmailMaskingMethodAuthorizationDeniedPostProcessor();
|
return new EmailMaskingMethodAuthorizationDeniedHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -2407,7 +2414,7 @@ public class User {
|
|||||||
// ...
|
// ...
|
||||||
|
|
||||||
@PostAuthorize(value = "hasAuthority('user:read')")
|
@PostAuthorize(value = "hasAuthority('user:read')")
|
||||||
@AuthorizationDeniedHandler(postProcessorClass = EmailMaskingMethodAuthorizationDeniedPostProcessor.class)
|
@HandleAuthorizationDenied(handlerClass = EmailMaskingMethodAuthorizationDeniedHandler.class)
|
||||||
public String getEmail() {
|
public String getEmail() {
|
||||||
return this.email;
|
return this.email;
|
||||||
}
|
}
|
||||||
@ -2418,9 +2425,13 @@ Kotlin::
|
|||||||
+
|
+
|
||||||
[source,kotlin,role="secondary"]
|
[source,kotlin,role="secondary"]
|
||||||
----
|
----
|
||||||
class EmailMaskingMethodAuthorizationDeniedPostProcessor : MethodAuthorizationDeniedPostProcessor {
|
class EmailMaskingMethodAuthorizationDeniedHandler : MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
override fun postProcessResult(methodInvocationResult: MethodInvocationResult, authorizationResult: AuthorizationResult): Any {
|
override fun handleDeniedInvocation(methodInvocation: MethodInvocation, authorizationResult: AuthorizationResult): Any {
|
||||||
|
return "***"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handleDeniedInvocationResult(methodInvocationResult: MethodInvocationResult, authorizationResult: AuthorizationResult): Any {
|
||||||
val email = methodInvocationResult.result as String
|
val email = methodInvocationResult.result as String
|
||||||
return email.replace("(^[^@]{3}|(?!^)\\G)[^@]".toRegex(), "$1*")
|
return email.replace("(^[^@]{3}|(?!^)\\G)[^@]".toRegex(), "$1*")
|
||||||
}
|
}
|
||||||
@ -2432,22 +2443,27 @@ class EmailMaskingMethodAuthorizationDeniedPostProcessor : MethodAuthorizationDe
|
|||||||
class SecurityConfig {
|
class SecurityConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
fun emailMaskingMethodAuthorizationDeniedPostProcessor(): EmailMaskingMethodAuthorizationDeniedPostProcessor {
|
fun emailMaskingMethodAuthorizationDeniedHandler(): EmailMaskingMethodAuthorizationDeniedHandler {
|
||||||
return EmailMaskingMethodAuthorizationDeniedPostProcessor()
|
return EmailMaskingMethodAuthorizationDeniedHandler()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class User (val name:String, @PostAuthorize(value = "hasAuthority('user:read')") @AuthorizationDeniedHandler(postProcessorClass = EmailMaskingMethodAuthorizationDeniedPostProcessor::class) val email:String) <3>
|
class User (val name:String, @PostAuthorize(value = "hasAuthority('user:read')") @HandleAuthorizationDenied(handlerClass = EmailMaskingMethodAuthorizationDeniedHandler::class) val email:String) <3>
|
||||||
----
|
----
|
||||||
======
|
======
|
||||||
|
|
||||||
<1> Create an implementation of `MethodAuthorizationDeniedPostProcessor` that returns a masked value of the unauthorized result value
|
<1> Create an implementation of `MethodAuthorizationDeniedHandler` that returns a masked value of the unauthorized result value
|
||||||
<2> Register the `EmailMaskingMethodAuthorizationDeniedPostProcessor` as a bean
|
<2> Register the `EmailMaskingMethodAuthorizationDeniedHandler` as a bean
|
||||||
<3> Annotate the method with `@AuthorizationDeniedHandler` and pass the `EmailMaskingMethodAuthorizationDeniedPostProcessor` to the `postProcessorClass` attribute
|
<3> Annotate the method with `@HandleAuthorizationDenied` and pass the `EmailMaskingMethodAuthorizationDeniedHandler` to the `handlerClass` attribute
|
||||||
|
|
||||||
And then you can verify that a masked email is returned instead of an `AccessDeniedException`:
|
And then you can verify that a masked email is returned instead of an `AccessDeniedException`:
|
||||||
|
|
||||||
|
[WARNING]
|
||||||
|
====
|
||||||
|
Since you have access to the original denied value, make sure that you correctly handle it and do not return it to the caller.
|
||||||
|
====
|
||||||
|
|
||||||
[tabs]
|
[tabs]
|
||||||
======
|
======
|
||||||
Java::
|
Java::
|
||||||
@ -2481,20 +2497,20 @@ fun getEmailWhenProxiedThenMaskedEmail() {
|
|||||||
----
|
----
|
||||||
======
|
======
|
||||||
|
|
||||||
When implementing the `MethodAuthorizationDeniedHandler` or the `MethodAuthorizationDeniedPostProcessor` you have a few options on what you can return:
|
When implementing the `MethodAuthorizationDeniedHandler` you have a few options on what type you can return:
|
||||||
|
|
||||||
- A `null` value.
|
- A `null` value.
|
||||||
- A non-null value, respecting the method's return type.
|
- A non-null value, respecting the method's return type.
|
||||||
- Throw an exception, usually an instance of `AccessDeniedException`. This is the default behavior.
|
- Throw an exception, usually an instance of `AuthorizationDeniedException`. This is the default behavior.
|
||||||
- A `Mono` type for reactive applications.
|
- A `Mono` type for reactive applications.
|
||||||
|
|
||||||
Note that since the handler and the post-processor must be registered as beans, you can inject dependencies into them if you need a more complex logic.
|
Note that since the handler must be registered as beans in your application context, you can inject dependencies into them if you need a more complex logic.
|
||||||
In addition to that, you have available the `MethodInvocation` or the `MethodInvocationResult`, as well as the `AuthorizationResult` for more details related to the authorization decision.
|
In addition to that, you have available the `MethodInvocation` or the `MethodInvocationResult`, as well as the `AuthorizationResult` for more details related to the authorization decision.
|
||||||
|
|
||||||
[[deciding-return-based-parameters]]
|
[[deciding-return-based-parameters]]
|
||||||
=== Deciding What to Return Based on Available Parameters
|
=== Deciding What to Return Based on Available Parameters
|
||||||
|
|
||||||
Consider a scenario where there might be multiple mask values for different methods, it would be not so productive if we had to create a handler or post-processor for each of those methods, although it is perfectly fine to do that.
|
Consider a scenario where there might be multiple mask values for different methods, it would be not so productive if we had to create a handler for each of those methods, although it is perfectly fine to do that.
|
||||||
In such cases, we can use the information passed via parameters to decide what to do.
|
In such cases, we can use the information passed via parameters to decide what to do.
|
||||||
For example, we can create a custom `@Mask` annotation and a handler that detects that annotation to decide what mask value to return:
|
For example, we can create a custom `@Mask` annotation and a handler that detects that annotation to decide what mask value to return:
|
||||||
|
|
||||||
@ -2517,7 +2533,7 @@ public @interface Mask {
|
|||||||
public class MaskAnnotationDeniedHandler implements MethodAuthorizationDeniedHandler {
|
public class MaskAnnotationDeniedHandler implements MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
||||||
Mask mask = AnnotationUtils.getAnnotation(methodInvocation.getMethod(), Mask.class);
|
Mask mask = AnnotationUtils.getAnnotation(methodInvocation.getMethod(), Mask.class);
|
||||||
return mask.value();
|
return mask.value();
|
||||||
}
|
}
|
||||||
@ -2539,14 +2555,14 @@ public class SecurityConfig {
|
|||||||
public class MyService {
|
public class MyService {
|
||||||
|
|
||||||
@PreAuthorize(value = "hasAuthority('user:read')")
|
@PreAuthorize(value = "hasAuthority('user:read')")
|
||||||
@AuthorizationDeniedHandler(handlerClass = MaskAnnotationDeniedHandler.class)
|
@HandleAuthorizationDenied(handlerClass = MaskAnnotationDeniedHandler.class)
|
||||||
@Mask("***")
|
@Mask("***")
|
||||||
public String foo() {
|
public String foo() {
|
||||||
return "foo";
|
return "foo";
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize(value = "hasAuthority('user:read')")
|
@PreAuthorize(value = "hasAuthority('user:read')")
|
||||||
@AuthorizationDeniedHandler(handlerClass = MaskAnnotationDeniedHandler.class)
|
@HandleAuthorizationDenied(handlerClass = MaskAnnotationDeniedHandler.class)
|
||||||
@Mask("???")
|
@Mask("???")
|
||||||
public String bar() {
|
public String bar() {
|
||||||
return "bar";
|
return "bar";
|
||||||
@ -2567,7 +2583,7 @@ annotation class Mask(val value: String)
|
|||||||
|
|
||||||
class MaskAnnotationDeniedHandler : MethodAuthorizationDeniedHandler {
|
class MaskAnnotationDeniedHandler : MethodAuthorizationDeniedHandler {
|
||||||
|
|
||||||
override fun handle(methodInvocation: MethodInvocation, authorizationResult: AuthorizationResult): Any {
|
override fun handleDeniedInvocation(methodInvocation: MethodInvocation, authorizationResult: AuthorizationResult): Any {
|
||||||
val mask = AnnotationUtils.getAnnotation(methodInvocation.method, Mask::class.java)
|
val mask = AnnotationUtils.getAnnotation(methodInvocation.method, Mask::class.java)
|
||||||
return mask.value
|
return mask.value
|
||||||
}
|
}
|
||||||
@ -2589,14 +2605,14 @@ class SecurityConfig {
|
|||||||
class MyService {
|
class MyService {
|
||||||
|
|
||||||
@PreAuthorize(value = "hasAuthority('user:read')")
|
@PreAuthorize(value = "hasAuthority('user:read')")
|
||||||
@AuthorizationDeniedHandler(handlerClass = MaskAnnotationDeniedHandler::class)
|
@HandleAuthorizationDenied(handlerClass = MaskAnnotationDeniedHandler::class)
|
||||||
@Mask("***")
|
@Mask("***")
|
||||||
fun foo(): String {
|
fun foo(): String {
|
||||||
return "foo"
|
return "foo"
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize(value = "hasAuthority('user:read')")
|
@PreAuthorize(value = "hasAuthority('user:read')")
|
||||||
@AuthorizationDeniedHandler(handlerClass = MaskAnnotationDeniedHandler::class)
|
@HandleAuthorizationDenied(handlerClass = MaskAnnotationDeniedHandler::class)
|
||||||
@Mask("???")
|
@Mask("???")
|
||||||
fun bar(): String {
|
fun bar(): String {
|
||||||
return "bar"
|
return "bar"
|
||||||
@ -2653,8 +2669,8 @@ fun barWhenDeniedThenReturnQuestionMarks() {
|
|||||||
|
|
||||||
=== Combining with Meta Annotation Support
|
=== Combining with Meta Annotation Support
|
||||||
|
|
||||||
You can also combine the `@AuthorizationDeniedHandler` with other annotations in order to reduce and simplify the annotations in a method.
|
You can also combine the `@HandleAuthorizationDenied` with other annotations in order to reduce and simplify the annotations in a method.
|
||||||
Let's consider the <<deciding-return-based-parameters,example from the previous section>> and merge `@AuthorizationDeniedHandler` with `@Mask`:
|
Let's consider the <<deciding-return-based-parameters,example from the previous section>> and merge `@HandleAuthorizationDenied` with `@Mask`:
|
||||||
|
|
||||||
[tabs]
|
[tabs]
|
||||||
======
|
======
|
||||||
@ -2664,7 +2680,7 @@ Java::
|
|||||||
----
|
----
|
||||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@AuthorizationDeniedHandler(handlerClass = MaskAnnotationDeniedHandler.class)
|
@HandleAuthorizationDenied(handlerClass = MaskAnnotationDeniedHandler.class)
|
||||||
public @interface Mask {
|
public @interface Mask {
|
||||||
|
|
||||||
String value();
|
String value();
|
||||||
@ -2683,7 +2699,7 @@ Kotlin::
|
|||||||
----
|
----
|
||||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
|
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
@AuthorizationDeniedHandler(handlerClass = MaskAnnotationDeniedHandler::class)
|
@HandleAuthorizationDenied(handlerClass = MaskAnnotationDeniedHandler::class)
|
||||||
annotation class Mask(val value: String)
|
annotation class Mask(val value: String)
|
||||||
|
|
||||||
@Mask("***")
|
@Mask("***")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user