Browse Source

Deprecate SecurityMetadataSource

- Updated FAQ to replace SecurityMetadataSource recommendation with
 AuthorizationManager

Issue gh-16772
Josh Cummings 5 months ago
parent
commit
bb438578cb
1 changed files with 75 additions and 39 deletions
  1. 75 39
      docs/modules/ROOT/pages/servlet/appendix/faq.adoc

+ 75 - 39
docs/modules/ROOT/pages/servlet/appendix/faq.adoc

@@ -519,12 +519,8 @@ A security-conscious organization should be aware that the benefits of their dil
 If you have taken this into account (perhaps by using multiple layers of security within your application), Spring Security lets you fully customize the source of security metadata.
 You can make it fully dynamic if you choose.
 
-Both method and web security are protected by subclasses of `AbstractSecurityInterceptor`, which is configured with a `SecurityMetadataSource` from which it obtains the metadata for a particular method or filter invocation.
-For web security, the interceptor class is `FilterSecurityInterceptor`, and it uses the `FilterInvocationSecurityMetadataSource` marker interface. The "`secured object`" type it operates on is a `FilterInvocation`. The default implementation (which is used both in the namespace `<http>` and when configuring the interceptor explicitly) stores the list of URL patterns and their corresponding list of "`configuration attributes`" (instances of `ConfigAttribute`) in an in-memory map.
-
-To load the data from an alternative source, you must use an explicitly declared security filter chain (typically Spring Security's `FilterChainProxy`) to customize the `FilterSecurityInterceptor` bean.
-You cannot use the namespace.
-You would then implement `FilterInvocationSecurityMetadataSource` to load the data as you please for a particular `FilterInvocation`. The `FilterInvocation` object contains the `HttpServletRequest`, so you can obtain the URL or any other relevant information on which to base your decision, based on what the list of returned attributes contains. A basic outline would look something like the following example:
+Both method and web security are protected by implementations of `AuthorizationManager`.
+For web security, you can supply your own implementation of `AuthorizationManager<RequestAuthorizationContext>` and supply it to the filter chain DSL like so:
 
 [tabs]
 ======
@@ -532,60 +528,100 @@ Java::
 +
 [source,java,role="primary"]
 ----
+@Component
+public class DynamicAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {
+	private final MyExternalAuthorizationService authz;
 
-	public class MyFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
+	// ...
 
-		public List<ConfigAttribute> getAttributes(Object object) {
-			FilterInvocation fi = (FilterInvocation) object;
-				String url = fi.getRequestUrl();
-				String httpMethod = fi.getRequest().getMethod();
-				List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();
+    @Override
+    public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
+		// query the external service
+    }
+}
 
-				// Lookup your database (or other source) using this information and populate the
-				// list of attributes
+// ...
 
-				return attributes;
-		}
+http
+    .authorizeHttpRequests((authorize) -> authorize.anyRequest().access(dynamicAuthorizationManager))
+----
 
-		public Collection<ConfigAttribute> getAllConfigAttributes() {
-			return null;
-		}
+Kotlin::
++
+[source,kotlin,role="secondary"]
+----
+@Component
+class DynamicAuthorizationManager : AuthorizationManager<RequestAuthorizationContext?> {
+    private val rules: MyAuthorizationRulesRepository? = null
 
-		public boolean supports(Class<?> clazz) {
-			return FilterInvocation.class.isAssignableFrom(clazz);
-		}
-	}
+    // ...
+
+    override fun check(authentication: Supplier<Authentication?>?, context: RequestAuthorizationContext?): AuthorizationDecision {
+		// look up rules from the database
+    }
+}
+
+// ...
 
+http {
+    authorizeHttpRequests {
+        authorize(anyRequest, dynamicAuthorizationManager)
+    }
+}
+----
+======
+
+For method security, you can supply your own implementation of `AuthorizationManager<MethodInvocation>` and supply it to Spring AOP like so:
+
+[tabs]
+======
+Java::
++
+[source,java,role="primary"]
+----
+@Component
+public class DynamicAuthorizationManager implements AuthorizationManager<MethodInvocation> {
+	private final MyExternalAuthorizationService authz;
+
+	// ...
+
+    @Override
+    public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation invocation) {
+		// query the external service
+    }
+}
+
+// ...
+
+@Bean
+static Advisor securedAuthorizationAdvisor(DynamicAuthorizationManager dynamicAuthorizationManager) {
+    return AuthorizationManagerBeforeMethodInterceptor.secured(dynamicAuthorizationManager)
+}
 ----
 
 Kotlin::
 +
 [source,kotlin,role="secondary"]
 ----
-class MyFilterSecurityMetadataSource : FilterInvocationSecurityMetadataSource {
-    override fun getAttributes(securedObject: Any): List<ConfigAttribute> {
-        val fi = securedObject as FilterInvocation
-        val url = fi.requestUrl
-        val httpMethod = fi.request.method
-
-        // Lookup your database (or other source) using this information and populate the
-        // list of attributes
-        return ArrayList()
-    }
+@Component
+class DynamicAuthorizationManager : AuthorizationManager<MethodInvocation?> {
+    private val authz: MyExternalAuthorizationService? = null
 
-    override fun getAllConfigAttributes(): Collection<ConfigAttribute>? {
-        return null
+     // ...
+    override fun check(authentication: Supplier<Authentication?>?, invocation: MethodInvocation?): AuthorizationDecision {
+		// query the external service
     }
+}
 
-    override fun supports(clazz: Class<*>): Boolean {
-        return FilterInvocation::class.java.isAssignableFrom(clazz)
+companion object {
+    @Bean
+    fun securedAuthorizationAdvisor(dynamicAuthorizationManager: DynamicAuthorizationManager): Advisor {
+        return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(dynamicAuthorizationManager)
     }
 }
 ----
 ======
 
-For more information, look at the code for `DefaultFilterInvocationSecurityMetadataSource`.
-
 
 [[appendix-faq-ldap-authorities]]
 === How do I authenticate against LDAP but load user roles from a database?