Skip @PostFilter when method returns null#19321
Open
seonwooj0810 wants to merge 1 commit into
Open
Conversation
PostFilterAuthorizationMethodInterceptor#invoke called MethodSecurityExpressionHandler#filter unconditionally with the value returned by the intercepted method. DefaultMethodSecurityExpressionHandler#filter requires the filter target to be a Collection, array, Map, or Stream and throws IllegalArgumentException for any other value, including null. As a result, any @PostFilter-annotated method that returns null under @EnableMethodSecurity threw IllegalArgumentException instead of returning null to the caller. The legacy ExpressionBasedPostInvocationAdvice, used by the deprecated @EnableGlobalMethodSecurity, explicitly skipped filtering when the returned object was null and logged "Return object is null, filtering will be skipped". The new interceptor introduced as part of the AuthorizationManager-based method security pipeline did not carry that guard over, which made the migration from @EnableGlobalMethodSecurity to @EnableMethodSecurity a behavioural breaking change for callers relying on null as a no-op signal (for example, a service that returns null when there is nothing to filter). Skip the filter call when the returned object is null and log the same debug message as the legacy advice so that @PostFilter on null returns preserves the documented behaviour across both APIs. Closes spring-projectsgh-19280 Signed-off-by: seonwoo_jung <laborlawseon@kap.kr>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes gh-19280
Background
When a
@PostFilter-annotated method returnsnull, the legacy@EnableGlobalMethodSecuritypipeline returnednullto the caller. After migrating the same method to@EnableMethodSecurity, it now throwsIllegalArgumentException: Filter target must be a collection, array, map or stream type, but was null. The issue suggests this is either a behavioural bug in the new interceptor or a documentation gap; this PR takes the first option and restores the legacy behaviour.Root cause
PostFilterAuthorizationMethodInterceptor#invokecallsMethodSecurityExpressionHandler#filterunconditionally with whatever the intercepted method returned.DefaultMethodSecurityExpressionHandler#filterrequires the filter target to be aCollection, array,Map, orStreamand otherwise throwsIllegalArgumentException.nulldoes not satisfy any of thoseinstanceofchecks (the array check is explicitly null-guarded), so the handler always throws when the method returnsnull.The deprecated legacy implementation,
ExpressionBasedPostInvocationAdvice#after, guards the call explicitly:That guard was not carried over to the new
AuthorizationManager-based interceptor, which is what made the migration a behavioural breaking change for any service that returnsnullas a "nothing to filter" signal.Change
PostFilterAuthorizationMethodInterceptor#invokenow short-circuits when the intercepted method returnsnull: it logs the same debug message as the legacy advice and returnsnullto the caller without consulting the expression handler. TheMethodSecurityExpressionHandler#filtercontract is intentionally left unchanged — semanticallynullis not a collection, so the handler should still reject it; the interceptor is the right layer to decide that@PostFilterover anullreturn is a no-op.Tests
Added
invokeWhenReturnedObjectIsNullThenSkipsFilteringAndReturnsNullinPostFilterAuthorizationMethodInterceptorTestscovering the scenario from the issue: an intercepted invocation whoseproceed()returnsnullnow resolves tonullinstead of throwing. Before the fix this test fails withIllegalArgumentExceptionfromDefaultMethodSecurityExpressionHandler#filter; after, it passes../gradlew :spring-security-core:test --tests 'org.springframework.security.authorization.method.*'runs all 117 method-authorization tests green, so no other consumer of the interceptor is affected.:spring-security-core:checkstyleMainand:checkstyleTestalso pass.Verification done
gh pr list --repo spring-projects/spring-security --search "PostFilter null"and... "PostFilterAuthorizationMethodInterceptor"returned no open PRs touching this code path; the issue thread has no "I'll work on this" claim.main: confirmedPostFilterAuthorizationMethodInterceptor#invokeonmain(HEADbf06d19225) still callsexpressionHandler.filterwithout a null guard.ExpressionBasedPostInvocationAdvice#afterinaccess/src/main/java/...still has the explicitif (returnedObject != null)branch this PR mirrors.