Spring Security 7.1 JWT authentication on Spring Boot 4.1
Runnable companion for https://ankurm.com/spring-security-7-1-jwt-authentication-guide/ - login -> token issue -> OncePerRequestFilter -> SecurityContext, end to end - HS256 and RS256 variants (RS256 publishes a real JWKS endpoint) - the same API secured by the built-in oauth2ResourceServer().jwt(), for comparison - 11 documentation chapters under docs/, interlinked with the code - docs/output/ is real captured output, regenerated by scripts/run-all.sh - 13 passing tests pinning the 401-vs-403 contract and the CSRF failure Verified against Spring Boot 4.1.1, Spring Security 7.1.1, JDK 25.0.4.1.
This commit is contained in:
132
docs/11-spring-security-7-changes.md
Normal file
132
docs/11-spring-security-7-changes.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# 11 — What changed in Spring Security 7
|
||||
|
||||
[← production checklist](10-production-checklist.md) · [README](../README.md)
|
||||
|
||||
Everything below was hit while building this repository against Spring Security **7.1.1**
|
||||
on Spring Boot **4.1.1**, JDK **25**. Verified by compiling or by reading real responses,
|
||||
not from release notes alone.
|
||||
|
||||
## `FACTOR_BEARER` in your authorities
|
||||
|
||||
Every bearer-token authentication now carries an extra authority:
|
||||
|
||||
```json
|
||||
"authorities": ["FACTOR_BEARER", "ROLE_USER", "SCOPE_profile:read"]
|
||||
```
|
||||
|
||||
It backs the new multi-factor authorization support —
|
||||
`AuthorizationManagerFactories.multiFactor()`, `@EnableMultiFactorAuthentication`, and in
|
||||
7.1 the `when` / `withWhen` conditions and `MultiFactorCondition.WEBAUTHN_REGISTERED`.
|
||||
Harmless until a test asserts an exact authority set, or code assumes every authority
|
||||
starts with `ROLE_` or `SCOPE_`.
|
||||
|
||||
## `resource_metadata` in every `WWW-Authenticate`
|
||||
|
||||
```
|
||||
WWW-Authenticate: Bearer realm="jwt-auth-demo",
|
||||
resource_metadata="http://localhost:8080/.well-known/oauth-protected-resource"
|
||||
```
|
||||
|
||||
Spring Security 7 adds `OAuth2ProtectedResourceMetadataFilter` to the resource-server
|
||||
chain — RFC 9728, OAuth 2.0 Protected Resource Metadata. Visible in the
|
||||
[resource-server chain](output/resource-server-loose.txt) and in the entry point's output
|
||||
even on the manual profile, because `BearerTokenAuthenticationEntryPoint` emits it.
|
||||
|
||||
7.1 additionally includes `charset` in `WWW-Authenticate` ([gh-18755]).
|
||||
|
||||
## `NimbusJwtEncoder` builders (7.0+) and their method names
|
||||
|
||||
```java
|
||||
NimbusJwtEncoder.withSecretKey(secretKey).algorithm(MacAlgorithm.HS256).build();
|
||||
NimbusJwtEncoder.withKeyPair(rsaPublic, rsaPrivate).algorithm(SignatureAlgorithm.RS256).build();
|
||||
NimbusJwtEncoder.withKeyPair(ecPublic, ecPrivate).build();
|
||||
```
|
||||
|
||||
Two traps:
|
||||
|
||||
- the builder method is **`algorithm(..)`**, not `jwsAlgorithm(..)` — while the *decoder*
|
||||
builders use `macAlgorithm(..)` and `signatureAlgorithm(..)`;
|
||||
- there is **no `keyId(..)`**. Set `kid` through `jwkPostProcessor(jwk -> jwk.keyID(..))`.
|
||||
|
||||
The pre-7.0 form still compiles:
|
||||
|
||||
```java
|
||||
new NimbusJwtEncoder(new ImmutableSecret<>(secretKey));
|
||||
```
|
||||
|
||||
`setJwkSelector(List::getFirst)` (6.5+) resolves the "multiple matching JWKs" exception.
|
||||
|
||||
## Three sibling classes, three packages
|
||||
|
||||
```java
|
||||
org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint
|
||||
org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler
|
||||
org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter
|
||||
```
|
||||
|
||||
Auto-import will confidently pick the wrong one. This cost a compile cycle here.
|
||||
|
||||
## Jackson 3
|
||||
|
||||
Spring Security 7 moves to Jackson 3 (`tools.jackson.*`). `SecurityJackson2Modules` is
|
||||
replaced by `SecurityJacksonModules` with `JsonMapper.Builder`. Boot 4.1.1 resolves
|
||||
`tools.jackson.core:jackson-databind:3.1.5`. If you serialise a `SecurityContext` — into
|
||||
a session store, a cache, a Redis-backed denylist — that code changes. See the
|
||||
[Jackson 2 to 3 migration guide](https://ankurm.com/jackson-3-migration-guide/).
|
||||
|
||||
## Boot 4 test slices moved
|
||||
|
||||
`@AutoConfigureMockMvc` is now `org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc`,
|
||||
in `spring-boot-starter-webmvc-test`. Security test support is in
|
||||
`spring-boot-starter-security-test`. `spring-boot-starter-test` alone no longer suffices.
|
||||
[→ doc 08](08-testing.md)
|
||||
|
||||
## Boot 4.1: SpEL authority extraction
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
security:
|
||||
oauth2:
|
||||
resourceserver:
|
||||
jwt:
|
||||
authorities-claim-expressions: "['realm_access']['roles']"
|
||||
authority-prefix: "ROLE_"
|
||||
```
|
||||
|
||||
Mutually exclusive with `authorities-claim-name` / `authorities-claim-delimiter`. This is
|
||||
the property-only answer to Keycloak-style nested role claims, which previously needed a
|
||||
custom converter.
|
||||
|
||||
## `csrf.spa()` is new in 7.0
|
||||
|
||||
```java
|
||||
.csrf(csrf -> csrf.spa())
|
||||
```
|
||||
|
||||
One call for `CookieCsrfTokenRepository` + `XorCsrfTokenRequestAttributeHandler` +
|
||||
deferred token loading. Checked against the jars: absent from `spring-security-config`
|
||||
6.4.7 and 6.5.1, present in 7.0.0. Several guides describe it as a 6.x feature.
|
||||
|
||||
## Other 7.1 additions worth knowing
|
||||
|
||||
- `RestClientOpaqueTokenIntrospector` ([gh-18745]) — the `RestClient`-based replacement for the `RestTemplate` introspector, for opaque rather than JWT tokens.
|
||||
- `ConditionalAuthorizationManager` and `AllRequiredFactorsAuthorizationManager.anyOf` ([gh-18960]).
|
||||
- `PreFlightRequestFilter` CORS support ([gh-18926]).
|
||||
- `InetAddressMatcher` ([gh-18634]).
|
||||
- WebAuthn now publishes authentication events ([gh-18113]).
|
||||
|
||||
## Migrating from 6.x
|
||||
|
||||
Spring Security's own advice: go to **6.5** first, use its opt-in switches to adopt the
|
||||
7.0 behaviours one at a time, then upgrade. The 6.5 preparation steps exist precisely so
|
||||
that the 7.0 jump is a version bump rather than a rewrite.
|
||||
|
||||
ankurm.com has a dedicated
|
||||
[Spring Security 5 → 6 → 7 migration guide](https://ankurm.com/spring-security-5-to-6-to-7-migration-guide/).
|
||||
|
||||
[gh-18755]: https://github.com/spring-projects/spring-security/issues/18755
|
||||
[gh-18745]: https://github.com/spring-projects/spring-security/issues/18745
|
||||
[gh-18960]: https://github.com/spring-projects/spring-security/issues/18960
|
||||
[gh-18926]: https://github.com/spring-projects/spring-security/issues/18926
|
||||
[gh-18634]: https://github.com/spring-projects/spring-security/pull/18634
|
||||
[gh-18113]: https://github.com/spring-projects/spring-security/issues/18113
|
||||
Reference in New Issue
Block a user