浏览代码

Polish Authorization Event Support

- Added spring-security-config support
- Renamed classes
- Changed contracts to include the authenticated user and secured
object
- Added method security support

Issue gh-9288
Josh Cummings 3 年之前
父节点
当前提交
bdd5f86526
共有 19 个文件被更改,包括 497 次插入239 次删除
  1. 14 8
      config/src/main/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfiguration.java
  2. 12 1
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java
  3. 41 1
      config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java
  4. 34 7
      config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java
  5. 25 5
      core/src/main/java/org/springframework/security/authorization/AuthorizationEventPublisher.java
  6. 0 61
      core/src/main/java/org/springframework/security/authorization/DefaultAuthorizationEventPublisher.java
  7. 65 0
      core/src/main/java/org/springframework/security/authorization/SpringAuthorizationEventPublisher.java
  8. 23 5
      core/src/main/java/org/springframework/security/authorization/event/AuthorizationDeniedEvent.java
  9. 26 5
      core/src/main/java/org/springframework/security/authorization/event/AuthorizationGrantedEvent.java
  10. 23 3
      core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptor.java
  11. 21 1
      core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptor.java
  12. 0 70
      core/src/test/java/org/springframework/security/authorization/DefaultAuthorizationEventPublisherTests.java
  13. 67 0
      core/src/test/java/org/springframework/security/authorization/SpringAuthorizationEventPublisherTests.java
  14. 38 1
      core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptorTests.java
  15. 40 1
      core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptorTests.java
  16. 31 2
      web/src/main/java/org/springframework/security/web/access/intercept/AuthorizationFilter.java
  17. 1 26
      web/src/main/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManager.java
  18. 36 3
      web/src/test/java/org/springframework/security/web/access/intercept/AuthorizationFilterTests.java
  19. 0 39
      web/src/test/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManagerTests.java

+ 14 - 8
config/src/main/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfiguration.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -17,16 +17,16 @@
 package org.springframework.security.config.annotation.method.configuration;
 
 import org.springframework.aop.Advisor;
-import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Role;
 import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
 import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
+import org.springframework.security.authorization.AuthorizationEventPublisher;
+import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
 import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;
 import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
 import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager;
@@ -45,7 +45,7 @@ import org.springframework.security.config.core.GrantedAuthorityDefaults;
  */
 @Configuration(proxyBeanMethods = false)
 @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
-final class PrePostMethodSecurityConfiguration implements ApplicationContextAware {
+final class PrePostMethodSecurityConfiguration {
 
 	private final PreFilterAuthorizationMethodInterceptor preFilterAuthorizationMethodInterceptor = new PreFilterAuthorizationMethodInterceptor();
 
@@ -61,7 +61,8 @@ final class PrePostMethodSecurityConfiguration implements ApplicationContextAwar
 
 	private final DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
 
-	PrePostMethodSecurityConfiguration() {
+	@Autowired
+	PrePostMethodSecurityConfiguration(ApplicationContext context) {
 		this.preAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);
 		this.preAuthorizeAuthorizationMethodInterceptor = AuthorizationManagerBeforeMethodInterceptor
 				.preAuthorize(this.preAuthorizeAuthorizationManager);
@@ -70,6 +71,10 @@ final class PrePostMethodSecurityConfiguration implements ApplicationContextAwar
 				.postAuthorize(this.postAuthorizeAuthorizationManager);
 		this.preFilterAuthorizationMethodInterceptor.setExpressionHandler(this.expressionHandler);
 		this.postFilterAuthorizationMethodInterceptor.setExpressionHandler(this.expressionHandler);
+		this.expressionHandler.setApplicationContext(context);
+		AuthorizationEventPublisher publisher = new SpringAuthorizationEventPublisher(context);
+		this.preAuthorizeAuthorizationMethodInterceptor.setAuthorizationEventPublisher(publisher);
+		this.postAuthorizeAuthorizaitonMethodInterceptor.setAuthorizationEventPublisher(publisher);
 	}
 
 	@Bean
@@ -109,9 +114,10 @@ final class PrePostMethodSecurityConfiguration implements ApplicationContextAwar
 		this.expressionHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());
 	}
 
-	@Override
-	public void setApplicationContext(ApplicationContext context) throws BeansException {
-		this.expressionHandler.setApplicationContext(context);
+	@Autowired(required = false)
+	void setAuthorizationEventPublisher(AuthorizationEventPublisher eventPublisher) {
+		this.preAuthorizeAuthorizationMethodInterceptor.setAuthorizationEventPublisher(eventPublisher);
+		this.postAuthorizeAuthorizaitonMethodInterceptor.setAuthorizationEventPublisher(eventPublisher);
 	}
 
 }

+ 12 - 1
config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -26,7 +26,9 @@ import org.springframework.http.HttpMethod;
 import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
 import org.springframework.security.authorization.AuthorityAuthorizationManager;
 import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.AuthorizationEventPublisher;
 import org.springframework.security.authorization.AuthorizationManager;
+import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
 import org.springframework.security.config.annotation.ObjectPostProcessor;
 import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
 import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
@@ -52,12 +54,20 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
 
 	private final AuthorizationManagerRequestMatcherRegistry registry;
 
