mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-07-10 12:23:30 +00:00
Add Publishing Predicate
Closes gh-17503
This commit is contained in:
parent
901b386ca6
commit
c312d18191
@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.security.authorization;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
@ -40,6 +41,8 @@ public final class SpringAuthorizationEventPublisher implements AuthorizationEve
|
||||
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
|
||||
private Predicate<AuthorizationResult> shouldPublishResult = (result) -> !result.isGranted();
|
||||
|
||||
/**
|
||||
* Construct this publisher using Spring's {@link ApplicationEventPublisher}
|
||||
* @param eventPublisher
|
||||
@ -55,11 +58,28 @@ public final class SpringAuthorizationEventPublisher implements AuthorizationEve
|
||||
@Override
|
||||
public <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
|
||||
AuthorizationResult result) {
|
||||
if (result == null || result.isGranted()) {
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
if (!this.shouldPublishResult.test(result)) {
|
||||
return;
|
||||
}
|
||||
AuthorizationDeniedEvent<T> failure = new AuthorizationDeniedEvent<>(authentication, object, result);
|
||||
this.eventPublisher.publishEvent(failure);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this predicate to test whether to publish an event.
|
||||
*
|
||||
* <p>
|
||||
* Since you cannot publish a {@code null} event, checking for null is already
|
||||
* performed before this test is run
|
||||
* @param shouldPublishResult the test to perform on non-{@code null} events
|
||||
* @since 7.0
|
||||
*/
|
||||
public void setShouldPublishResult(Predicate<AuthorizationResult> shouldPublishResult) {
|
||||
Assert.notNull(shouldPublishResult, "shouldPublishResult cannot be null");
|
||||
this.shouldPublishResult = shouldPublishResult;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.security.authorization;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@ -26,10 +27,13 @@ import org.springframework.security.authentication.TestAuthentication;
|
||||
import org.springframework.security.authorization.event.AuthorizationDeniedEvent;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.isA;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringAuthorizationEventPublisher}
|
||||
@ -64,4 +68,16 @@ public class SpringAuthorizationEventPublisherTests {
|
||||
verify(this.applicationEventPublisher).publishEvent(isA(AuthorizationDeniedEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void publishWhenPredicateMatchesThenEvent() {
|
||||
Predicate<AuthorizationResult> test = mock(Predicate.class);
|
||||
given(test.test(any())).willReturn(true, false);
|
||||
this.authorizationEventPublisher.setShouldPublishResult(test);
|
||||
AuthorizationResult result = new AuthorizationDecision(false);
|
||||
this.authorizationEventPublisher.publishAuthorizationEvent(this.authentication, mock(Object.class), result);
|
||||
verify(this.applicationEventPublisher).publishEvent(isA(AuthorizationDeniedEvent.class));
|
||||
this.authorizationEventPublisher.publishAuthorizationEvent(this.authentication, mock(Object.class), result);
|
||||
verifyNoMoreInteractions(this.applicationEventPublisher);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ Because ``AuthorizationGrantedEvent``s have the potential to be quite noisy, the
|
||||
|
||||
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.
|
||||
You can provide your own predicate that filters success events.
|
||||
For example, the following publisher only publishes authorization grants where `ROLE_ADMIN` was required:
|
||||
|
||||
[tabs]
|
||||
@ -83,44 +83,20 @@ 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, AuthorizationResult result) {
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
@Bean
|
||||
AuthorizationEventPublisher authorizationEventPublisher() {
|
||||
SpringAuthorizationEventPublisher eventPublisher = new SpringAuthorizationEventPublisher();
|
||||
eventPublisher.setShouldPublishEvent((result) -> {
|
||||
if (!result.isGranted()) {
|
||||
this.delegate.publishAuthorizationEvent(authentication, object, result);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
if (shouldThisEventBePublished(result)) {
|
||||
AuthorizationGrantedEvent granted = new AuthorizationGrantedEvent(
|
||||
authentication, object, result);
|
||||
this.publisher.publishEvent(granted);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldThisEventBePublished(AuthorizationResult result) {
|
||||
if (result instanceof AuthorityAuthorizationDecision authorityAuthorizationDecision) {
|
||||
Collection<GrantedAuthority> authorities = authorityAuthorizationDecision.getAuthorities();
|
||||
for (GrantedAuthority authority : authorities) {
|
||||
if ("ROLE_ADMIN".equals(authority.getAuthority())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (result instanceof AuthorityAuthorizationDecision decision) {
|
||||
Collection<GrantedAuthority> authorities = decision.getAuthorities();
|
||||
return AuthorityUtils.authorityListToSet(authorities).contains("ROLE_ADMIN");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return eventPublisher;
|
||||
}
|
||||
----
|
||||
|
||||
@ -128,41 +104,20 @@ 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,
|
||||
result: AuthorizationResult?
|
||||
) {
|
||||
if (result == null) {
|
||||
return
|
||||
@Bean
|
||||
fun authorizationEventPublisher(): AuthorizationEventPublisher {
|
||||
val eventPublisher = SpringAuthorizationEventPublisher()
|
||||
eventPublisher.setShouldPublishEvent { (result) ->
|
||||
if (!result.isGranted()) {
|
||||
return true
|
||||
}
|
||||
if (!result.isGranted) {
|
||||
this.delegate.publishAuthorizationEvent(authentication, `object`, result)
|
||||
return
|
||||
}
|
||||
if (shouldThisEventBePublished(result)) {
|
||||
val granted = AuthorizationGrantedEvent(authentication, `object`, result)
|
||||
this.publisher.publishEvent(granted)
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldThisEventBePublished(result: AuthorizationResult): Boolean {
|
||||
if (decision !is AuthorityAuthorizationDecision) {
|
||||
return false
|
||||
}
|
||||
val authorities = decision.authorities
|
||||
for (authority in authorities) {
|
||||
if ("ROLE_ADMIN" == authority.authority) {
|
||||
return true
|
||||
}
|
||||
if (decision is AuthorityAuthorizationDecision) {
|
||||
val authorities = decision.getAuthorities()
|
||||
return AuthorityUtils.authorityListToSet(authorities).contains("ROLE_ADMIN")
|
||||
}
|
||||
return false
|
||||
}
|
||||
return eventPublisher
|
||||
}
|
||||
----
|
||||
======
|
||||
|
Loading…
x
Reference in New Issue
Block a user