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
77 lines
3.9 KiB
Markdown
77 lines
3.9 KiB
Markdown
[← chapter index](README.md) · [next: the SpEL reference →](02-spel-reference.md)
|
|
|
|
# 01 · How method security actually runs
|
|
|
|
Run: `java -cp target/classes:$(cat cp.txt) com.ankurm.methodsec.Demo1AnnotationsInAction`
|
|
Output: [`output/demo1.txt`](output/demo1.txt)
|
|
Source: [`Demo1AnnotationsInAction.java`](../src/main/java/com/ankurm/methodsec/Demo1AnnotationsInAction.java)
|
|
|
|
## The one-sentence model
|
|
|
|
`@PreAuthorize` is not a keyword the JVM understands. It is an annotation that a Spring AOP
|
|
**advisor** matches, on a **proxy** that wraps your bean, intercepting calls that arrive from
|
|
outside. Every trap in this repository follows from that sentence.
|
|
|
|
## What `@EnableMethodSecurity` switches on
|
|
|
|
Nothing happens without it. Spring Boot's security auto-configuration does not enable method
|
|
security; the annotation is yours to add. Its attributes, read out of the `AnnotationDefault`
|
|
attributes in `spring-security-config-7.1.1.jar` rather than from documentation:
|
|
|
|
| Attribute | Default | Effect |
|
|
|---|---|---|
|
|
| `prePostEnabled` | `true` | `@PreAuthorize`, `@PostAuthorize`, `@PreFilter`, `@PostFilter` |
|
|
| `securedEnabled` | `false` | `@Secured` |
|
|
| `jsr250Enabled` | `false` | `@RolesAllowed`, `@PermitAll`, `@DenyAll` |
|
|
| `proxyTargetClass` | `false` | Force CGLIB even when the bean implements interfaces |
|
|
| `mode` | `AdviceMode.PROXY` | The alternative is `ASPECTJ`, which sidesteps chapters 03 and 04 entirely |
|
|
| `offset` | `0` | Shifts every security advisor's order by this amount |
|
|
|
|
Note the third row. `@Secured("ROLE_ADMIN")` and `@RolesAllowed("ADMIN")` compile, look
|
|
correct in review, and do nothing at all until you switch them on. That is the zeroth silent
|
|
failure, and unlike the two the article is named for it takes one attribute to fix.
|
|
|
|
There is no `order` attribute, despite what several guides say. It is `offset`, and it moves
|
|
all the interceptors together — see [chapter 07](07-ordering-and-transactions.md).
|
|
|
|
## The call path
|
|
|
|
```
|
|
caller
|
|
└─ proxy (CGLIB subclass, or JDK dynamic proxy)
|
|
└─ @PreFilter advisor, order 100 mutates the argument collection
|
|
└─ @PreAuthorize advisor, order 200 evaluates SpEL, throws or proceeds
|
|
└─ @PostAuthorize advisor, order 500
|
|
└─ @PostFilter advisor, order 600
|
|
└─ your method body
|
|
```
|
|
|
|
Each advisor builds a `MethodSecurityExpressionRoot` over the `Authentication` from
|
|
`SecurityContextHolder` plus the `MethodInvocation`, hands it to a
|
|
`MethodSecurityExpressionHandler`, and evaluates the annotation's expression against it.
|
|
|
|
## What a denial looks like
|
|
|
|
`Demo1` runs the same service as three identities. Two details from
|
|
[`output/demo1.txt`](output/demo1.txt) are worth internalising:
|
|
|
|
- With an `Authentication` present but insufficient, you get `AuthorizationDeniedException`.
|
|
- With **no** `Authentication` at all you get `AuthenticationCredentialsNotFoundException`
|
|
instead — a different exception, from a different place, which an `@ExceptionHandler` written
|
|
only for `AccessDeniedException` will not catch. `@PermitAll` still returns normally, because
|
|
it never asks for the `Authentication`.
|
|
|
|
In a servlet application both are normally translated by `ExceptionTranslationFilter`, so you
|
|
see 403 and 401 respectively. Outside a request — a scheduled job, a message listener, a test —
|
|
nothing translates them and they surface raw.
|
|
|
|
## Where the `Authentication` comes from
|
|
|
|
`SecurityContextHolder`, on the calling thread. If the call happens on a thread that never
|
|
received the context, method security does not fail open; it throws
|
|
`AuthenticationCredentialsNotFoundException`. Getting the context onto that thread is a
|
|
separate topic with its own module in this repository — see
|
|
[`../context-propagation/`](../context-propagation/README.md).
|
|
|
|
[← chapter index](README.md) · [next: the SpEL reference →](02-spel-reference.md)
|