Просмотр исходного кода

AuthorizationEventPublisher Accepts AuthorizationResult

Closes gh-15915

Co-authored-by: Max Batischev <mblancer@mail.ru>
Josh Cummings 10 месяцев назад
Родитель
Сommit
702538ebce
17 измененных файлов с 196 добавлено и 33 удалено
  1. 4 0
      config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java
  2. 5 1
      config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java
  3. 30 1
      core/src/main/java/org/springframework/security/authorization/AuthorizationEventPublisher.java
  4. 8 2
      core/src/main/java/org/springframework/security/authorization/SpringAuthorizationEventPublisher.java
  5. 12 0
      core/src/main/java/org/springframework/security/authorization/event/AuthorizationDeniedEvent.java
  6. 37 5
      core/src/main/java/org/springframework/security/authorization/event/AuthorizationEvent.java
  7. 13 0
      core/src/main/java/org/springframework/security/authorization/event/AuthorizationGrantedEvent.java
  8. 1 6
      core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptor.java
  9. 1 6
      core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptor.java
  10. 44 0
      core/src/main/java/org/springframework/security/authorization/method/NoOpAuthorizationEventPublisher.java
  11. 4 1
      core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptorTests.java
  12. 3 1
      core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptorTests.java
  13. 0 1
      gradle.properties
  14. 6 0
      messaging/src/main/java/org/springframework/security/messaging/access/intercept/AuthorizationChannelInterceptor.java
  15. 6 1
      messaging/src/test/java/org/springframework/security/messaging/access/intercept/AuthorizationChannelInterceptorTests.java
  16. 18 7
      web/src/main/java/org/springframework/security/web/access/intercept/AuthorizationFilter.java
  17. 4 1
      web/src/test/java/org/springframework/security/web/access/intercept/AuthorizationFilterTests.java

+ 4 - 0
config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java

@@ -75,6 +75,7 @@ import org.springframework.security.access.prepost.PreFilter;
 import org.springframework.security.authorization.AuthorizationDecision;
 import org.springframework.security.authorization.AuthorizationEventPublisher;
 import org.springframework.security.authorization.AuthorizationManager;
+import org.springframework.security.authorization.AuthorizationResult;
 import org.springframework.security.authorization.method.AuthorizationAdvisor;
 import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;
 import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory.TargetVisitor;