+	private final AuthorizationEventPublisher publisher;
+
 	/**
 	 * Creates an instance.
 	 * @param context the {@link ApplicationContext} to use
 	 */
 	public AuthorizeHttpRequestsConfigurer(ApplicationContext context) {
 		this.registry = new AuthorizationManagerRequestMatcherRegistry(context);
+		if (context.getBeanNamesForType(AuthorizationEventPublisher.class).length > 0) {
+			this.publisher = context.getBean(AuthorizationEventPublisher.class);
+		}
+		else {
+			this.publisher = new SpringAuthorizationEventPublisher(context);
+		}
 	}
 
 	/**
@@ -74,6 +84,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
 	public void configure(H http) {
 		AuthorizationManager<HttpServletRequest> authorizationManager = this.registry.createAuthorizationManager();
 		AuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager);
+		authorizationFilter.setAuthorizationEventPublisher(this.publisher);
 		http.addFilter(postProcess(authorizationFilter));
 	}
 

+ 41 - 1
config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -20,6 +20,7 @@ import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.function.Supplier;
 
 import org.aopalliance.intercept.MethodInterceptor;
 import org.aopalliance.intercept.MethodInvocation;
@@ -32,6 +33,7 @@ import org.springframework.aop.support.JdkRegexpMethodPointcut;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Role;
 import org.springframework.core.annotation.AnnotationConfigurationException;
 import org.springframework.security.access.AccessDeniedException;
@@ -43,9 +45,11 @@ import org.springframework.security.access.annotation.Jsr250BusinessServiceImpl;
 import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
 import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
 import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.AuthorizationEventPublisher;
 import org.springframework.security.authorization.AuthorizationManager;
 import org.springframework.security.authorization.method.AuthorizationInterceptorsOrder;
 import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
+import org.springframework.security.authorization.method.MethodInvocationResult;
 import org.springframework.security.config.core.GrantedAuthorityDefaults;
 import org.springframework.security.config.test.SpringTestContext;
 import org.springframework.security.config.test.SpringTestContextExtension;
@@ -58,6 +62,9 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 
 /**
  * Tests for {@link PrePostMethodSecurityConfiguration}.
@@ -350,6 +357,27 @@ public class PrePostMethodSecurityConfigurationTests {
 				.isThrownBy(() -> this.businessService.repeatedAnnotations());
 	}
 
+	@WithMockUser
+	@Test
+	public void preAuthorizeWhenAuthorizationEventPublisherThenUses() {
+		this.spring.register(MethodSecurityServiceConfig.class, AuthorizationEventPublisherConfig.class).autowire();
+		assertThatExceptionOfType(AccessDeniedException.class)
+				.isThrownBy(() -> this.methodSecurityService.preAuthorize());
+		AuthorizationEventPublisher publisher = this.spring.getContext().getBean(AuthorizationEventPublisher.class);
+		verify(publisher).publishAuthorizationEvent(any(Supplier.class), any(MethodInvocation.class),
+				any(AuthorizationDecision.class));
+	}
+
+	@WithMockUser
+	@Test
+	public void postAuthorizeWhenAuthorizationEventPublisherThenUses() {
+		this.spring.register(MethodSecurityServiceConfig.class, AuthorizationEventPublisherConfig.class).autowire();
+		this.methodSecurityService.postAnnotation("grant");
+		AuthorizationEventPublisher publisher = this.spring.getContext().getBean(AuthorizationEventPublisher.class);
+		verify(publisher).publishAuthorizationEvent(any(Supplier.class), any(MethodInvocationResult.class),
+				any(AuthorizationDecision.class));
+	}
+
 	// gh-10305
 	@WithMockUser
 	@Test
@@ -484,4 +512,16 @@ public class PrePostMethodSecurityConfigurationTests {
 
 	}
 
+	@Configuration
+	static class AuthorizationEventPublisherConfig {
+
+		private final AuthorizationEventPublisher publisher = mock(AuthorizationEventPublisher.class);
+
+		@Bean
+		AuthorizationEventPublisher authorizationEventPublisher() {
+			return this.publisher;
+		}
+
+	}
+
 }

+ 34 - 7
config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -16,12 +16,18 @@
 
 package org.springframework.security.config.annotation.web.configurers;
 
+import java.util.function.Supplier;
+
+import jakarta.servlet.http.HttpServletRequest;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 
 import org.springframework.beans.factory.BeanCreationException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.AuthorizationEventPublisher;
 import org.springframework.security.authorization.AuthorizationManager;
 import org.springframework.security.config.annotation.ObjectPostProcessor;
 import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
@@ -129,9 +135,9 @@ public class AuthorizeHttpRequestsConfigurerTests {
 	@Test
 	public void configureWhenObjectPostProcessorRegisteredThenInvokedOnAuthorizationManagerAndAuthorizationFilter() {
 		this.spring.register(ObjectPostProcessorConfig.class).autowire();
-		verify(ObjectPostProcessorConfig.objectPostProcessor)
-				.postProcess(any(RequestMatcherDelegatingAuthorizationManager.class));
-		verify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(AuthorizationFilter.class));
+		ObjectPostProcessor objectPostProcessor = this.spring.getContext().getBean(ObjectPostProcessor.class);
+		verify(objectPostProcessor).postProcess(any(RequestMatcherDelegatingAuthorizationManager.class));
+		verify(objectPostProcessor).postProcess(any(AuthorizationFilter.class));
 	}
 
 	@Test
@@ -369,6 +375,15 @@ public class AuthorizeHttpRequestsConfigurerTests {
 		this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
 	}
 
+	@Test
+	public void getWhenCustomAuthorizationEventPublisherThenUses() throws Exception {
+		this.spring.register(AuthenticatedConfig.class, AuthorizationEventPublisherConfig.class).autowire();
+		this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
+		AuthorizationEventPublisher publisher = this.spring.getContext().getBean(AuthorizationEventPublisher.class);
+		verify(publisher).publishAuthorizationEvent(any(Supplier.class), any(HttpServletRequest.class),
+				any(AuthorizationDecision.class));
+	}
+
 	@Test
 	public void getWhenAnyRequestAuthenticatedConfiguredAndUserLoggedInThenRespondsWithOk() throws Exception {
 		this.spring.register(AuthenticatedConfig.class, BasicController.class).autowire();
@@ -495,7 +510,7 @@ public class AuthorizeHttpRequestsConfigurerTests {
 	@EnableWebSecurity
 	static class ObjectPostProcessorConfig {
 
-		static ObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);
+		ObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);
 
 		@Bean
 		SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
@@ -509,8 +524,8 @@ public class AuthorizeHttpRequestsConfigurerTests {
 		}
 
 		@Bean
-		static ObjectPostProcessor<Object> objectPostProcessor() {
-			return objectPostProcessor;
+		ObjectPostProcessor<Object> objectPostProcessor() {
+			return this.objectPostProcessor;
 		}
 
 	}
@@ -698,6 +713,18 @@ public class AuthorizeHttpRequestsConfigurerTests {
 
 	}
 
+	@Configuration
+	static class AuthorizationEventPublisherConfig {
+
+		private final AuthorizationEventPublisher publisher = mock(AuthorizationEventPublisher.class);
+
+		@Bean
+		AuthorizationEventPublisher authorizationEventPublisher() {
+			return this.publisher;
+		}
+
+	}
+
 	@RestController
 	static class BasicController {
 

+ 25 - 5
core/src/main/java/org/springframework/security/authorization/AuthorizationEventPublisher.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -16,14 +16,34 @@
 
 package org.springframework.security.authorization;
 
+import java.util.function.Supplier;
+
+import org.springframework.security.authorization.event.AuthorizationDeniedEvent;
+import org.springframework.security.authorization.event.AuthorizationGrantedEvent;
+import org.springframework.security.core.Authentication;
+
 /**
+ * A contract for publishing authorization events
+ *
  * @author Parikshit Dutta
- * @since 5.5
+ * @author Josh Cummings
+ * @since 5.7
+ * @see AuthorizationManager
  */
 public interface AuthorizationEventPublisher {
 
-	void publishAuthorizationSuccess(AuthorizationDecision authorizationDecision);
-
-	void publishAuthorizationFailure(AuthorizationDecision authorizationDecision);
+	/**
+	 * 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 decision the decision about whether the user may access the secured object
+	 * @param <T> the secured object's type
+	 */
+	<T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
+			AuthorizationDecision decision);
 
 }

+ 0 - 61
core/src/main/java/org/springframework/security/authorization/DefaultAuthorizationEventPublisher.java

@@ -1,61 +0,0 @@
-/*
- * Copyright 2002-2021 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.security.authorization;
-
-import org.springframework.context.ApplicationEventPublisher;
-import org.springframework.context.ApplicationEventPublisherAware;
-import org.springframework.security.authorization.event.AuthorizationFailureEvent;
-import org.springframework.security.authorization.event.AuthorizationSuccessEvent;
-
-/**
- * Default implementation of {@link AuthorizationEventPublisher}
- *
- * @author Parikshit Dutta
- * @since 5.5
- */
-public class DefaultAuthorizationEventPublisher implements AuthorizationEventPublisher, ApplicationEventPublisherAware {
-
-	private ApplicationEventPublisher applicationEventPublisher;
-
-	public DefaultAuthorizationEventPublisher() {
-		this(null);
-	}
-
-	public DefaultAuthorizationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
-		this.applicationEventPublisher = applicationEventPublisher;
-	}
-
-	@Override
-	public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
-		this.applicationEventPublisher = applicationEventPublisher;
-	}
-
-	@Override
-	public void publishAuthorizationSuccess(AuthorizationDecision authorizationDecision) {
-		if (this.applicationEventPublisher != null) {
-			this.applicationEventPublisher.publishEvent(new AuthorizationSuccessEvent(authorizationDecision));
-		}
-	}
-
-	@Override
-	public void publishAuthorizationFailure(AuthorizationDecision authorizationDecision) {
-		if (this.applicationEventPublisher != null) {
-			this.applicationEventPublisher.publishEvent(new AuthorizationFailureEvent(authorizationDecision));
-		}
-	}
-
-}

+ 65 - 0
core/src/main/java/org/springframework/security/authorization/SpringAuthorizationEventPublisher.java

@@ -0,0 +1,65 @@
+/*
+ * Copyright 2002-2022 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;
+
+import java.util.function.Supplier;
+
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.security.authorization.event.AuthorizationDeniedEvent;
+import org.springframework.security.authorization.event.AuthorizationGrantedEvent;
+import org.springframework.security.core.Authentication;
+import org.springframework.util.Assert;
+
+/**
+ * An implementation of {@link AuthorizationEventPublisher} that uses Spring's event
+ * publishing support.
+ *
+ * Because {@link AuthorizationGrantedEvent}s typically require additional business logic
+ * to decide whether to publish, this implementation only publishes
+ * {@link AuthorizationDeniedEvent}s.
+ *
+ * @author Parikshit Dutta
+ * @author Josh Cummings
+ * @since 5.7
+ */
+public final class SpringAuthorizationEventPublisher implements AuthorizationEventPublisher {
+
+	private final ApplicationEventPublisher eventPublisher;
+
+	/**
+	 * Construct this publisher using Spring's {@link ApplicationEventPublisher}
+	 * @param eventPublisher
+	 */
+	public SpringAuthorizationEventPublisher(ApplicationEventPublisher eventPublisher) {
+		Assert.notNull(eventPublisher, "eventPublisher cannot be null");
+		this.eventPublisher = eventPublisher;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
+			AuthorizationDecision decision) {
+		if (decision == null || decision.isGranted()) {
+			return;
+		}
+		AuthorizationDeniedEvent<T> failure = new AuthorizationDeniedEvent<>(authentication, object, decision);
+		this.eventPublisher.publishEvent(failure);
+	}
+
+}

