Skip to content

Commit ce720ad

Browse files
committed
Document Authorization Events
Issue gh-9288
1 parent bdd5f86 commit ce720ad

File tree

2 files changed

+161
-0
lines changed

2 files changed

+161
-0
lines changed

docs/modules/ROOT/nav.adoc

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
*** xref:servlet/authorization/secure-objects.adoc[Secure Object Implementations]
5555
*** xref:servlet/authorization/method-security.adoc[Method Security]
5656
*** xref:servlet/authorization/acls.adoc[Domain Object Security ACLs]
57+
*** xref:servlet/authorization/events.adoc[Authorization Events]
5758
** xref:servlet/oauth2/index.adoc[OAuth2]
5859
*** xref:servlet/oauth2/login/index.adoc[OAuth2 Log In]
5960
**** xref:servlet/oauth2/login/core.adoc[Core Configuration]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
[[servlet-events]]
2+
= Authorization Events
3+
4+
For each authorization that is denied, an `AuthorizationDeniedEvent` is fired.
5+
Also, it's possible to fire and `AuthorizationGrantedEvent` for authorizations that are granted.
6+
7+
To listen for these events, you must first publish an `AuthorizationEventPublisher`.
8+
9+
Spring Security's `SpringAuthorizationEventPublisher` will probably do fine.
10+
It comes publishes authorization events using Spring's `ApplicationEventPublisher`:
11+
12+
====
13+
.Java
14+
[source,java,role="primary"]
15+
----
16+
@Bean
17+
public AuthorizationEventPublisher authorizationEventPublisher
18+
(ApplicationEventPublisher applicationEventPublisher) {
19+
return new SpringAuthorizationEventPublisher(applicationEventPublisher);
20+
}
21+
----
22+
23+
.Kotlin
24+
[source,kotlin,role="secondary"]
25+
----
26+
@Bean
27+
fun authorizationEventPublisher
28+
(applicationEventPublisher: ApplicationEventPublisher?): AuthorizationEventPublisher {
29+
return SpringAuthorizationEventPublisher(applicationEventPublisher)
30+
}
31+
----
32+
====
33+
34+
Then, you can use Spring's `@EventListener` support:
35+
36+
====
37+
.Java
38+
[source,java,role="primary"]
39+
----
40+
@Component
41+
public class AuthenticationEvents {
42+
43+
@EventListener
44+
public void onFailure(AuthorizationDeniedEvent failure) {
45+
// ...
46+
}
47+
}
48+
----
49+
50+
.Kotlin
51+
[source,kotlin,role="secondary"]
52+
----
53+
@Component
54+
class AuthenticationEvents {
55+
56+
@EventListener
57+
fun onFailure(failure: AuthorizationDeniedEvent?) {
58+
// ...
59+
}
60+
}
61+
----
62+
====
63+
64+
[[authorization-granted-events]]
65+
== Authorization Granted Events
66+
67+
Because ``AuthorizationGrantedEvent``s have the potential to be quite noisy, they are not published by default.
68+
69+
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.
70+
71+
You can create your own event publisher that filters success events.
72+
For example, the following publisher only publishes authorization grants where `ROLE_ADMIN` was required:
73+
74+
====
75+
.Java
76+
[source,java,role="primary"]
77+
----
78+
@Component
79+
public class MyAuthorizationEventPublisher implements AuthorizationEventPublisher {
80+
private final ApplicationEventPublisher publisher;
81+
private final AuthorizationEventPublisher delegate;
82+
83+
public MyAuthorizationEventPublisher(ApplicationEventPublisher publisher) {
84+
this.publisher = publisher;
85+
this.delegate = new SpringAuthorizationEventPublisher(publisher);
86+
}
87+
88+
@Override
89+
public <T> void publishAuthorizationEvent(Supplier<Authentication> authentication,
90+
T object, AuthorizationDecision decision) {
91+
if (decision == null) {
92+
return;
93+
}
94+
if (!decision.isGranted()) {
95+
this.delegate.publishAuthorizationEvent(authentication, object, decision);
96+
return;
97+
}
98+
if (shouldThisEventBePublished(decision)) {
99+
AuthorizationGrantedEvent granted = new AuthorizationGrantedEvent(
100+
authentication, object, decision);
101+
this.publisher.publishEvent(granted);
102+
}
103+
}
104+
105+
private boolean shouldThisEventBePublished(AuthorizationDecision decision) {
106+
if (!(decision instanceof AuthorityAuthorizationDecision)) {
107+
return false;
108+
}
109+
Collection<GrantedAuthority> authorities = ((AuthorityAuthorizationDecision) decision).getAuthorities();
110+
for (GrantedAuthority authority : authorities) {
111+
if ("ROLE_ADMIN".equals(authority.getAuthority())) {
112+
return true;
113+
}
114+
}
115+
return false;
116+
}
117+
}
118+
----
119+
120+
.Kotlin
121+
[source,kotlin,role="secondary"]
122+
----
123+
@Component
124+
class MyAuthorizationEventPublisher(val publisher: ApplicationEventPublisher,
125+
val delegate: SpringAuthorizationEventPublisher = SpringAuthorizationEventPublisher(publisher)):
126+
AuthorizationEventPublisher {
127+
128+
override fun <T : Any?> publishAuthorizationEvent(
129+
authentication: Supplier<Authentication>?,
130+
`object`: T,
131+
decision: AuthorizationDecision?
132+
) {
133+
if (decision == null) {
134+
return
135+
}
136+
if (!decision.isGranted) {
137+
this.delegate.publishAuthorizationEvent(authentication, `object`, decision)
138+
return
139+
}
140+
if (shouldThisEventBePublished(decision)) {
141+
val granted = AuthorizationGrantedEvent(authentication, `object`, decision)
142+
this.publisher.publishEvent(granted)
143+
}
144+
}
145+
146+
private fun shouldThisEventBePublished(decision: AuthorizationDecision): Boolean {
147+
if (decision !is AuthorityAuthorizationDecision) {
148+
return false
149+
}
150+
val authorities = decision.authorities
151+
for (authority in authorities) {
152+
if ("ROLE_ADMIN" == authority.authority) {
153+
return true
154+
}
155+
}
156+
return false
157+
}
158+
}
159+
----
160+
====

0 commit comments

Comments
 (0)