diff --git a/docs/modules/ROOT/pages/migration/servlet/authorization.adoc b/docs/modules/ROOT/pages/migration/servlet/authorization.adoc index b7a84af622..7e59cd0f77 100644 --- a/docs/modules/ROOT/pages/migration/servlet/authorization.adoc +++ b/docs/modules/ROOT/pages/migration/servlet/authorization.adoc @@ -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` 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, 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, MethodInvocation)` method like so: + +==== +.Java +[source,java,role="primary"] +---- +@Component +class MyExpressionHandler extends DefaultMethodSecurityExpressionHandler { + @Override + public EvaluationContext createEvaluationContext( + Supplier 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, + 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]] === Publish a `MethodSecurityExpressionHandler` instead of a `PermissionEvaluator`