+ 23 - 5
core/src/main/java/org/springframework/security/authorization/event/AuthorizationFailureEvent.java → core/src/main/java/org/springframework/security/authorization/event/AuthorizationDeniedEvent.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -16,19 +16,37 @@
 
 package org.springframework.security.authorization.event;
 
+import java.util.function.Supplier;
+
 import org.springframework.context.ApplicationEvent;
 import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.core.Authentication;
 
 /**
  * An {@link ApplicationEvent} which indicates failed authorization.
  *
  * @author Parikshit Dutta
- * @since 5.5
+ * @author Josh Cummings
+ * @since 5.7
  */
-public class AuthorizationFailureEvent extends ApplicationEvent {
+public class AuthorizationDeniedEvent<T> extends ApplicationEvent {
+
+	private final Supplier<Authentication> authentication;
+
+	private final AuthorizationDecision decision;
+
+	public AuthorizationDeniedEvent(Supplier<Authentication> authentication, T object, AuthorizationDecision decision) {
+		super(object);
+		this.authentication = authentication;
+		this.decision = decision;
+	}
+
+	public Supplier<Authentication> getAuthentication() {
+		return this.authentication;
+	}
 
-	public AuthorizationFailureEvent(AuthorizationDecision authorizationDecision) {
-		super(authorizationDecision);
+	public AuthorizationDecision getAuthorizationDecision() {
+		return this.decision;
 	}
 
 }

+ 26 - 5
core/src/main/java/org/springframework/security/authorization/event/AuthorizationSuccessEvent.java → core/src/main/java/org/springframework/security/authorization/event/AuthorizationGrantedEvent.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -16,19 +16,40 @@
 
 package org.springframework.security.authorization.event;
 
+import java.util.function.Supplier;
+
 import org.springframework.context.ApplicationEvent;
 import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.core.Authentication;
+import org.springframework.util.Assert;
 
 /**
  * An {@link ApplicationEvent} which indicates successful authorization.
  *
  * @author Parikshit Dutta
- * @since 5.5
+ * @author Josh Cummings
+ * @since 5.7
  */
-public class AuthorizationSuccessEvent extends ApplicationEvent {
+public class AuthorizationGrantedEvent<T> extends ApplicationEvent {
+
+	private final Supplier<Authentication> authentication;
+
+	private final AuthorizationDecision decision;
+
+	public AuthorizationGrantedEvent(Supplier<Authentication> authentication, T object,
+			AuthorizationDecision decision) {
+		super(object);
+		Assert.notNull(authentication, "authentication supplier cannot be null");
+		this.authentication = authentication;
+		this.decision = decision;
+	}
+
+	public Supplier<Authentication> getAuthentication() {
+		return this.authentication;
+	}
 
-	public AuthorizationSuccessEvent(AuthorizationDecision authorizationDecision) {
-		super(authorizationDecision);
+	public AuthorizationDecision getAuthorizationDecision() {
+		return this.decision;
 	}
 
 }

+ 23 - 3
core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptor.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 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.access.AccessDeniedException;
 import org.springframework.security.access.prepost.PostAuthorize;
 import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
 import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.AuthorizationEventPublisher;
 import org.springframework.security.authorization.AuthorizationManager;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
@@ -66,6 +67,8 @@ public final class AuthorizationManagerAfterMethodInterceptor
 
 	private int order;
 
+	private AuthorizationEventPublisher eventPublisher = AuthorizationManagerAfterMethodInterceptor::noPublish;
+
 	/**
 	 * Creates an instance.
 	 * @param pointcut the {@link Pointcut} to use
@@ -122,6 +125,17 @@ public final class AuthorizationManagerAfterMethodInterceptor
 		this.order = order;
 	}
 
+	/**
+	 * Use this {@link AuthorizationEventPublisher} to publish the
+	 * {@link AuthorizationManager} result.
+	 * @param eventPublisher
+	 * @since 5.7
+	 */
+	public void setAuthorizationEventPublisher(AuthorizationEventPublisher eventPublisher) {
+		Assert.notNull(eventPublisher, "eventPublisher cannot be null");
+		this.eventPublisher = eventPublisher;
+	}
+
 	/**
 	 * {@inheritDoc}
 	 */
@@ -142,8 +156,9 @@ public final class AuthorizationManagerAfterMethodInterceptor
 
 	private void attemptAuthorization(MethodInvocation mi, Object result) {
 		this.logger.debug(LogMessage.of(() -> "Authorizing method invocation " + mi));
-		AuthorizationDecision decision = this.authorizationManager.check(AUTHENTICATION_SUPPLIER,
-				new MethodInvocationResult(mi, result));
+		MethodInvocationResult object = new MethodInvocationResult(mi, result);
+		AuthorizationDecision decision = this.authorizationManager.check(AUTHENTICATION_SUPPLIER, object);
+		this.eventPublisher.publishAuthorizationEvent(AUTHENTICATION_SUPPLIER, object, decision);
 		if (decision != null && !decision.isGranted()) {
 			this.logger.debug(LogMessage.of(() -> "Failed to authorize " + mi + " with authorization manager "
 					+ this.authorizationManager + " and decision " + decision));
@@ -152,4 +167,9 @@ public final class AuthorizationManagerAfterMethodInterceptor
 		this.logger.debug(LogMessage.of(() -> "Authorized method invocation " + mi));
 	}
 
+	private static <T> void noPublish(Supplier<Authentication> authentication, T object,
+			AuthorizationDecision decision) {
+
+	}
+
 }

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

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -37,6 +37,7 @@ import org.springframework.security.access.annotation.Secured;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
 import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.AuthorizationEventPublisher;
 import org.springframework.security.authorization.AuthorizationManager;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
@@ -70,6 +71,8 @@ public final class AuthorizationManagerBeforeMethodInterceptor
 
 	private int order = AuthorizationInterceptorsOrder.FIRST.getOrder();
 
+	private AuthorizationEventPublisher eventPublisher = AuthorizationManagerBeforeMethodInterceptor::noPublish;
+
 	/**
 	 * Creates an instance.
 	 * @param pointcut the {@link Pointcut} to use
@@ -167,6 +170,17 @@ public final class AuthorizationManagerBeforeMethodInterceptor
 		this.order = order;
 	}
 
+	/**
+	 * Use this {@link AuthorizationEventPublisher} to publish the
+	 * {@link AuthorizationManager} result.
+	 * @param eventPublisher
+	 * @since 5.7
+	 */
+	public void setAuthorizationEventPublisher(AuthorizationEventPublisher eventPublisher) {
+		Assert.notNull(eventPublisher, "eventPublisher cannot be null");
+		this.eventPublisher = eventPublisher;
+	}
+
 	/**
 	 * {@inheritDoc}
 	 */
@@ -188,6 +202,7 @@ public final class AuthorizationManagerBeforeMethodInterceptor
 	private void attemptAuthorization(MethodInvocation mi) {
 		this.logger.debug(LogMessage.of(() -> "Authorizing method invocation " + mi));
 		AuthorizationDecision decision = this.authorizationManager.check(AUTHENTICATION_SUPPLIER, mi);
+		this.eventPublisher.publishAuthorizationEvent(AUTHENTICATION_SUPPLIER, mi, decision);
 		if (decision != null && !decision.isGranted()) {
 			this.logger.debug(LogMessage.of(() -> "Failed to authorize " + mi + " with authorization manager "
 					+ this.authorizationManager + " and decision " + decision));
@@ -196,4 +211,9 @@ public final class AuthorizationManagerBeforeMethodInterceptor
 		this.logger.debug(LogMessage.of(() -> "Authorized method invocation " + mi));
 	}
 
+	private static <T> void noPublish(Supplier<Authentication> authentication, T object,
+			AuthorizationDecision decision) {
+
+	}
+
 }

+ 0 - 70
core/src/test/java/org/springframework/security/authorization/DefaultAuthorizationEventPublisherTests.java

@@ -1,70 +0,0 @@
-/*
- * Copyright 2002-2021 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.security.authorization;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import org.springframework.context.ApplicationEventPublisher;
-import org.springframework.security.authorization.event.AuthorizationFailureEvent;
-import org.springframework.security.authorization.event.AuthorizationSuccessEvent;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.isA;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-/**
- * Tests for {@link DefaultAuthorizationEventPublisher}
- *
- * @author Parikshit Dutta
- */
-public class DefaultAuthorizationEventPublisherTests {
-
-	ApplicationEventPublisher applicationEventPublisher;
-
-	DefaultAuthorizationEventPublisher authorizationEventPublisher;
-
-	@BeforeEach
-	public void init() {
-		this.applicationEventPublisher = mock(ApplicationEventPublisher.class);
-		this.authorizationEventPublisher = new DefaultAuthorizationEventPublisher();
-		this.authorizationEventPublisher.setApplicationEventPublisher(this.applicationEventPublisher);
-	}
-
-	@Test
-	public void testAuthenticationSuccessIsPublished() {
-		this.authorizationEventPublisher.publishAuthorizationSuccess(mock(AuthorizationDecision.class));
-		verify(this.applicationEventPublisher).publishEvent(isA(AuthorizationSuccessEvent.class));
-	}
-
-	@Test
-	public void testAuthenticationFailureIsPublished() {
-		this.authorizationEventPublisher.publishAuthorizationFailure(mock(AuthorizationDecision.class));
-		verify(this.applicationEventPublisher).publishEvent(isA(AuthorizationFailureEvent.class));
-	}
-
-	@Test
-	public void testNullPublisherNotInvoked() {
-		this.authorizationEventPublisher.setApplicationEventPublisher(null);
-		this.authorizationEventPublisher.publishAuthorizationSuccess(mock(AuthorizationDecision.class));
-		this.authorizationEventPublisher.publishAuthorizationFailure(mock(AuthorizationDecision.class));
-		verify(this.applicationEventPublisher, never()).publishEvent(any());
-	}
-
-}

+ 67 - 0
core/src/test/java/org/springframework/security/authorization/SpringAuthorizationEventPublisherTests.java

@@ -0,0 +1,67 @@
+/*
+ * Copyright 2002-2022 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;
+
+import java.util.function.Supplier;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.security.authentication.TestAuthentication;
+import org.springframework.security.authorization.event.AuthorizationDeniedEvent;
+import org.springframework.security.core.Authentication;
+
+import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+
+/**
+ * Tests for {@link SpringAuthorizationEventPublisher}
+ *
+ * @author Parikshit Dutta
+ */
+public class SpringAuthorizationEventPublisherTests {
+
+	Supplier<Authentication> authentication = () -> TestAuthentication.authenticatedUser();
+
+	ApplicationEventPublisher applicationEventPublisher;
+
+	SpringAuthorizationEventPublisher authorizationEventPublisher;
+
+	@BeforeEach
+	public void init() {
+		this.applicationEventPublisher = mock(ApplicationEventPublisher.class);
+		this.authorizationEventPublisher = new SpringAuthorizationEventPublisher(this.applicationEventPublisher);
+	}
+
+	@Test
+	public void testAuthenticationSuccessIsNotPublished() {
+		AuthorizationDecision decision = new AuthorizationDecision(true);
+		this.authorizationEventPublisher.publishAuthorizationEvent(this.authentication, mock(Object.class), decision);
+		verifyNoInteractions(this.applicationEventPublisher);
+	}
+
+	@Test
+	public void testAuthenticationFailureIsPublished() {
+		AuthorizationDecision decision = new AuthorizationDecision(false);
+		this.authorizationEventPublisher.publishAuthorizationEvent(this.authentication, mock(Object.class), decision);
+		verify(this.applicationEventPublisher).publishEvent(isA(AuthorizationDeniedEvent.class));
+	}
+
+}

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

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -16,11 +16,20 @@
 
 package org.springframework.security.authorization.method;
 
+import java.util.function.Supplier;
+
 import org.aopalliance.intercept.MethodInvocation;
 import org.junit.jupiter.api.Test;
 
 import org.springframework.aop.Pointcut;
+import org.springframework.security.authentication.TestingAuthenticationToken;
+import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
+import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.AuthorizationEventPublisher;
 import org.springframework.security.authorization.AuthorizationManager;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextImpl;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
@@ -66,4 +75,32 @@ public class AuthorizationManagerAfterMethodInterceptorTests {
 				any(MethodInvocationResult.class));
 	}
 
+	@Test
+	public void configureWhenAuthorizationEventPublisherIsNullThenIllegalArgument() {
+		AuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor(
+				Pointcut.TRUE, AuthenticatedAuthorizationManager.authenticated());
+		assertThatIllegalArgumentException().isThrownBy(() -> advice.setAuthorizationEventPublisher(null))
+				.withMessage("eventPublisher cannot be null");
+	}
+
+	@Test
+	public void invokeWhenAuthorizationEventPublisherThenUses() throws Throwable {
+		AuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor(
+				Pointcut.TRUE, AuthenticatedAuthorizationManager.authenticated());
+		AuthorizationEventPublisher eventPublisher = mock(AuthorizationEventPublisher.class);
+		advice.setAuthorizationEventPublisher(eventPublisher);
+
+		SecurityContext securityContext = new SecurityContextImpl();
+		securityContext.setAuthentication(new TestingAuthenticationToken("user", "password", "ROLE_USER"));
+		SecurityContextHolder.setContext(securityContext);
+
+		MethodInvocation mockMethodInvocation = mock(MethodInvocation.class);
+		MethodInvocationResult result = new MethodInvocationResult(mockMethodInvocation, new Object());
+		given(mockMethodInvocation.proceed()).willReturn(result.getResult());
+
+		advice.invoke(mockMethodInvocation);
+		verify(eventPublisher).publishAuthorizationEvent(any(Supplier.class), any(MethodInvocationResult.class),
+				any(AuthorizationDecision.class));
+	}
+
 }

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

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -16,13 +16,24 @@
 
 package org.springframework.security.authorization.method;
 
+import java.util.function.Supplier;
+
 import org.aopalliance.intercept.MethodInvocation;
 import org.junit.jupiter.api.Test;
 
 import org.springframework.aop.Pointcut;
+import org.springframework.security.authentication.TestingAuthenticationToken;
+import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
+import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.AuthorizationEventPublisher;
 import org.springframework.security.authorization.AuthorizationManager;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextImpl;
 
 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.mock;
 import static org.mockito.Mockito.verify;
 
@@ -59,4 +70,32 @@ public class AuthorizationManagerBeforeMethodInterceptorTests {
 				mockMethodInvocation);
 	}
 
+	@Test
+	public void configureWhenAuthorizationEventPublisherIsNullThenIllegalArgument() {
+		AuthorizationManagerBeforeMethodInterceptor advice = new AuthorizationManagerBeforeMethodInterceptor(
+				Pointcut.TRUE, AuthenticatedAuthorizationManager.authenticated());
+		assertThatIllegalArgumentException().isThrownBy(() -> advice.setAuthorizationEventPublisher(null))
+				.withMessage("eventPublisher cannot be null");
+	}
+
+	@Test
+	public void invokeWhenAuthorizationEventPublisherThenUses() throws Throwable {
+		AuthorizationManagerBeforeMethodInterceptor advice = new AuthorizationManagerBeforeMethodInterceptor(
+				Pointcut.TRUE, AuthenticatedAuthorizationManager.authenticated());
+		AuthorizationEventPublisher eventPublisher = mock(AuthorizationEventPublisher.class);
+		advice.setAuthorizationEventPublisher(eventPublisher);
+
+		SecurityContext securityContext = new SecurityContextImpl();
+		securityContext.setAuthentication(new TestingAuthenticationToken("user", "password", "ROLE_USER"));
+		SecurityContextHolder.setContext(securityContext);
+
+		MethodInvocation mockMethodInvocation = mock(MethodInvocation.class);
+		MethodInvocationResult result = new MethodInvocationResult(mockMethodInvocation, new Object());
+		given(mockMethodInvocation.proceed()).willReturn(result.getResult());
+
+		advice.invoke(mockMethodInvocation);
+		verify(eventPublisher).publishAuthorizationEvent(any(Supplier.class), any(MethodInvocation.class),
+				any(AuthorizationDecision.class));
+	}
+
 }

