Add DefaultMethodSecurityExpressionHandler
Closes gh-12356
This commit is contained in:
parent
6bf11181ef
commit
35cf52d3bd
|
@ -104,6 +104,111 @@ should change to:
|
||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
|
=== Use a Custom `@Bean` instead of subclassing `DefaultMethodSecurityExpressionHandler`
|
||||||
|
|
||||||
|
As a performance optimization, a new method was introduced to `MethodSecurityExpressionHandler` that takes a `Supplier<Authentication>` instead of an `Authentication`.
|
||||||
|
|
||||||
|
This allows Spring Security to defer the lookup of the `Authentication`, and is taken advantage of automatically when you use `@EnableMethodSecurity` instead of `@EnableGlobalMethodSecurity`.
|
||||||
|
|
||||||
|
However, let's say that your code extends `DefaultMethodSecurityExpressionHandler` and overrides `createSecurityExpressionRoot(Authentication, MethodInvocation)` to return a custom `SecurityExpressionRoot` instance.
|
||||||
|
This will no longer work because the arrangement that `@EnableMethodSecurity` sets up calls `createEvaluationContext(Supplier<Authentication>, MethodInvocation)` instead.
|
||||||
|
|
||||||
|
Happily, such a level of customization is often unnecessary.
|
||||||
|
Instead, you can create a custom bean with the authorization methods that you need.
|
||||||
|
|
||||||
|
For example, let's say you are wanting a custom evaluation of `@PostAuthorize("hasAuthority('ADMIN')")`.
|
||||||
|
You can create a custom `@Bean` like this one:
|
||||||
|
|
||||||
|
====
|
||||||
|
.Java
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
class MyAuthorizer {
|
||||||
|
boolean isAdmin(MethodSecurityExpressionOperations root) {
|
||||||
|
boolean decision = root.hasAuthority("ADMIN");
|
||||||
|
// custom work ...
|
||||||
|
return decision;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
.Kotlin
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
|
----
|
||||||
|
class MyAuthorizer {
|
||||||
|
fun isAdmin(val root: MethodSecurityExpressionOperations): boolean {
|
||||||
|
val decision = root.hasAuthority("ADMIN");
|
||||||
|
// custom work ...
|
||||||
|
return decision;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
and then refer to it in the annotation like so:
|
||||||
|
|
||||||
|
====
|
||||||
|
.Java
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
@PreAuthorize("@authz.isAdmin(#root)")
|
||||||
|
----
|
||||||
|
|
||||||
|
.Kotlin
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
|
----
|
||||||
|
@PreAuthorize("@authz.isAdmin(#root)")
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
==== I'd still prefer to subclass `DefaultMethodSecurityExpressionHandler`
|
||||||
|
|
||||||
|
If you must continue subclassing `DefaultMethodSecurityExpressionHandler`, you can still do so.
|
||||||
|
Instead, override the `createEvaluationContext(Supplier<Authentication>, MethodInvocation)` method like so:
|
||||||
|
|
||||||
|
====
|
||||||
|
.Java
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
@Component
|
||||||
|
class MyExpressionHandler extends DefaultMethodSecurityExpressionHandler {
|
||||||
|
@Override
|
||||||
|
public EvaluationContext createEvaluationContext(
|
||||||
|
Supplier<Authentication> authentication, MethodInvocation mi) {
|
||||||
|
StandardEvaluationContext context = (StandardEvaluationContext) super.createEvaluationContext(authentication, mi);
|
||||||
|
MySecurityExpressionRoot root = new MySecurityExpressionRoot(authentication, invocation);
|
||||||
|
root.setPermissionEvaluator(getPermissionEvaluator());
|
||||||
|
root.setTrustResolver(new AuthenticationTrustResolverImpl());
|
||||||
|
root.setRoleHierarchy(getRoleHierarchy());
|
||||||
|
context.setRootObject(root);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
.Kotlin
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
|
----
|
||||||
|
@Component
|
||||||
|
class MyExpressionHandler: DefaultMethodSecurityExpressionHandler {
|
||||||
|
override fun createEvaluationContext(val authentication: Supplier<Authentication>,
|
||||||
|
val mi: MethodInvocation): EvaluationContext {
|
||||||
|
val context = super.createEvaluationContext(authentication, mi) as StandardEvaluationContext;
|
||||||
|
val root = new MySecurityExpressionRoot(authentication, invocation);
|
||||||
|
root.setPermissionEvaluator(getPermissionEvaluator());
|
||||||
|
root.setTrustResolver(new AuthenticationTrustResolverImpl());
|
||||||
|
root.setRoleHierarchy(getRoleHierarchy());
|
||||||
|
context.setRootObject(root);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
==== Opt-out Steps
|
||||||
|
|
||||||
|
If you need to opt-out of these changes, you can use `@EnableGlobalMethodSecurity` instead of `@EnableMethodSecurity`
|
||||||
|
|
||||||
[[servlet-replace-permissionevaluator-bean-with-methodsecurityexpression-handler]]
|
[[servlet-replace-permissionevaluator-bean-with-methodsecurityexpression-handler]]
|
||||||
=== Publish a `MethodSecurityExpressionHandler` instead of a `PermissionEvaluator`
|
=== Publish a `MethodSecurityExpressionHandler` instead of a `PermissionEvaluator`
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue