Skip to content

Commit 35cf52d

Browse files
committed
Add DefaultMethodSecurityExpressionHandler
Closes gh-12356
1 parent 6bf1118 commit 35cf52d

File tree

1 file changed

+105
-0
lines changed

1 file changed

+105
-0
lines changed

docs/modules/ROOT/pages/migration/servlet/authorization.adoc

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,111 @@ should change to:
104104
----
105105
====
106106

107+
=== Use a Custom `@Bean` instead of subclassing `DefaultMethodSecurityExpressionHandler`
108+
109+
As a performance optimization, a new method was introduced to `MethodSecurityExpressionHandler` that takes a `Supplier<Authentication>` instead of an `Authentication`.
110+
111+
This allows Spring Security to defer the lookup of the `Authentication`, and is taken advantage of automatically when you use `@EnableMethodSecurity` instead of `@EnableGlobalMethodSecurity`.
112+
113+
However, let's say that your code extends `DefaultMethodSecurityExpressionHandler` and overrides `createSecurityExpressionRoot(Authentication, MethodInvocation)` to return a custom `SecurityExpressionRoot` instance.
114+
This will no longer work because the arrangement that `@EnableMethodSecurity` sets up calls `createEvaluationContext(Supplier<Authentication>, MethodInvocation)` instead.
115+
116+
Happily, such a level of customization is often unnecessary.
117+
Instead, you can create a custom bean with the authorization methods that you need.
118+
119+
For example, let's say you are wanting a custom evaluation of `@PostAuthorize("hasAuthority('ADMIN')")`.
120+
You can create a custom `@Bean` like this one:
121+
122+
====
123+
.Java
124+
[source,java,role="primary"]
125+
----
126+
class MyAuthorizer {
127+
boolean isAdmin(MethodSecurityExpressionOperations root) {
128+
boolean decision = root.hasAuthority("ADMIN");
129+
// custom work ...
130+
return decision;
131+
}
132+
}
133+
----
134+
135+
.Kotlin
136+
[source,kotlin,role="secondary"]
137+
----
138+
class MyAuthorizer {
139+
fun isAdmin(val root: MethodSecurityExpressionOperations): boolean {
140+
val decision = root.hasAuthority("ADMIN");
141+
// custom work ...
142+
return decision;
143+
}
144+
}
145+
----
146+
====
147+
148+
and then refer to it in the annotation like so:
149+
150+
====
151+
.Java
152+
[source,java,role="primary"]
153+
----
154+
@PreAuthorize("@authz.isAdmin(#root)")
155+
----
156+
157+
.Kotlin
158+
[source,kotlin,role="secondary"]
159+
----
160+
@PreAuthorize("@authz.isAdmin(#root)")
161+
----
162+
====
163+
164+
==== I'd still prefer to subclass `DefaultMethodSecurityExpressionHandler`
165+
166+
If you must continue subclassing `DefaultMethodSecurityExpressionHandler`, you can still do so.
167+
Instead, override the `createEvaluationContext(Supplier<Authentication>, MethodInvocation)` method like so:
168+
169+
====
170+
.Java
171+
[source,java,role="primary"]
172+
----
173+
@Component
174+
class MyExpressionHandler extends DefaultMethodSecurityExpressionHandler {
175+
@Override
176+
public EvaluationContext createEvaluationContext(
177+
Supplier<Authentication> authentication, MethodInvocation mi) {
178+
StandardEvaluationContext context = (StandardEvaluationContext) super.createEvaluationContext(authentication, mi);
179+
MySecurityExpressionRoot root = new MySecurityExpressionRoot(authentication, invocation);
180+
root.setPermissionEvaluator(getPermissionEvaluator());
181+
root.setTrustResolver(new AuthenticationTrustResolverImpl());
182+
root.setRoleHierarchy(getRoleHierarchy());
183+
context.setRootObject(root);
184+
return context;
185+
}
186+
}
187+
----
188+
189+
.Kotlin
190+
[source,kotlin,role="secondary"]
191+
----
192+
@Component
193+
class MyExpressionHandler: DefaultMethodSecurityExpressionHandler {
194+
override fun createEvaluationContext(val authentication: Supplier<Authentication>,
195+
val mi: MethodInvocation): EvaluationContext {
196+
val context = super.createEvaluationContext(authentication, mi) as StandardEvaluationContext;
197+
val root = new MySecurityExpressionRoot(authentication, invocation);
198+
root.setPermissionEvaluator(getPermissionEvaluator());
199+
root.setTrustResolver(new AuthenticationTrustResolverImpl());
200+
root.setRoleHierarchy(getRoleHierarchy());
201+
context.setRootObject(root);
202+
return context;
203+
}
204+
}
205+
----
206+
====
207+
208+
==== Opt-out Steps
209+
210+
If you need to opt-out of these changes, you can use `@EnableGlobalMethodSecurity` instead of `@EnableMethodSecurity`
211+
107212
[[servlet-replace-permissionevaluator-bean-with-methodsecurityexpression-handler]]
108213
=== Publish a `MethodSecurityExpressionHandler` instead of a `PermissionEvaluator`
109214

0 commit comments

Comments
 (0)