Browse Source

Add DefaultMethodSecurityExpressionHandler

Closes gh-12356
Josh Cummings 2 years ago
parent
commit
35cf52d3bd
1 changed files with 105 additions and 0 deletions
  1. 105 0
      docs/modules/ROOT/pages/migration/servlet/authorization.adoc

+ 105 - 0
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<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]]
 === Publish a `MethodSecurityExpressionHandler` instead of a `PermissionEvaluator`