1
0

Split into per-article modules and add the method-security module

Moves the existing virtual-thread/context-propagation project into
context-propagation/ and adds method-security/ for the Spring Security 7
method-security article: nine runnable demos, fourteen assertions, and every
transcript the article quotes, regenerated by scripts/run-all.sh.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RSrsDSRKVsY588yFiMJMo9
This commit is contained in:
2026-08-25 02:01:29 +00:00
parent 9f950bffa9
commit 5e9e7f1b12
65 changed files with 4088 additions and 119 deletions

View File

@@ -0,0 +1,58 @@
==============================================================================
Demo 5 -- filterObject: mutability, container types, and the silent no-op
==============================================================================
SLF4J(W): No SLF4J providers were found.
SLF4J(W): Defaulting to no-operation (NOP) logger implementation
SLF4J(W): See https://www.slf4j.org/codes.html#noProviders for further details.
@PreFilter needs a MUTABLE argument -- and does not tell you when it is not
---------------------------------------------------------------------------
method body saw: [Account[1,alice,100], Account[3,alice,300]]
new ArrayList<>(..) ALLOWED -> (void)
method body saw: [Account[1,alice,100], Account[2,bob,200], Account[3,alice,300]]
List.of(..) (immutable) ALLOWED -> (void)
method body saw: [Account[1,alice,100], Account[2,bob,200], Account[3,alice,300]]
List.copyOf(..) (immutable) ALLOWED -> (void)
method body saw: [Account[1,alice,100], Account[2,bob,200], Account[3,alice,300]]
Arrays.asList(..) ALLOWED -> (void)
method body saw: [Account[1,alice,100], Account[2,bob,200], Account[3,alice,300]]
Collections.unmodifiableList(..) ALLOWED -> (void)
method body saw: [Account[1,alice,100], Account[2,bob,200], Account[3,alice,300]]
stream().toList() (unmodifiable since 16) ALLOWED -> (void)
method body saw: [Account[1,alice,100], Account[3,alice,300]]
stream().collect(toList()) (ArrayList) ALLOWED -> (void)
Account[] (arrays rejected outright) DENIED -> IllegalStateException: Pre-filtering on array types is not supported. Using a Collection will solve this problem.
Read the second and third lines again: bob's account reached the method
body. @PreFilter filters by CLEARING the caller's collection and adding
the survivors back. On an immutable list that throws, and
DefaultMethodSecurityExpressionHandler.filterCollection catches the
UnsupportedOperationException and returns a fresh list instead -- which
PreFilterAuthorizationMethodInterceptor.invoke then discards, because it
ignores filter()'s return value entirely. No exception, no WARN, no 403.
The only trace it leaves (same call, logger at TRACE)
-----------------------------------------------------
method body saw: [Account[1,alice,100], Account[2,bob,200], Account[3,alice,300]]
List.of(..) with TRACE on ALLOWED -> (void)
What @PostFilter accepts as a return type
-----------------------------------------
List<Account> ALLOWED -> [Account[1,alice,100], Account[3,alice,300]]
Account[] ALLOWED -> [Account[1,alice,100], Account[3,alice,300]]
Stream<Account> (collected here) ALLOWED -> [alice, alice]
Map<String, Account> ALLOWED -> {acct-1=Account[1,alice,100], acct-3=Account[3,alice,300]}
Optional<Account> (alice's) DENIED -> IllegalArgumentException: Filter target must be a collection, array, map or stream type, but was Optional[Account[1,alice,100]]
Optional<Account> (bob's) DENIED -> IllegalArgumentException: Filter target must be a collection, array, map or stream type, but was Optional[Account[2,bob,200]]
List.of(..) (immutable return) ALLOWED -> [Account[1,alice,100], Account[3,alice,300]]
Ledger (a type Spring Security does not know) DENIED -> IllegalArgumentException: Filter target must be a collection, array, map or stream type, but was Ledger[Account[1,alice,100], Account[2,bob,200], Account[3,alice,300]]
Page<Account> (real Spring Data PageImpl) DENIED -> IllegalArgumentException: Filter target must be a collection, array, map or stream type, but was Page 1 of 1 containing com.ankurm.methodsec.Account instances
Identity: does @PostFilter hand back the same object?
-----------------------------------------------------
returned == the list the method returned : true
the method's own list, after filtering : [Account[1,alice,100], Account[3,alice,300]]
@PostFilter mutates the returned collection in place and hands the same
reference back. If that collection is a cached or shared instance, you
have just deleted rows from it for every future caller.