============================================================================== 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.