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
48 lines
3.1 KiB
Plaintext
48 lines
3.1 KiB
Plaintext
==============================================================================
|
|
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.
|