@@ -110,6 +111,7 @@ import static org.assertj.core.api.Assertions.assertThatNoException;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
@@ -1293,6 +1295,8 @@ public class PrePostMethodSecurityConfigurationTests {
 
 		@Bean
 		AuthorizationEventPublisher authorizationEventPublisher() {
+			doCallRealMethod().when(this.publisher)
+				.publishAuthorizationEvent(any(), any(), any(AuthorizationResult.class));
 			return this.publisher;
 		}
 

+ 5 - 1
config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2023 the original author or authors.
+ * 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.
@@ -42,6 +42,7 @@ import org.springframework.security.authorization.AuthorizationDecision;
 import org.springframework.security.authorization.AuthorizationEventPublisher;
 import org.springframework.security.authorization.AuthorizationManager;
 import org.springframework.security.authorization.AuthorizationObservationContext;
+import org.springframework.security.authorization.AuthorizationResult;
 import org.springframework.security.config.ObjectPostProcessor;
 import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -78,6 +79,7 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -1241,6 +1243,8 @@ public class AuthorizeHttpRequestsConfigurerTests {
 
 		@Bean
 		AuthorizationEventPublisher authorizationEventPublisher() {
+			doCallRealMethod().when(this.publisher)
+				.publishAuthorizationEvent(any(), any(), any(AuthorizationResult.class));
 			return this.publisher;
 		}
 

+ 30 - 1
core/src/main/java/org/springframework/security/authorization/AuthorizationEventPublisher.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2022 the original author or authors.
+ * 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.
@@ -42,8 +42,37 @@ public interface AuthorizationEventPublisher {
 	 * @param object the secured object
 	 * @param decision the decision about whether the user may access the secured object
 	 * @param <T> the secured object's type
+	 * @deprecated use
+	 * {@link #publishAuthorizationEvent(Supplier, Object, AuthorizationResult)} instead
 	 */
+	@Deprecated
 	<T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
 			AuthorizationDecision decision);
 
+	/**
+	 * Publish the given details in the form of an event, typically
+	 * {@link AuthorizationGrantedEvent} or {@link AuthorizationDeniedEvent}.
+	 *
+	 * Note that success events can be very noisy if enabled by default. Because of this
+	 * implementations may choose to drop success events by default.
+	 * @param authentication a {@link Supplier} for the current user
+	 * @param object the secured object
+	 * @param result {@link AuthorizationResult} the result about whether the user may
+	 * access the secured object
+	 * @param <T> the secured object's type
+	 * @since 6.4
+	 */
+	default <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
+			AuthorizationResult result) {
+		if (result == null) {
+			publishAuthorizationEvent(authentication, object, null);
+			return;
+		}
+		if (result instanceof AuthorizationDecision decision) {
+			publishAuthorizationEvent(authentication, object, decision);
+			return;
+		}
+		throw new UnsupportedOperationException("result must be of type AuthorizationDecision");
+	}
+
 }

+ 8 - 2
core/src/main/java/org/springframework/security/authorization/SpringAuthorizationEventPublisher.java

@@ -55,10 +55,16 @@ public final class SpringAuthorizationEventPublisher implements AuthorizationEve
 	@Override
 	public <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
 			AuthorizationDecision decision) {
-		if (decision == null || decision.isGranted()) {
+		publishAuthorizationEvent(authentication, object, (AuthorizationResult) decision);
+	}
+
+	@Override
+	public <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
+			AuthorizationResult result) {
+		if (result == null || result.isGranted()) {
 			return;
 		}
-		AuthorizationDeniedEvent<T> failure = new AuthorizationDeniedEvent<>(authentication, object, decision);
+		AuthorizationDeniedEvent<T> failure = new AuthorizationDeniedEvent<>(authentication, object, result);
 		this.eventPublisher.publishEvent(failure);
 	}
 

+ 12 - 0
core/src/main/java/org/springframework/security/authorization/event/AuthorizationDeniedEvent.java

