# method-security Companion module for [Method Security in Spring Security 7: `@PreAuthorize`, `@PostAuthorize` and the Proxy Traps](https://ankurm.com/spring-security-7-method-security-proxy-traps/) on ankurm.com. Nine small programs and a JUnit suite, each answering one question about what `@PreAuthorize` and its siblings actually do at runtime — including the three cases where they do nothing at all and say nothing about it. No web layer, no Boot application, no server: a plain `AnnotationConfigApplicationContext`, a `SecurityContextHolder`, and real proxied beans, so every result is about method security and not about a filter chain. Part of [spring-security-demo](../README.md); the sibling module is [context-propagation](../context-propagation/README.md). ## Verified versions | Component | Version | |---|---| | JDK | 25 (Temurin 25.0.4.1+1), LTS | | Spring Boot (reference target) | 4.1.1 | | Spring Framework | 7.0.9 | | Spring Security | 7.1.1 (`-core`, `-config`, `-test`) | | AspectJ Weaver | 1.9.25 (only for `exposeProxy` in Demo 2) | | Spring Data Commons | 4.1.1 (only so Demo 5 can filter a real `Page`) | | H2 | 2.4.240 (Demo 6's transaction rollback proof) | | JUnit Jupiter | 6.0.3 | | AssertJ | 3.27.7 | Latest GA on Maven Central at the time of writing, taken from `maven-metadata.xml`. `4.2.0-M1` / `7.2.0-M1` exist as milestones only. ## Quickstart ```bash mvn dependency:build-classpath -Dmdep.outputFile=cp.txt javac --release 25 -parameters -cp "$(cat cp.txt)" -d target/classes $(find src/main -name '*.java') java -cp "target/classes:$(cat cp.txt)" com.ankurm.methodsec.Demo2SelfInvocation ``` `-parameters` matters: without it every `#parameterName` expression in this module silently stops working, which is what Demo 9 is about. Everything at once, regenerating `docs/output/`: `scripts/run-all.sh`. Just the assertions: `mvn test`. ## What each demo shows | Demo | Question | Chapter | |---|---|---| | `Demo1AnnotationsInAction` | What do all six annotation families do on the happy path, and what is thrown when they deny? | [docs/01](docs/01-how-method-security-runs.md) | | `Demo2SelfInvocation` | Why does an annotated method called from inside its own class run unchecked, and what are the three fixes? | [docs/03](docs/03-self-invocation.md) | | `Demo3NonProxyable` | Which of `final` / `static` / `private` / package-private / final-class / interface-only actually get advised? | [docs/04](docs/04-non-proxyable-methods.md) | | `Demo4SpelReference` | Every expression you can write inside the annotation, evaluated as two different users | [docs/02](docs/02-spel-reference.md) | | `Demo5FilteringTraps` | Which container types can `@PreFilter` / `@PostFilter` handle, and what happens on an immutable one? | [docs/05](docs/05-filtering.md) | | `Demo6InterceptorOrder` | Where does the security advice sit relative to `@Transactional`, and does a denial roll back? | [docs/07](docs/07-ordering-and-transactions.md) | | `Demo7DeniedHandling` | What is actually thrown, and how do `@HandleAuthorizationDenied` and `@AuthorizeReturnObject` change it? | [docs/06](docs/06-denied-handling.md) | | `Demo8MetaAnnotations` | Do `{value}` templates need a bean? What does a method-level rule do to a class-level one? | [docs/08](docs/08-meta-annotations.md) | | `Demo9ParameterNames` | The same class compiled with and without `-parameters` | [docs/02](docs/02-spel-reference.md) | | `MethodSecurityTrapsTest` | 14 assertions pinning every claim above | [docs/09](docs/09-audit-checklist.md) | ## Captured output | File | From | |---|---| | [`docs/output/demo1.txt`](docs/output/demo1.txt) … [`demo8.txt`](docs/output/demo8.txt) | the eight demos above | | [`docs/output/demo9-with-parameters.txt`](docs/output/demo9-with-parameters.txt) | Demo 9, compiled with `-parameters` | | [`docs/output/demo9-without-parameters.txt`](docs/output/demo9-without-parameters.txt) | Demo 9, same source, compiled without it | | [`docs/output/tests.txt`](docs/output/tests.txt) | `mvn test` | Regenerate all of it with `scripts/run-all.sh`. Nothing in `docs/` or in the article is hand-typed output. ## Documentation corrections found while building this Each of these disagrees with the current reference documentation or with widely repeated guidance, and each was verified by running the code or reading the 7.1.1 source: - `@EnableMethodSecurity` has an `offset` attribute, not `order`. - `AuthorizationProxyFactory` is in `org.springframework.security.authorization`, not `…authorization.method`. - `AbstractSecurityExpressionHandler.setRoleHierarchy(..)` is deprecated in 7.1; `AuthorizationManagerFactory` is where role hierarchy and role prefix now live. - `{value}` meta-annotation templates work **without** an `AnnotationTemplateExpressionDefaults` bean. - Conflicting `@PreAuthorize` inherited from two interfaces fails at **call time**, not at startup. - `@PreFilter` on an immutable collection is a **silent no-op**, not an exception. - Package-private methods **are** advised by a CGLIB proxy. See [docs/09](docs/09-audit-checklist.md) for the full index. ## License MIT — see [LICENSE](../LICENSE).