From bb438578cb9539260f6a0467e9dda2d6f717bc27 Mon Sep 17 00:00:00 2001 From: Josh Cummings <3627351+jzheaux@users.noreply.github.com> Date: Wed, 19 Mar 2025 17:34:16 -0600 Subject: [PATCH] Deprecate SecurityMetadataSource - Updated FAQ to replace SecurityMetadataSource recommendation with AuthorizationManager Issue gh-16772 --- .../ROOT/pages/servlet/appendix/faq.adoc | 116 ++++++++++++------ 1 file changed, 76 insertions(+), 40 deletions(-) diff --git a/docs/modules/ROOT/pages/servlet/appendix/faq.adoc b/docs/modules/ROOT/pages/servlet/appendix/faq.adoc index e63478125f..d987b67642 100644 --- a/docs/modules/ROOT/pages/servlet/appendix/faq.adoc +++ b/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 `` 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` and supply it to the filter chain DSL like so: [tabs] ====== @@ -532,59 +528,99 @@ Java:: + [source,java,role="primary"] ---- +@Component +public class DynamicAuthorizationManager implements AuthorizationManager { + private final MyExternalAuthorizationService authz; - public class MyFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource { + // ... - public List getAttributes(Object object) { - FilterInvocation fi = (FilterInvocation) object; - String url = fi.getRequestUrl(); - String httpMethod = fi.getRequest().getMethod(); - List attributes = new ArrayList(); + @Override + public AuthorizationDecision check(Supplier authentication, RequestAuthorizationContext context) { + // query the external service + } +} - // Lookup your database (or other source) using this information and populate the - // list of attributes - - return attributes; - } - - public Collection getAllConfigAttributes() { - return null; - } - - public boolean supports(Class clazz) { - return FilterInvocation.class.isAssignableFrom(clazz); - } - } +// ... +http + .authorizeHttpRequests((authorize) -> authorize.anyRequest().access(dynamicAuthorizationManager)) ---- Kotlin:: + [source,kotlin,role="secondary"] ---- -class MyFilterSecurityMetadataSource : FilterInvocationSecurityMetadataSource { - override fun getAttributes(securedObject: Any): List { - val fi = securedObject as FilterInvocation - val url = fi.requestUrl - val httpMethod = fi.request.method +@Component +class DynamicAuthorizationManager : AuthorizationManager { + private val rules: MyAuthorizationRulesRepository? = null - // Lookup your database (or other source) using this information and populate the - // list of attributes - return ArrayList() + // ... + + override fun check(authentication: Supplier?, context: RequestAuthorizationContext?): AuthorizationDecision { + // look up rules from the database } +} - override fun getAllConfigAttributes(): Collection? { - return null - } +// ... - override fun supports(clazz: Class<*>): Boolean { - return FilterInvocation::class.java.isAssignableFrom(clazz) +http { + authorizeHttpRequests { + authorize(anyRequest, dynamicAuthorizationManager) } } ---- ====== -For more information, look at the code for `DefaultFilterInvocationSecurityMetadataSource`. +For method security, you can supply your own implementation of `AuthorizationManager` and supply it to Spring AOP like so: + +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +@Component +public class DynamicAuthorizationManager implements AuthorizationManager { + private final MyExternalAuthorizationService authz; + + // ... + + @Override + public AuthorizationDecision check(Supplier authentication, MethodInvocation invocation) { + // query the external service + } +} + +// ... + +@Bean +static Advisor securedAuthorizationAdvisor(DynamicAuthorizationManager dynamicAuthorizationManager) { + return AuthorizationManagerBeforeMethodInterceptor.secured(dynamicAuthorizationManager) +} +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +@Component +class DynamicAuthorizationManager : AuthorizationManager { + private val authz: MyExternalAuthorizationService? = null + + // ... + override fun check(authentication: Supplier?, invocation: MethodInvocation?): AuthorizationDecision { + // query the external service + } +} + +companion object { + @Bean + fun securedAuthorizationAdvisor(dynamicAuthorizationManager: DynamicAuthorizationManager): Advisor { + return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(dynamicAuthorizationManager) + } +} +---- +====== [[appendix-faq-ldap-authorities]]