@@ -20,6 +20,7 @@ import java.util.function.Supplier;
 
 import org.springframework.context.ApplicationEvent;
 import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.AuthorizationResult;
 import org.springframework.security.core.Authentication;
 
 /**
@@ -31,10 +32,21 @@ import org.springframework.security.core.Authentication;
  */
 public class AuthorizationDeniedEvent<T> extends AuthorizationEvent {
 
+	/**
+	 * @deprecated Please use an {@link AuthorizationResult} constructor instead
+	 */
+	@Deprecated
 	public AuthorizationDeniedEvent(Supplier<Authentication> authentication, T object, AuthorizationDecision decision) {
 		super(authentication, object, decision);
 	}
 
+	/**
+	 * @since 6.4
+	 */
+	public AuthorizationDeniedEvent(Supplier<Authentication> authentication, T object, AuthorizationResult result) {
+		super(authentication, object, result);
+	}
+
 	/**
 	 * Get the object to which access was requested
 	 * @return the object to which access was requested

+ 37 - 5
core/src/main/java/org/springframework/security/authorization/event/AuthorizationEvent.java

@@ -20,6 +20,7 @@ import java.util.function.Supplier;
 
 import org.springframework.context.ApplicationEvent;
 import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.AuthorizationResult;
 import org.springframework.security.core.Authentication;
 import org.springframework.util.Assert;
 
@@ -34,19 +35,32 @@ public class AuthorizationEvent extends ApplicationEvent {
 
 	private final Supplier<Authentication> authentication;
 
-	private final AuthorizationDecision decision;
+	private final AuthorizationResult result;
 
 	/**
 	 * Construct an {@link AuthorizationEvent}
 	 * @param authentication the principal requiring access
 	 * @param object the object to which access was requested
-	 * @param decision whether authorization was granted or denied
+	 * @param result whether authorization was granted or denied
 	 */
-	public AuthorizationEvent(Supplier<Authentication> authentication, Object object, AuthorizationDecision decision) {
+	public AuthorizationEvent(Supplier<Authentication> authentication, Object object, AuthorizationDecision result) {
 		super(object);
 		Assert.notNull(authentication, "authentication supplier cannot be null");
 		this.authentication = authentication;
-		this.decision = decision;
+		this.result = result;
+	}
+
+	/**
+	 * Construct an {@link AuthorizationEvent}
+	 * @param authentication the principal requiring access
+	 * @param object the object to which access was requested
+	 * @param result whether authorization was granted or denied
+	 */
+	public AuthorizationEvent(Supplier<Authentication> authentication, Object object, AuthorizationResult result) {
+		super(object);
+		Assert.notNull(authentication, "authentication supplier cannot be null");
+		this.authentication = authentication;
+		this.result = result;
 	}
 
 	/**
@@ -68,9 +82,27 @@ public class AuthorizationEvent extends ApplicationEvent {
 	/**
 	 * Get the response to the principal's request
 	 * @return the response to the principal's request
+	 * @deprecated please use {@link #getAuthorizationResult()}
 	 */
+	@Deprecated
 	public AuthorizationDecision getAuthorizationDecision() {
-		return this.decision;
+		if (this.result == null) {
+			return null;
+		}
+		if (this.result instanceof AuthorizationDecision decision) {
+			return decision;
+		}
+		throw new IllegalArgumentException(
+				"Please either call getAuthorizationResult or ensure that the result is of type AuthorizationDecision");
+	}
+
+	/**
+	 * Get the response to the principal's request
+	 * @return the response to the principal's request
+	 * @since 6.4
+	 */
+	public AuthorizationResult getAuthorizationResult() {
+		return this.result;
 	}
 
 }

+ 13 - 0
core/src/main/java/org/springframework/security/authorization/event/AuthorizationGrantedEvent.java

@@ -20,6 +20,7 @@ import java.util.function.Supplier;
 
 import org.springframework.context.ApplicationEvent;
 import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.AuthorizationResult;
 import org.springframework.security.core.Authentication;
 
 /**
@@ -31,11 +32,23 @@ import org.springframework.security.core.Authentication;
  */
 public class AuthorizationGrantedEvent<T> extends AuthorizationEvent {
 
+	/**
+	 * @deprecated please use a constructor that takes an
+	 * {@link org.springframework.security.authorization.AuthorizationResult}
+	 */
+	@Deprecated
 	public AuthorizationGrantedEvent(Supplier<Authentication> authentication, T object,
 			AuthorizationDecision decision) {
 		super(authentication, object, decision);
 	}
 
+	/**
+	 * @since 6.4
+	 */
+	public AuthorizationGrantedEvent(Supplier<Authentication> authentication, T object, AuthorizationResult result) {
+		super(authentication, object, result);
+	}
+
 	/**
 	 * Get the object to which access was requested
 	 * @return the object to which access was requested

+ 1 - 6
core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptor.java

@@ -60,7 +60,7 @@ public final class AuthorizationManagerAfterMethodInterceptor implements Authori
 
 	private int order;
 
-	private AuthorizationEventPublisher eventPublisher = AuthorizationManagerAfterMethodInterceptor::noPublish;
+	private AuthorizationEventPublisher eventPublisher = new NoOpAuthorizationEventPublisher();
 
 	/**
 	 * Creates an instance.
@@ -209,9 +209,4 @@ public final class AuthorizationManagerAfterMethodInterceptor implements Authori
 		return authentication;
 	}
 
-	private static <T> void noPublish(Supplier<Authentication> authentication, T object,
-			AuthorizationDecision decision) {
-
-	}
-
 }

+ 1 - 6
core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptor.java

@@ -65,7 +65,7 @@ public final class AuthorizationManagerBeforeMethodInterceptor implements Author
 
 	private int order = AuthorizationInterceptorsOrder.FIRST.getOrder();
 
-	private AuthorizationEventPublisher eventPublisher = AuthorizationManagerBeforeMethodInterceptor::noPublish;
+	private AuthorizationEventPublisher eventPublisher = new NoOpAuthorizationEventPublisher();
 
 	/**
 	 * Creates an instance.
@@ -299,9 +299,4 @@ public final class AuthorizationManagerBeforeMethodInterceptor implements Author
 		return authentication;
 	}
 
-	private static <T> void noPublish(Supplier<Authentication> authentication, T object,
-			AuthorizationDecision decision) {
-
-	}
-
 }

+ 44 - 0
core/src/main/java/org/springframework/security/authorization/method/NoOpAuthorizationEventPublisher.java

@@ -0,0 +1,44 @@
+/*
+ * 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 java.util.function.Supplier;
+
+import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.AuthorizationEventPublisher;
+import org.springframework.security.authorization.AuthorizationResult;
+import org.springframework.security.core.Authentication;
+
+/**
+ * An {@link AuthorizationEventPublisher} implementation that does nothing.
+ *
+ * @author Max Batischev
+ * @since 6.4
+ */
+final class NoOpAuthorizationEventPublisher implements AuthorizationEventPublisher {
+
+	@Override
+	public <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
+			AuthorizationDecision decision) {
+	}
+
+	@Override
+	public <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
+			AuthorizationResult result) {
+	}
+
+}