+ 31 - 2
web/src/main/java/org/springframework/security/web/access/intercept/AuthorizationFilter.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -17,14 +17,21 @@
 package org.springframework.security.web.access.intercept;
 
 import java.io.IOException;
+import java.util.function.Supplier;
 
 import jakarta.servlet.FilterChain;
 import jakarta.servlet.ServletException;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.security.access.AccessDeniedException;
 import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
+import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.AuthorizationEventPublisher;
 import org.springframework.security.authorization.AuthorizationManager;
+import org.springframework.security.authorization.event.AuthorizationDeniedEvent;
+import org.springframework.security.authorization.event.AuthorizationGrantedEvent;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.util.Assert;
@@ -41,6 +48,8 @@ public class AuthorizationFilter extends OncePerRequestFilter {
 
 	private final AuthorizationManager<HttpServletRequest> authorizationManager;
 
+	private AuthorizationEventPublisher eventPublisher = AuthorizationFilter::noPublish;
+
 	/**
 	 * Creates an instance.
 	 * @param authorizationManager the {@link AuthorizationManager} to use
@@ -54,7 +63,11 @@ public class AuthorizationFilter extends OncePerRequestFilter {
 	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
 			throws ServletException, IOException {
 
-		this.authorizationManager.verify(this::getAuthentication, request);
+		AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, request);
+		this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, request, decision);
+		if (decision != null && !decision.isGranted()) {
+			throw new AccessDeniedException("Access Denied");
+		}
 		filterChain.doFilter(request, response);
 	}
 
@@ -67,6 +80,17 @@ public class AuthorizationFilter extends OncePerRequestFilter {
 		return authentication;
 	}
 
+	/**
+	 * Use this {@link AuthorizationEventPublisher} to publish
+	 * {@link AuthorizationDeniedEvent}s and {@link AuthorizationGrantedEvent}s.
+	 * @param eventPublisher the {@link ApplicationEventPublisher} to use
+	 * @since 5.7
+	 */
+	public void setAuthorizationEventPublisher(AuthorizationEventPublisher eventPublisher) {
+		Assert.notNull(eventPublisher, "eventPublisher cannot be null");
+		this.eventPublisher = eventPublisher;
+	}
+
 	/**
 	 * Gets the {@link AuthorizationManager} used by this filter
 	 * @return the {@link AuthorizationManager}
@@ -75,4 +99,9 @@ public class AuthorizationFilter extends OncePerRequestFilter {
 		return this.authorizationManager;
 	}
 
+	private static <T> void noPublish(Supplier<Authentication> authentication, T object,
+			AuthorizationDecision decision) {
+
+	}
+
 }

+ 1 - 26
web/src/main/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManager.java

@@ -27,7 +27,6 @@ import org.apache.commons.logging.LogFactory;
 
 import org.springframework.core.log.LogMessage;
 import org.springframework.security.authorization.AuthorizationDecision;
-import org.springframework.security.authorization.AuthorizationEventPublisher;
 import org.springframework.security.authorization.AuthorizationManager;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.web.util.matcher.RequestMatcher;
@@ -48,8 +47,6 @@ public final class RequestMatcherDelegatingAuthorizationManager implements Autho
 
 	private final Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> mappings;
 
-	private AuthorizationEventPublisher authorizationEventPublisher;
-
 	private RequestMatcherDelegatingAuthorizationManager(
 			Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> mappings) {
 		Assert.notEmpty(mappings, "mappings cannot be empty");
@@ -80,36 +77,14 @@ public final class RequestMatcherDelegatingAuthorizationManager implements Autho
 				if (this.logger.isTraceEnabled()) {
 					this.logger.trace(LogMessage.format("Checking authorization on %s using %s", request, manager));
 				}
-				AuthorizationDecision authorizationDecision = manager.check(authentication,
+				return manager.check(authentication,
 						new RequestAuthorizationContext(request, matchResult.getVariables()));
-				publishAuthorizationEvent(authorizationDecision);
-				return authorizationDecision;
 			}
 		}
 		this.logger.trace("Abstaining since did not find matching RequestMatcher");
 		return null;
 	}
 
-	private void publishAuthorizationEvent(AuthorizationDecision authorizationDecision) {
-		if (this.authorizationEventPublisher != null) {
-			if (authorizationDecision.isGranted()) {
-				this.authorizationEventPublisher.publishAuthorizationSuccess(authorizationDecision);
-			}
-			else {
-				this.authorizationEventPublisher.publishAuthorizationFailure(authorizationDecision);
-			}
-		}
-	}
-
-	/**
-	 * Set implementation of an {@link AuthorizationEventPublisher}
-	 * @param authorizationEventPublisher
-	 */
-	public void setAuthorizationEventPublisher(AuthorizationEventPublisher authorizationEventPublisher) {
-		Assert.notNull(authorizationEventPublisher, "AuthorizationEventPublisher cannot be null");
-		this.authorizationEventPublisher = authorizationEventPublisher;
-	}
-
 	/**
 	 * Creates a builder for {@link RequestMatcherDelegatingAuthorizationManager}.
 	 * @return the new {@link Builder} instance

+ 36 - 3
web/src/test/java/org/springframework/security/web/access/intercept/AuthorizationFilterTests.java

@@ -30,6 +30,8 @@ import org.springframework.security.access.AccessDeniedException;
 import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
 import org.springframework.security.authentication.TestingAuthenticationToken;
 import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
+import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.AuthorizationEventPublisher;
 import org.springframework.security.authorization.AuthorizationManager;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContext;
@@ -38,8 +40,10 @@ import org.springframework.security.core.context.SecurityContextImpl;
 
 import static org.assertj.core.api.Assertions.assertThat;
 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.ArgumentMatchers.eq;
+import static org.mockito.BDDMockito.given;
 import static org.mockito.BDDMockito.willThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -60,6 +64,8 @@ public class AuthorizationFilterTests {
 	@Test
 	public void filterWhenAuthorizationManagerVerifyPassesThenNextFilter() throws Exception {
 		AuthorizationManager<HttpServletRequest> mockAuthorizationManager = mock(AuthorizationManager.class);
+		given(mockAuthorizationManager.check(any(Supplier.class), any(HttpServletRequest.class)))
+				.willReturn(new AuthorizationDecision(true));
 		AuthorizationFilter filter = new AuthorizationFilter(mockAuthorizationManager);
 		TestingAuthenticationToken authenticationToken = new TestingAuthenticationToken("user", "password");
 
@@ -74,7 +80,7 @@ public class AuthorizationFilterTests {
 		filter.doFilter(mockRequest, mockResponse, mockFilterChain);
 
 		ArgumentCaptor<Supplier<Authentication>> authenticationCaptor = ArgumentCaptor.forClass(Supplier.class);
-		verify(mockAuthorizationManager).verify(authenticationCaptor.capture(), eq(mockRequest));
+		verify(mockAuthorizationManager).check(authenticationCaptor.capture(), eq(mockRequest));
 		Supplier<Authentication> authentication = authenticationCaptor.getValue();
 		assertThat(authentication.get()).isEqualTo(authenticationToken);
 
@@ -95,7 +101,7 @@ public class AuthorizationFilterTests {
 		MockHttpServletResponse mockResponse = new MockHttpServletResponse();
 		FilterChain mockFilterChain = mock(FilterChain.class);
 
-		willThrow(new AccessDeniedException("Access Denied")).given(mockAuthorizationManager).verify(any(),
+		willThrow(new AccessDeniedException("Access Denied")).given(mockAuthorizationManager).check(any(),
 				eq(mockRequest));
 
 		assertThatExceptionOfType(AccessDeniedException.class)
@@ -103,7 +109,7 @@ public class AuthorizationFilterTests {
 				.withMessage("Access Denied");
 
 		ArgumentCaptor<Supplier<Authentication>> authenticationCaptor = ArgumentCaptor.forClass(Supplier.class);
-		verify(mockAuthorizationManager).verify(authenticationCaptor.capture(), eq(mockRequest));
+		verify(mockAuthorizationManager).check(authenticationCaptor.capture(), eq(mockRequest));
 		Supplier<Authentication> authentication = authenticationCaptor.getValue();
 		assertThat(authentication.get()).isEqualTo(authenticationToken);
 
@@ -131,4 +137,31 @@ public class AuthorizationFilterTests {
 		assertThat(authorizationFilter.getAuthorizationManager()).isSameAs(authorizationManager);
 	}
 
+	@Test
+	public void configureWhenAuthorizationEventPublisherIsNullThenIllegalArgument() {
+		AuthorizationManager<HttpServletRequest> authorizationManager = mock(AuthorizationManager.class);
+		AuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager);
+		assertThatIllegalArgumentException().isThrownBy(() -> authorizationFilter.setAuthorizationEventPublisher(null))
+				.withMessage("eventPublisher cannot be null");
+	}
+
+	@Test
+	public void doFilterWhenAuthorizationEventPublisherThenUses() throws Exception {
+		AuthorizationFilter authorizationFilter = new AuthorizationFilter(
+				AuthenticatedAuthorizationManager.authenticated());
+		MockHttpServletRequest mockRequest = new MockHttpServletRequest(null, "/path");
+		MockHttpServletResponse mockResponse = new MockHttpServletResponse();
+		FilterChain mockFilterChain = mock(FilterChain.class);
+
+		SecurityContext securityContext = new SecurityContextImpl();
+		securityContext.setAuthentication(new TestingAuthenticationToken("user", "password", "ROLE_USER"));
+		SecurityContextHolder.setContext(securityContext);
+
+		AuthorizationEventPublisher eventPublisher = mock(AuthorizationEventPublisher.class);
+		authorizationFilter.setAuthorizationEventPublisher(eventPublisher);
+		authorizationFilter.doFilter(mockRequest, mockResponse, mockFilterChain);
+		verify(eventPublisher).publishAuthorizationEvent(any(Supplier.class), any(HttpServletRequest.class),
+				any(AuthorizationDecision.class));
+	}
+
 }

+ 0 - 39
web/src/test/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManagerTests.java

@@ -24,15 +24,12 @@ import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.security.authentication.TestingAuthenticationToken;
 import org.springframework.security.authorization.AuthorityAuthorizationManager;
 import org.springframework.security.authorization.AuthorizationDecision;
-import org.springframework.security.authorization.AuthorizationEventPublisher;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
 import org.springframework.security.web.util.matcher.AnyRequestMatcher;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
 
 /**
  * Tests for {@link RequestMatcherDelegatingAuthorizationManager}.
@@ -126,40 +123,4 @@ public class RequestMatcherDelegatingAuthorizationManagerTests {
 				.withMessage("mappingsConsumer cannot be null");
 	}
 
-	@Test
-	public void testAuthorizationEventPublisherIsNotNull() {
-		RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()
-				.add(new MvcRequestMatcher(null, "/grant"), (a, o) -> new AuthorizationDecision(true)).build();
-		assertThatIllegalArgumentException().isThrownBy(() -> manager.setAuthorizationEventPublisher(null))
-				.withMessage("AuthorizationEventPublisher cannot be null");
-	}
-
-	@Test
-	public void testAuthorizationSuccessEventWhenAuthorizationGranted() {
-		RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()
-				.add(new MvcRequestMatcher(null, "/grant"), (a, o) -> new AuthorizationDecision(true)).build();
-
-		AuthorizationEventPublisher authorizationEventPublisher = mock(AuthorizationEventPublisher.class);
-		manager.setAuthorizationEventPublisher(authorizationEventPublisher);
-
-		Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
-
-		AuthorizationDecision grant = manager.check(authentication, new MockHttpServletRequest(null, "/grant"));
-		verify(authorizationEventPublisher).publishAuthorizationSuccess(grant);
-	}
-
-	@Test
-	public void testAuthorizationFailureEventWhenAuthorizationNotGranted() {
-		RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()
-				.add(new MvcRequestMatcher(null, "/deny"), (a, o) -> new AuthorizationDecision(false)).build();
-
-		AuthorizationEventPublisher authorizationEventPublisher = mock(AuthorizationEventPublisher.class);
-		manager.setAuthorizationEventPublisher(authorizationEventPublisher);
-
-		Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
-
-		AuthorizationDecision grant = manager.check(authentication, new MockHttpServletRequest(null, "/deny"));
-		verify(authorizationEventPublisher).publishAuthorizationFailure(grant);
-	}
-
 }