1
0
Files
asmhatre 5e9e7f1b12 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
2026-08-25 02:01:29 +00:00

96 lines
5.1 KiB
Markdown

# 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).