+ 4 - 1
core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptorTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2023 the original author or authors.
+ * 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.
@@ -42,6 +42,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
@@ -127,6 +128,8 @@ public class AuthorizationManagerAfterMethodInterceptorTests {
 		AuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor(
 				Pointcut.TRUE, AuthenticatedAuthorizationManager.authenticated());
 		AuthorizationEventPublisher eventPublisher = mock(AuthorizationEventPublisher.class);
+		doCallRealMethod().when(eventPublisher)
+			.publishAuthorizationEvent(any(Supplier.class), any(), any(AuthorizationResult.class));
 		advice.setAuthorizationEventPublisher(eventPublisher);
 
 		SecurityContext securityContext = new SecurityContextImpl();

+ 3 - 1
core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptorTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2023 the original author or authors.
+ * 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.
@@ -41,6 +41,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
@@ -121,6 +122,7 @@ public class AuthorizationManagerBeforeMethodInterceptorTests {
 		AuthorizationManagerBeforeMethodInterceptor advice = new AuthorizationManagerBeforeMethodInterceptor(
 				Pointcut.TRUE, AuthenticatedAuthorizationManager.authenticated());
 		AuthorizationEventPublisher eventPublisher = mock(AuthorizationEventPublisher.class);
+		doCallRealMethod().when(eventPublisher).publishAuthorizationEvent(any(), any(), any(AuthorizationResult.class));
 		advice.setAuthorizationEventPublisher(eventPublisher);
 
 		SecurityContext securityContext = new SecurityContextImpl();

+ 0 - 1
gradle.properties

@@ -13,7 +13,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-
 springBootVersion=3.3.3
 version=6.4.0-SNAPSHOT
 samplesBranch=main

+ 6 - 0
messaging/src/main/java/org/springframework/security/messaging/access/intercept/AuthorizationChannelInterceptor.java

@@ -30,6 +30,7 @@ import org.springframework.security.authentication.AuthenticationCredentialsNotF
 import org.springframework.security.authorization.AuthorizationDecision;
 import org.springframework.security.authorization.AuthorizationEventPublisher;
 import org.springframework.security.authorization.AuthorizationManager;
+import org.springframework.security.authorization.AuthorizationResult;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.context.SecurityContextHolderStrategy;
@@ -114,6 +115,11 @@ public final class AuthorizationChannelInterceptor implements ChannelInterceptor
 
 		}
 
+		@Override
+		public <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
+				AuthorizationResult result) {
+		}
+
 	}
 
 }

+ 6 - 1
messaging/src/test/java/org/springframework/security/messaging/access/intercept/AuthorizationChannelInterceptorTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2022 the original author or authors.
+ * 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.
@@ -30,6 +30,7 @@ import org.springframework.security.authentication.TestingAuthenticationToken;
 import org.springframework.security.authorization.AuthorizationDecision;
 import org.springframework.security.authorization.AuthorizationEventPublisher;
 import org.springframework.security.authorization.AuthorizationManager;
+import org.springframework.security.authorization.AuthorizationResult;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
 
