2 changed files with 161 additions and 0 deletions
@ -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