2 changed files with 161 additions and 0 deletions
@ -0,0 +1,160 @@
@@ -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 |
||||
} |
||||
} |
||||
---- |
||||
==== |
||||
Loading…
Reference in new issue