Deprecate SecurityMetadataSource

- Updated FAQ to replace SecurityMetadataSource recommendation with
 AuthorizationManager

Issue gh-16772
This commit is contained in:
Josh Cummings 2025-03-19 17:34:16 -06:00
parent 8e9634d25c
commit bb438578cb
No known key found for this signature in database
GPG Key ID: 869B37A20E876129

View File

@ -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,59 +528,99 @@ 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;
}
public Collection<ConfigAttribute> 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<ConfigAttribute> {
val fi = securedObject as FilterInvocation
val url = fi.requestUrl
val httpMethod = fi.request.method
@Component
class DynamicAuthorizationManager : AuthorizationManager<RequestAuthorizationContext?> {
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<Authentication?>?, context: RequestAuthorizationContext?): AuthorizationDecision {
// look up rules from the database
}
}
override fun getAllConfigAttributes(): Collection<ConfigAttribute>? {
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<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"]
----
@Component
class DynamicAuthorizationManager : AuthorizationManager<MethodInvocation?> {
private val authz: MyExternalAuthorizationService? = null
// ...
override fun check(authentication: Supplier<Authentication?>?, invocation: MethodInvocation?): AuthorizationDecision {
// query the external service
}
}
companion object {
@Bean
fun securedAuthorizationAdvisor(dynamicAuthorizationManager: DynamicAuthorizationManager): Advisor {
return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(dynamicAuthorizationManager)
}
}
----
======
[[appendix-faq-ldap-authorities]]