Procházet zdrojové kódy

Document Authorization Events

Issue gh-9288
Josh Cummings před 3 roky
rodič
revize
ce720ad38e

+ 1 - 0
docs/modules/ROOT/nav.adoc

@@ -54,6 +54,7 @@
 *** xref:servlet/authorization/secure-objects.adoc[Secure Object Implementations]
 *** xref:servlet/authorization/method-security.adoc[Method Security]
 *** xref:servlet/authorization/acls.adoc[Domain Object Security ACLs]
+*** xref:servlet/authorization/events.adoc[Authorization Events]
 ** xref:servlet/oauth2/index.adoc[OAuth2]
 *** xref:servlet/oauth2/login/index.adoc[OAuth2 Log In]
 **** xref:servlet/oauth2/login/core.adoc[Core Configuration]

+ 160 - 0
docs/modules/ROOT/pages/servlet/authorization/events.adoc

@@ -0,0 +1,160 @@
+[[servlet-events]]
+= Authorization Events
+
+For each authorization that is denied, an `AuthorizationDeniedEvent` is fired.
+Also, it's possible to fire and `AuthorizationGrantedEvent` for authorizations that are granted.
+
+To listen for these events, you must first publish an `AuthorizationEventPublisher`.
+
+Spring Security's `SpringAuthorizationEventPublisher` will probably do fine.
+It comes publishes authorization events using Spring's `ApplicationEventPublisher`:
+
+====
+.Java
+[source,java,role="primary"]
+----
+@Bean
+public AuthorizationEventPublisher authorizationEventPublisher
+        (ApplicationEventPublisher applicationEventPublisher) {
+    return new SpringAuthorizationEventPublisher(applicationEventPublisher);
+}
+----
+
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@Bean
+fun authorizationEventPublisher
+        (applicationEventPublisher: ApplicationEventPublisher?): AuthorizationEventPublisher {
+    return SpringAuthorizationEventPublisher(applicationEventPublisher)
+}
+----
+====
+
+Then, you can use Spring's `@EventListener` support:
+
+====
+.Java
+[source,java,role="primary"]
+----
+@Component
+public class AuthenticationEvents {
+
+    @EventListener
+    public void onFailure(AuthorizationDeniedEvent failure) {
+		// ...
+    }
+}
+----
+
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@Component
+class AuthenticationEvents {
+
+    @EventListener
+    fun onFailure(failure: AuthorizationDeniedEvent?) {
+        // ...
+    }
+}
+----
+====
+
+[[authorization-granted-events]]
+== Authorization Granted Events
+
+Because ``AuthorizationGrantedEvent``s have the potential to be quite noisy, they are not published by default.
+
+In fact, publishing these events will likely require some business logic on your part to ensure that your application is not inundated with noisy authorization events.
+
+You can create your own event publisher that filters success events.
+For example, the following publisher only publishes authorization grants where `ROLE_ADMIN` was required:
+
+====
+.Java
+[source,java,role="primary"]
+----
+@Component
+public class MyAuthorizationEventPublisher implements AuthorizationEventPublisher {
+    private final ApplicationEventPublisher publisher;
+    private final AuthorizationEventPublisher delegate;
+
+    public MyAuthorizationEventPublisher(ApplicationEventPublisher publisher) {
+        this.publisher = publisher;
+        this.delegate = new SpringAuthorizationEventPublisher(publisher);
+    }
+
+    @Override
+    public <T> void publishAuthorizationEvent(Supplier<Authentication> authentication,
+            T object, AuthorizationDecision decision) {
+        if (decision == null) {
+            return;
+        }
+        if (!decision.isGranted()) {
+            this.delegate.publishAuthorizationEvent(authentication, object, decision);
+            return;
+        }
+        if (shouldThisEventBePublished(decision)) {
+            AuthorizationGrantedEvent granted = new AuthorizationGrantedEvent(
+                    authentication, object, decision);
+            this.publisher.publishEvent(granted);
+        }
+    }
+
+    private boolean shouldThisEventBePublished(AuthorizationDecision decision) {
+        if (!(decision instanceof AuthorityAuthorizationDecision)) {
+            return false;
+        }
+        Collection<GrantedAuthority> authorities = ((AuthorityAuthorizationDecision) decision).getAuthorities();
+        for (GrantedAuthority authority : authorities) {
+            if ("ROLE_ADMIN".equals(authority.getAuthority())) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
+----
+
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@Component
+class MyAuthorizationEventPublisher(val publisher: ApplicationEventPublisher,
+    val delegate: SpringAuthorizationEventPublisher = SpringAuthorizationEventPublisher(publisher)):
+    AuthorizationEventPublisher {
+
+    override fun <T : Any?> publishAuthorizationEvent(
+        authentication: Supplier<Authentication>?,
+        `object`: T,
+        decision: AuthorizationDecision?
+    ) {
+        if (decision == null) {
+            return
+        }
+        if (!decision.isGranted) {
+            this.delegate.publishAuthorizationEvent(authentication, `object`, decision)
+            return
+        }
+        if (shouldThisEventBePublished(decision)) {
+            val granted = AuthorizationGrantedEvent(authentication, `object`, decision)
+            this.publisher.publishEvent(granted)
+        }
+    }
+
+    private fun shouldThisEventBePublished(decision: AuthorizationDecision): Boolean {
+        if (decision !is AuthorityAuthorizationDecision) {
+            return false
+        }
+        val authorities = decision.authorities
+        for (authority in authorities) {
+            if ("ROLE_ADMIN" == authority.authority) {
+                return true
+            }
+        }
+        return false
+    }
+}
+----
+====