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,47 @@
==============================================================================
Demo 8 -- meta-annotations, templates, class-level rules, ambiguity
==============================================================================
A plain meta-annotation needs no extra configuration
----------------------------------------------------
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.
@IsAdmin (alice, ROLE_USER) DENIED -> AuthorizationDeniedException: Access Denied
@IsAdmin (root, ROLE_ADMIN) ALLOWED -> ok
A TEMPLATED meta-annotation, with NO AnnotationTemplateExpressionDefaults bean
------------------------------------------------------------------------------
@HasRole("ADMIN") as root (ROLE_ADMIN) ALLOWED -> ok
@HasRole("ADMIN") as alice (ROLE_USER) DENIED -> AuthorizationDeniedException: Access Denied
'{value}' was substituted anyway. The reference documentation says you
must publish an AnnotationTemplateExpressionDefaults bean for templated
meta-annotations to work; in 7.1.1 you do not.
PreAuthorizeExpressionAttributeRegistry initialises its scanner with
SecurityAnnotationScanners.requireUnique(PreAuthorize.class), and that
overload constructs a default AnnotationTemplateExpressionDefaults for
you. Publishing the bean only changes ignoreUnknown.
The same annotation WITH the AnnotationTemplateExpressionDefaults bean
----------------------------------------------------------------------
@HasRole("ADMIN") as root (ROLE_ADMIN) ALLOWED -> ok
@HasRole("ADMIN") as alice (ROLE_USER) DENIED -> AuthorizationDeniedException: Access Denied
@HasRole("USER") as alice (ROLE_USER) ALLOWED -> ok
Class-level rules, and what a method-level one does to them
-----------------------------------------------------------
inherited from the class (needs ADMIN) DENIED -> AuthorizationDeniedException: Access Denied
method-level overrides it (needs USER) ALLOWED -> ok
class @PreAuthorize AND method @PostAuthorize DENIED -> AuthorizationDeniedException: Access Denied
Two interfaces, two different @PreAuthorize on the same method
--------------------------------------------------------------
context started fine.
bean type -> jdk.proxy2.$Proxy21
read() -- inherits two conflicting rules DENIED -> AnnotationConfigurationException: Please ensure there is one unique annotation of type [interface org.springframework.security.access.prepost.PreAuthorize] attributed to public abstract java.lang.String com.ankurm.methodsec.Demo8MetaAnnotations$ReadsAsUser.read(). Found 2 competing annotations: [@org.springframework.security.access.prepost.PreAuthorize("hasRole('USER')"), @org.springframework.security.access.prepost.PreAuthorize("hasRole('ADMIN')")]
It is not a startup failure: the context refreshes, the bean is
proxied, and the conflict only surfaces when the method is called.
The fix is to put @PreAuthorize on the implementation method, which
is the nearest declaration and therefore wins outright.