@@ -38,6 +39,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.lenient;
 import static org.mockito.Mockito.verify;
 
 /**
@@ -102,6 +104,9 @@ public class AuthorizationChannelInterceptorTests {
 	public void preSendWhenAuthorizationEventPublisherThenPublishes() {
 		this.interceptor.setAuthorizationEventPublisher(this.eventPublisher);
 		given(this.authorizationManager.check(any(), any())).willReturn(new AuthorizationDecision(true));
+		lenient().doCallRealMethod()
+			.when(this.eventPublisher)
+			.publishAuthorizationEvent(any(), any(), any(AuthorizationResult.class));
 		this.interceptor.preSend(this.message, this.channel);
 		verify(this.eventPublisher).publishAuthorizationEvent(any(), any(), any());
 	}

+ 18 - 7
web/src/main/java/org/springframework/security/web/access/intercept/AuthorizationFilter.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2023 the original author or authors.
+ * 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.
@@ -33,6 +33,7 @@ import org.springframework.security.authorization.AuthorizationDecision;
 import org.springframework.security.authorization.AuthorizationDeniedException;
 import org.springframework.security.authorization.AuthorizationEventPublisher;
 import org.springframework.security.authorization.AuthorizationManager;
+import org.springframework.security.authorization.AuthorizationResult;
 import org.springframework.security.authorization.event.AuthorizationDeniedEvent;
 import org.springframework.security.authorization.event.AuthorizationGrantedEvent;
 import org.springframework.security.core.Authentication;
@@ -55,7 +56,7 @@ public class AuthorizationFilter extends GenericFilterBean {
 
 	private final AuthorizationManager<HttpServletRequest> authorizationManager;
 
-	private AuthorizationEventPublisher eventPublisher = AuthorizationFilter::noPublish;
+	private AuthorizationEventPublisher eventPublisher = new NoopAuthorizationEventPublisher();
 
 	private boolean observeOncePerRequest = false;
 
@@ -195,11 +196,6 @@ public class AuthorizationFilter extends GenericFilterBean {
 		this.filterAsyncDispatch = shouldFilterAllDispatcherTypes;
 	}
 
-	private static <T> void noPublish(Supplier<Authentication> authentication, T object,
-			AuthorizationDecision decision) {
-
-	}
-
 	public boolean isObserveOncePerRequest() {
 		return this.observeOncePerRequest;
 	}
@@ -235,4 +231,19 @@ public class AuthorizationFilter extends GenericFilterBean {
 		this.filterAsyncDispatch = filterAsyncDispatch;
 	}
 
+	private static class NoopAuthorizationEventPublisher implements AuthorizationEventPublisher {
+
+		@Override
+		public <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
+				AuthorizationDecision decision) {
+
+		}
+
+		@Override
+		public <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
+				AuthorizationResult result) {
+		}
+
+	}
+
 }

+ 4 - 1
web/src/test/java/org/springframework/security/web/access/intercept/AuthorizationFilterTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2022 the original author or authors.
+ * 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.
@@ -38,6 +38,7 @@ import org.springframework.security.authorization.AuthenticatedAuthorizationMana
 import org.springframework.security.authorization.AuthorizationDecision;
 import org.springframework.security.authorization.AuthorizationEventPublisher;
 import org.springframework.security.authorization.AuthorizationManager;
+import org.springframework.security.authorization.AuthorizationResult;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolder;
@@ -53,6 +54,7 @@ import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.BDDMockito.given;
 import static org.mockito.BDDMockito.willThrow;
+import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -186,6 +188,7 @@ public class AuthorizationFilterTests {
 		SecurityContextHolder.setContext(securityContext);
 
 		AuthorizationEventPublisher eventPublisher = mock(AuthorizationEventPublisher.class);
+		doCallRealMethod().when(eventPublisher).publishAuthorizationEvent(any(), any(), any(AuthorizationResult.class));
 		authorizationFilter.setAuthorizationEventPublisher(eventPublisher);
 		authorizationFilter.doFilter(mockRequest, mockResponse, mockFilterChain);
 		verify(eventPublisher).publishAuthorizationEvent(any(Supplier.class), any(HttpServletRequest.class),