You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/modules/ROOT/pages/reactive/authorization/method.adoc
+209Lines changed: 209 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -118,6 +118,215 @@ We expose `GrantedAuthorityDefaults` using a `static` method to ensure that Spri
118
118
Since the `GrantedAuthorityDefaults` bean is part of internal workings of Spring Security, we should also expose it as an infrastructural bean effectively avoiding some warnings related to bean post-processing (see https://github.com/spring-projects/spring-security/issues/14751[gh-14751]).
119
119
====
120
120
121
+
[[use-programmatic-authorization]]
122
+
== Authorizing Methods Programmatically
123
+
124
+
As you've already seen, there are several ways that you can specify non-trivial authorization rules using xref:servlet/authorization/method-security.adoc#authorization-expressions[Method Security SpEL expressions].
125
+
126
+
There are a number of ways that you can instead allow your logic to be Java-based instead of SpEL-based.
127
+
This gives use access the entire Java language for increased testability and flow control.
128
+
129
+
=== Using a Custom Bean in SpEL
130
+
131
+
The first way to authorize a method programmatically is a two-step process.
132
+
133
+
First, declare a bean that has a method that takes a `MethodSecurityExpressionOperations` instance like the following:
134
+
135
+
[tabs]
136
+
======
137
+
Java::
138
+
+
139
+
[source,java,role="primary"]
140
+
----
141
+
@Component("authz")
142
+
public class AuthorizationLogic {
143
+
public decide(MethodSecurityExpressionOperations operations): Mono<Boolean> {
144
+
// ... authorization logic
145
+
}
146
+
}
147
+
----
148
+
149
+
Kotlin::
150
+
+
151
+
[source,kotlin,role="secondary"]
152
+
----
153
+
@Component("authz")
154
+
open class AuthorizationLogic {
155
+
fun decide(val operations: MethodSecurityExpressionOperations): Mono<Boolean> {
156
+
// ... authorization logic
157
+
}
158
+
}
159
+
----
160
+
======
161
+
162
+
Then, reference that bean in your annotations in the following way:
163
+
164
+
[tabs]
165
+
======
166
+
Java::
167
+
+
168
+
[source,java,role="primary"]
169
+
----
170
+
@Controller
171
+
public class MyController {
172
+
@PreAuthorize("@authz.decide(#root)")
173
+
@GetMapping("/endpoint")
174
+
public Mono<String> endpoint() {
175
+
// ...
176
+
}
177
+
}
178
+
----
179
+
180
+
Kotlin::
181
+
+
182
+
[source,kotlin,role="secondary"]
183
+
----
184
+
@Controller
185
+
open class MyController {
186
+
@PreAuthorize("@authz.decide(#root)")
187
+
@GetMapping("/endpoint")
188
+
fun endpoint(): Mono<String> {
189
+
// ...
190
+
}
191
+
}
192
+
----
193
+
======
194
+
195
+
Spring Security will invoke the given method on that bean for each method invocation.
196
+
197
+
What's nice about this is all your authorization logic is in a separate class that can be independently unit tested and verified for correctness.
198
+
It also has access to the full Java language.
199
+
200
+
[TIP]
201
+
In addition to returning a `Mono<Boolean>`, you can also return `Mono.empty()` to indicate that the code abstains from making a decision.
202
+
203
+
If you want to include more information about the nature of the decision, you can instead return a custom `AuthorizationDecision` like this:
204
+
205
+
[tabs]
206
+
======
207
+
Java::
208
+
+
209
+
[source,java,role="primary"]
210
+
----
211
+
@Component("authz")
212
+
public class AuthorizationLogic {
213
+
public Mono<AuthorizationDecision> decide(MethodSecurityExpressionOperations operations) {
Or throw a custom `AuthorizationDeniedException` instance.
235
+
Note, though, that returning an object is preferred as this doesn't incur the expense of generating a stacktrace.
236
+
237
+
Then, you can access the custom details when you xref:servlet/authorization/method-security.adoc#fallback-values-authorization-denied[customize how the authorization result is handled].
238
+
239
+
[[custom-authorization-managers]]
240
+
=== Using a Custom Authorization Manager
241
+
242
+
The second way to authorize a method programmatically is to create a custom xref:servlet/authorization/architecture.adoc#_the_authorizationmanager[`AuthorizationManager`].
243
+
244
+
First, declare an authorization manager instance, perhaps like this one:
245
+
246
+
[tabs]
247
+
======
248
+
Java::
249
+
+
250
+
[source,java,role="primary"]
251
+
----
252
+
@Component
253
+
public class MyPreAuthorizeAuthorizationManager implements ReactiveAuthorizationManager<MethodInvocation> {
254
+
@Override
255
+
public Mono<AuthorizationDecision> check(Supplier<Authentication> authentication, MethodInvocation invocation) {
256
+
// ... authorization logic
257
+
}
258
+
259
+
}
260
+
----
261
+
262
+
Kotlin::
263
+
+
264
+
[source,kotlin,role="secondary"]
265
+
----
266
+
@Component
267
+
class MyPreAuthorizeAuthorizationManager : ReactiveAuthorizationManager<MethodInvocation> {
268
+
override fun check(authentication: Supplier<Authentication>, invocation: MethodInvocation): Mono<AuthorizationDecision> {
269
+
// ... authorization logic
270
+
}
271
+
272
+
}
273
+
----
274
+
======
275
+
276
+
Then, publish the method interceptor with a pointcut that corresponds to when you want that `ReactiveAuthorizationManager` to run.
277
+
For example, you could replace how `@PreAuthorize` and `@PostAuthorize` work like so:
278
+
279
+
.Only @PreAuthorize and @PostAuthorize Configuration
0 commit comments