From 048b6bdd88ae16213e46f02702ebe93811a9b8aa Mon Sep 17 00:00:00 2001
From: Robert Winch <362503+rwinch@users.noreply.github.com>
Date: Fri, 16 Jan 2026 10:51:42 -0600
Subject: [PATCH] Update to JDK 25 (release = 17)
This commit updates the build to use JDK 25 while remaining compatable with JDK 17.
Note that we must update our JAAS related tests to use release=25 due to the disabling of
the Security Manager. See
https://docs.oracle.com/en/java/javase/25/security/security-manager-is-permanently-disabled.html
Closes gh-18512
---
.github/workflows/check-snapshots.yml | 6 ++--
.../continuous-integration-workflow.yml | 2 +-
.../gradle-wrapper-upgrade-execution.yml | 4 +--
.github/workflows/pr-build-workflow.yml | 4 +--
.sdkmanrc | 2 +-
build.gradle | 12 ++++++--
.../groovy/test-compile-target-jdk25.gradle | 30 +++++++++++++++++++
config/spring-security-config.gradle | 1 +
.../config/http/MiscHttpConfigTests.java | 3 +-
web/spring-security-web.gradle | 1 +
.../JaasApiIntegrationFilterTests.java | 5 ++--
11 files changed, 53 insertions(+), 17 deletions(-)
create mode 100644 buildSrc/src/main/groovy/test-compile-target-jdk25.gradle
diff --git a/.github/workflows/check-snapshots.yml b/.github/workflows/check-snapshots.yml
index f482c0459f..f14526a76b 100644
--- a/.github/workflows/check-snapshots.yml
+++ b/.github/workflows/check-snapshots.yml
@@ -18,10 +18,8 @@ jobs:
strategy:
matrix:
include:
- - java-version: 21-ea
- toolchain: 21
- - java-version: 17
- toolchain: 17
+ - java-version: 25
+ toolchain: 25
with:
java-version: ${{ matrix.java-version }}
test-args: --refresh-dependencies -PforceMavenRepositories=snapshot,https://oss.sonatype.org/content/repositories/snapshots -PisOverrideVersionCatalog -PtestToolchain=${{ matrix.toolchain }} -PspringFrameworkVersion=7.+ -PreactorVersion=2025.+ -PspringDataVersion=2025.+ --stacktrace
diff --git a/.github/workflows/continuous-integration-workflow.yml b/.github/workflows/continuous-integration-workflow.yml
index 12e9eec754..cb07005577 100644
--- a/.github/workflows/continuous-integration-workflow.yml
+++ b/.github/workflows/continuous-integration-workflow.yml
@@ -21,7 +21,7 @@ jobs:
strategy:
matrix:
os: [ ubuntu-latest, windows-latest ]
- jdk: [ 17 ]
+ jdk: [ 25 ]
with:
runs-on: ${{ matrix.os }}
java-version: ${{ matrix.jdk }}
diff --git a/.github/workflows/gradle-wrapper-upgrade-execution.yml b/.github/workflows/gradle-wrapper-upgrade-execution.yml
index 8207edddef..7886f32425 100644
--- a/.github/workflows/gradle-wrapper-upgrade-execution.yml
+++ b/.github/workflows/gradle-wrapper-upgrade-execution.yml
@@ -20,10 +20,10 @@ jobs:
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
- name: Checkout
uses: actions/checkout@v4
- - name: Set up JDK 17
+ - name: Set up JDK 25
uses: actions/setup-java@v4
with:
- java-version: '17'
+ java-version: '25'
distribution: 'temurin'
- name: Set up Gradle
uses: gradle/gradle-build-action@v2
diff --git a/.github/workflows/pr-build-workflow.yml b/.github/workflows/pr-build-workflow.yml
index 2ebf86c76b..5578899d8b 100644
--- a/.github/workflows/pr-build-workflow.yml
+++ b/.github/workflows/pr-build-workflow.yml
@@ -15,7 +15,7 @@ jobs:
- name: Set up gradle
uses: spring-io/spring-gradle-build-action@v2
with:
- java-version: '17'
+ java-version: '25'
distribution: 'temurin'
- name: Build with Gradle
run: ./gradlew clean build -PskipCheckExpectedBranchVersion --continue --scan
@@ -28,7 +28,7 @@ jobs:
- name: Set up gradle
uses: spring-io/spring-gradle-build-action@v2
with:
- java-version: '17'
+ java-version: '25'
distribution: 'temurin'
- name: Run Antora
run: ./gradlew -PbuildSrc.skipTests=true :spring-security-docs:antora
diff --git a/.sdkmanrc b/.sdkmanrc
index 64345a4be3..f6c602d589 100644
--- a/.sdkmanrc
+++ b/.sdkmanrc
@@ -3,4 +3,4 @@
# See https://sdkman.io/usage#config
# A summary is to add the following to ~/.sdkman/etc/config
# sdkman_auto_env=true
-java=17.0.3-tem
+java=25-librca
diff --git a/build.gradle b/build.gradle
index 33a1ae068e..acf92f6d59 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,5 +1,7 @@
import io.spring.gradle.IncludeRepoTask
+import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import trang.RncToXsd
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
buildscript {
dependencies {
@@ -50,7 +52,7 @@ def toolchainVersion() {
if (project.hasProperty('testToolchain')) {
return project.property('testToolchain').toString().toInteger()
}
- return 17
+ return 25
}
subprojects {
@@ -61,7 +63,7 @@ subprojects {
}
kotlin {
jvmToolchain {
- languageVersion = JavaLanguageVersion.of(17)
+ languageVersion = JavaLanguageVersion.of(toolchainVersion())
}
}
tasks.withType(JavaCompile).configureEach {
@@ -69,6 +71,12 @@ subprojects {
options.compilerArgs.add("-parameters")
options.release.set(17)
}
+ tasks.withType(KotlinCompile).configureEach {
+ compilerOptions {
+ javaParameters = true
+ jvmTarget.set(JvmTarget.JVM_17)
+ }
+ }
}
allprojects {
diff --git a/buildSrc/src/main/groovy/test-compile-target-jdk25.gradle b/buildSrc/src/main/groovy/test-compile-target-jdk25.gradle
new file mode 100644
index 0000000000..2a65969ece
--- /dev/null
+++ b/buildSrc/src/main/groovy/test-compile-target-jdk25.gradle
@@ -0,0 +1,30 @@
+import org.gradle.api.tasks.compile.JavaCompile
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+/**
+ * We need to compile with JDK 25 for nullability support, but using JDK 25 means that our tests will fail due to the
+ * removal
+ * of the Java Security Manager. For example, in JDK 25 {@code Subject.getSubject(AccessControlContext)} throws an
+ * {@code UnsupportedOperationException}.
+ *
+ * To resolve this, we must migrate tests to use the new APIs (e.g. {@code Subject.current()}) but those APIs are not
+ * available in the JDK 17 source, so compiling with JDK 25 and release 17 fails. The plugin overrides the test
+ * compilation to use release 25.
+ *
+ * @see The
+ * Security Manager Is Permanently Disabled
+ * @see Quality Outreach Heads-up - JDK 23: Re-Specified
+ * Subject.getSubject API
+ */
+
+tasks.withType(JavaCompile).configureEach { task ->
+ if (task.name == 'compileTestJava' || task.name == 'compileIntegrationTestJava') {
+ task.options.release.set(25)
+ }
+}
+
+tasks.withType(KotlinCompile).configureEach { task ->
+ if (task.name == 'compileTestKotlin' || task.name == 'compileIntegrationTestKotlin') {
+ task.kotlinOptions.jvmTarget = '25'
+ }
+}
diff --git a/config/spring-security-config.gradle b/config/spring-security-config.gradle
index d732cf4c2d..d5ba327bfe 100644
--- a/config/spring-security-config.gradle
+++ b/config/spring-security-config.gradle
@@ -4,6 +4,7 @@ import trang.RncToXsd
apply plugin: 'io.spring.convention.spring-module'
apply plugin: 'trang'
apply plugin: 'security-kotlin'
+apply plugin: 'test-compile-target-jdk25'
configurations {
opensaml5 {
diff --git a/config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java b/config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java
index 8a4a15821a..8406ba1527 100644
--- a/config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java
+++ b/config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java
@@ -19,7 +19,6 @@ package org.springframework.security.config.http;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
-import java.security.AccessController;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.Arrays;
@@ -989,7 +988,7 @@ public class MiscHttpConfigTests {
@GetMapping("/username")
String username() {
- Subject subject = Subject.getSubject(AccessController.getContext());
+ Subject subject = Subject.current();
return subject.getPrincipals().iterator().next().getName();
}
diff --git a/web/spring-security-web.gradle b/web/spring-security-web.gradle
index 887966e47f..2be598a34d 100644
--- a/web/spring-security-web.gradle
+++ b/web/spring-security-web.gradle
@@ -1,6 +1,7 @@
plugins {
id 'security-nullability'
id 'javadoc-warnings-error'
+ id 'test-compile-target-jdk25'
}
apply plugin: 'io.spring.convention.spring-module'
diff --git a/web/src/test/java/org/springframework/security/web/jaasapi/JaasApiIntegrationFilterTests.java b/web/src/test/java/org/springframework/security/web/jaasapi/JaasApiIntegrationFilterTests.java
index c6b1cefbe3..eb510fa600 100644
--- a/web/src/test/java/org/springframework/security/web/jaasapi/JaasApiIntegrationFilterTests.java
+++ b/web/src/test/java/org/springframework/security/web/jaasapi/JaasApiIntegrationFilterTests.java
@@ -17,7 +17,6 @@
package org.springframework.security.web.jaasapi;
import java.io.IOException;
-import java.security.AccessController;
import java.util.HashMap;
import javax.security.auth.Subject;
@@ -131,7 +130,7 @@ public class JaasApiIntegrationFilterTests {
*/
@Test
public void currentSubjectNull() {
- assertThat(Subject.getSubject(AccessController.getContext())).isNull();
+ assertThat(Subject.current()).isNull();
}
@Test
@@ -207,7 +206,7 @@ public class JaasApiIntegrationFilterTests {
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// See if the subject was updated
- Subject currentSubject = Subject.getSubject(AccessController.getContext());
+ Subject currentSubject = Subject.current();
assertThat(currentSubject).isEqualTo(expectedValue);
// run so we know the chain was executed
super.doFilter(request, response);