// OSSeva Blog
MigrationSpring Security 5.8 to 6.x Migration: What Breaks and How to Prepare on 5.8
The short answer
Do most of the work on 5.8. The Spring Security team designed 5.8 so that nearly every default that changes in 6.0 can be switched on early, with the deprecated APIs still present and Spring Boot 2.7 underneath. Its migration guide, "Preparing for 6.0", lists each change with the 5.8 configuration that opts into it. Apply them one at a time, test each one, and the move to 6.x comes down to removing deprecated calls and doing the Spring Boot 3 upgrade.
Check the dates before you pick a target. Open source support for 5.8 ended on 31 December 2023. Every 6.x line is now out of open source support too: 6.5, the last of them, ended on 30 June 2026. The lines with open source support are 7.0, until 31 December 2026, and 7.1, until 31 July 2027. So 6.x is a waypoint. Spring's documentation describes 6.5 as the bridge to 7.0, the way 5.8 was the bridge to 6.0.
What changes in 6.0
All of these come from the 5.8 migration guide.
| Area | Change in 6.0 | Prepare on 5.8 by |
|---|---|---|
| Configuration | WebSecurityConfigurerAdapter removed | Publishing SecurityFilterChain, WebSecurityCustomizer and AuthenticationManager beans |
| Configuration | antMatchers, mvcMatchers and regexMatchers removed | Switching to requestMatchers, which picks MvcRequestMatcher when Spring MVC is on the classpath and AntPathRequestMatcher otherwise |
| Configuration | @Configuration removed from @EnableWebSecurity, @EnableMethodSecurity and related annotations | Adding @Configuration to those classes |
| Request authorization | authorizeHttpRequests and AuthorizationManager replace authorizeRequests and AccessDecisionManager | Moving to authorizeHttpRequests and replacing custom voters |
| Request authorization | Requests with no matching rule are denied | Ending every rule set with an explicit anyRequest() rule |
| Request authorization | All dispatcher types are authorized, including FORWARD, INCLUDE and ERROR | Testing forwards and error pages, and permitting them where needed |
| Request authorization | hasRole in authorizeHttpRequests ignores a custom GrantedAuthorityDefaults prefix | Using hasAuthority with the full authority name |
| Method security | @EnableGlobalMethodSecurity deprecated (not removed) | Switching to @EnableMethodSecurity, which enables pre and post annotations by default |
| CSRF | Loading of the CsrfToken is deferred until needed | Opting into deferred loading with CsrfTokenRequestAttributeHandler |
| CSRF | BREACH protection: the token is masked with XorCsrfTokenRequestAttributeHandler | Opting in, and fixing single-page apps that read the token from a cookie |
| Sessions | The SecurityContext is no longer saved automatically; SecurityContextHolderFilter only reads it | Setting requireExplicitSave(true) and saving the context in custom authentication code |
| Sessions | Default repository becomes DelegatingSecurityContextRepository; the RequestCache is only checked when a continue parameter is present; authentication mechanisms must call the SessionAuthenticationStrategy themselves | Applying each opt-in from the guide |
| Authentication | Remember-me tokens use SHA-256; AuthenticationServiceException goes to the container instead of the entry point | Encoding with SHA-256 while still matching MD5, and rethrowing service exceptions |
| Passwords | New minimums for PBKDF2, SCrypt and Argon2 encoders | Replacing deprecated encoder constructors; no action if you use the default encoder |
| OAuth2 | oauth2Login() grants OAUTH2_USER or OIDC_USER instead of ROLE_USER | Updating rules that check hasRole("USER") for OAuth2 users |
| OAuth2 | Implicit grant support and deprecated OAuth2 client classes removed; JwtAuthenticationConverter#extractAuthorities removed | Using the listed replacements and setJwtGrantedAuthoritiesConverter |
| SAML | OpenSAML 4 and OpenSaml4AuthenticationProvider | Upgrading OpenSAML and the provider on 5.8 |
The order to do it in
- Get onto the latest patch releases of Spring Boot 2.7 and Spring Security 5.8. On Boot 2.7 this means overriding the Spring Security version from 5.7 to 5.8, which the guide notes is fully compatible.
- Turn on deprecation warnings in the build and treat them as the work list.
- Remove
WebSecurityConfigurerAdapterand the old matchers. These are compile-time changes with no behaviour change on 5.8, so they are the safest place to start. - Switch to
authorizeHttpRequests, add an explicitanyRequest()rule, then turn on filtering of all dispatcher types. - Apply the CSRF, session, authentication and OAuth2 opt-ins one at a time, each in its own release, with the tests below run for each.
- Upgrade to Spring Boot 3 and Spring Security 6. This brings Java 17 and the move from
javax.*tojakarta.*; see the Spring Boot 2 to 3 migration guide. - Continue to 6.5, then follow its "Preparing for 7.0" steps before moving to Spring Security 7 on Spring Boot 4.
Before and after
A typical 5.x configuration:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/public/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
The same rules written so they compile on 5.8 and on 6.x:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults());
return http.build();
}
}
If a single-page application breaks after the CSRF changes and you need time to fix it, the guide gives an opt-out that restores the 5.8 behaviour:
CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
requestHandler.setCsrfRequestAttributeName(null);
http.csrf((csrf) -> csrf.csrfTokenRequestHandler(requestHandler));
Use opt-outs as temporary measures with a ticket attached, not as the end state.
Test plan
Most of these changes fail open or fail closed without a compile error, so the tests have to check behaviour.
- Build an access matrix before you change anything. List every endpoint with the expected status for an anonymous user and for each role, and write it as a table-driven MockMvc or WebTestClient test using
spring-security-test. Run it on the current version first, so you know it passes, then after every step. - Look for new 403 responses after deny-by-default and dispatcher filtering, especially on error pages, forwards to views and endpoints that previously had no rule.
- Log in, then make a second request. A login that appears to work but leaves the user anonymous on the next request is the usual symptom of the explicit-save change in custom authentication filters.
- Test CSRF from real clients: server-rendered forms, single-page apps that read the token from a cookie, and any client that sends the token in a header.
- Test every authentication route: form login, HTTP Basic, remember-me cookies issued before the change, OAuth2 and OIDC login with the new authorities, JWT resource servers and SAML.
- Check method security still applies after moving to
@EnableMethodSecurity, including any custom expression handler or permission evaluator. - Include actuator and management endpoints in the access matrix. They are easy to forget and often the most sensitive.
Rollback
Each preparation step on 5.8 is a code or configuration change, so it rolls back with a redeploy of the previous build. That is the main benefit of doing the work on 5.8. Two things persist and need checking before each rollout:
- Stored credentials and tokens. Password hashes written with new encoder settings and remember-me cookies issued with SHA-256 must still be readable by the version you might roll back to. The guide's remember-me opt-in encodes with SHA-256 while still matching MD5 for this reason.
- Sessions. A serialized
SecurityContextin a shared session store, such as Spring Session on Redis or JDBC, may not deserialize across Spring Security versions. Plan to invalidate sessions when you cut over and when you roll back.
The Spring Boot 3 step itself is a larger change. Keep the last Boot 2.7 build deployable, with its own patched dependencies, until the new version has run through a full release cycle.
If the migration will take longer than your support window
Commercial support from Broadcom covers 5.8 until 30 June 2029 and 6.5 until 30 June 2032, under a Spring enterprise subscription. OSSeva is the other route. It ships patched Spring Security builds for 5.6, 5.7 and 5.8 and for the 6.0 to 6.5 lines that have left open source support, with emergency patches for authentication bypass CVEs, delivered as signed artifacts through Maven Central or your private repository. Assure adds a Spring Security configuration audit, an OAuth 2.0 and OIDC review, a Spring Security 6 migration assessment, VEX attestation for scanner findings and SOC 2 or PCI DSS evidence. Operate adds 24/7 monitoring of authentication events, a 15-minute P1 response for authentication bypass incidents and execution of the Spring Security 6 migration. See Spring Security support and Spring continuation.
Related
Tags
Ready to get your open source under control?
Talk to an OSSeva engineer about CVE coverage, compliance, and migration support for your stack.