1
0
mirror of synced 2026-07-10 05:10:03 +00:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Josh Cummings e3e7d76b70 Add Workflow to Finalize a Release 2025-11-04 09:32:41 -07:00
147 changed files with 1095 additions and 1612 deletions
+8
View File
@@ -111,3 +111,11 @@ updates:
labels:
- 'type: task'
- 'in: build'
- package-ecosystem: npm
target-branch: 6.3.x
directory: /docs
schedule:
interval: weekly
labels:
- 'type: task'
- 'in: build'
@@ -17,7 +17,7 @@ permissions:
jobs:
build:
name: Build
uses: spring-io/spring-security-release-tools/.github/workflows/build.yml@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
uses: spring-io/spring-security-release-tools/.github/workflows/build.yml@v1
strategy:
matrix:
os: [ ubuntu-latest, windows-latest ]
@@ -30,22 +30,29 @@ jobs:
deploy-artifacts:
name: Deploy Artifacts
needs: [ build]
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml@v1
with:
should-deploy-artifacts: ${{ needs.build.outputs.should-deploy-artifacts }}
default-publish-milestones-central: true
secrets: inherit
deploy-docs:
name: Deploy Docs
needs: [ build ]
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-docs.yml@v1
with:
should-deploy-docs: ${{ needs.build.outputs.should-deploy-artifacts }}
secrets: inherit
deploy-schema:
name: Deploy Schema
needs: [ build ]
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml@v1
with:
should-deploy-schema: ${{ needs.build.outputs.should-deploy-artifacts }}
secrets: inherit
perform-release:
name: Perform Release
needs: [ deploy-artifacts, deploy-schema ]
uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
needs: [ deploy-artifacts, deploy-docs, deploy-schema ]
uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@v1
with:
should-perform-release: ${{ needs.deploy-artifacts.outputs.artifacts-deployed }}
project-version: ${{ needs.deploy-artifacts.outputs.project-version }}
@@ -61,6 +68,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Send Notification
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@v1
with:
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
if: github.repository_owner == 'spring-projects'
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@v4
with:
ref: docs-build
fetch-depth: 1
+19 -5
View File
@@ -2,10 +2,6 @@ name: Finalize Release
on:
workflow_dispatch: # Manual trigger
inputs:
version:
description: The Spring Security release to finalize (e.g. 7.0.0-RC2)
required: true
env:
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
@@ -14,14 +10,32 @@ permissions:
contents: read
jobs:
project-version:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.project-version.outputs.version }}
steps:
- id: project-version
run: echo "version=$(grep '^version=' gradle.properties | cut -d'=' -f2)" >> $GITHUB_OUTPUT
perform-release:
name: Perform Release
needs: [ project-version ]
uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@v1
with:
should-perform-release: true
project-version: ${{ inputs.version }}
project-version: ${{ needs.project-version.outputs.version }}
milestone-repo-url: https://repo1.maven.org/maven2
release-repo-url: https://repo1.maven.org/maven2
artifact-path: org/springframework/security/spring-security-core
slack-announcing-id: spring-security-announcing
secrets: inherit
send-notification:
name: Send Notification
needs: [ perform-release ]
if: ${{ !success() }}
runs-on: ubuntu-latest
steps:
- name: Send Notification
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@v1
with:
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
@@ -19,14 +19,14 @@ jobs:
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Set up Gradle
uses: gradle/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1
uses: gradle/gradle-build-action@v2
- name: Upgrade Wrappers
run: ./gradlew clean upgradeGradleWrapperAll --continue -Porg.gradle.java.installations.auto-download=false
env:
@@ -30,6 +30,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Send Notification
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@v1
with:
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
+6 -6
View File
@@ -11,9 +11,9 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.repository == 'spring-projects/spring-security' }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@v4
- name: Set up gradle
uses: spring-io/spring-gradle-build-action@efc55f07f4dfa22f2afd97f9ea1be4212eeed737 # v2.0.5
uses: spring-io/spring-gradle-build-action@v2
with:
java-version: '17'
distribution: 'temurin'
@@ -24,9 +24,9 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.repository == 'spring-projects/spring-security' }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@v4
- name: Set up gradle
uses: spring-io/spring-gradle-build-action@efc55f07f4dfa22f2afd97f9ea1be4212eeed737 # v2.0.5
uses: spring-io/spring-gradle-build-action@v2
with:
java-version: '17'
distribution: 'temurin'
@@ -34,7 +34,7 @@ jobs:
run: ./gradlew -PbuildSrc.skipTests=true :spring-security-docs:antora
- name: Upload Docs
id: upload
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@v4
with:
name: docs
path: docs/build/site
@@ -46,6 +46,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Send Notification
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@v1
with:
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Dispatch
@@ -16,9 +16,9 @@ jobs:
name: Update on Supported Branches
strategy:
matrix:
branch: [ '6.4.x', '6.5.x', 'main' ]
branch: [ '5.8.x', '6.2.x', '6.3.x', 'main' ]
steps:
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@415e2b11a766ba64799fffb5c97a4f7e17f677cf
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@e28269199d1d27975cf7f65e16d6095c555b3cd0
name: Update
with:
docs-branch: ${{ matrix.branch }}
@@ -28,7 +28,7 @@ jobs:
runs-on: ubuntu-latest
name: Update on docs-build
steps:
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@415e2b11a766ba64799fffb5c97a4f7e17f677cf
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@e28269199d1d27975cf7f65e16d6095c555b3cd0
name: Update
with:
docs-branch: 'docs-build'
@@ -9,7 +9,7 @@ permissions:
jobs:
update-scheduled-release-version:
name: Update Scheduled Release Version
uses: spring-io/spring-security-release-tools/.github/workflows/update-scheduled-release-version.yml@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
uses: spring-io/spring-security-release-tools/.github/workflows/update-scheduled-release-version.yml@v1
secrets: inherit
send-notification:
name: Send Notification
@@ -18,6 +18,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Send Notification
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@v1
with:
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
@@ -39,7 +39,7 @@ interface EvaluationContextPostProcessor<I> {
* that was passed in.
* @param context the original {@link EvaluationContext}
* @param invocation the security invocation object (i.e. Message)
* @return the updated context.
* @return the upated context.
*/
EvaluationContext postProcess(EvaluationContext context, I invocation);
@@ -20,7 +20,7 @@ import org.springframework.security.acls.model.Acl;
/**
* Strategy used by {@link AclImpl} to determine whether a principal is permitted to call
* administrative methods on the <code>AclImpl</code>.
* adminstrative methods on the <code>AclImpl</code>.
*
* @author Ben Alex
*/
@@ -42,7 +42,7 @@ public class GrantedAuthoritySid implements Sid {
public GrantedAuthoritySid(GrantedAuthority grantedAuthority) {
Assert.notNull(grantedAuthority, "GrantedAuthority required");
Assert.notNull(grantedAuthority.getAuthority(),
"This Sid is only compatible with GrantedAuthority that provide a non-null getAuthority()");
"This Sid is only compatible with GrantedAuthoritys that provide a non-null getAuthority()");
this.grantedAuthority = grantedAuthority.getAuthority();
}
@@ -160,7 +160,7 @@ public class JdbcAclService implements AclService {
this.findChildrenSql = DEFAULT_SELECT_ACL_WITH_PARENT_SQL_WITH_CLASS_ID_TYPE;
}
else {
log.debug("Find children statement has already been overridden, so not overriding the default");
log.debug("Find children statement has already been overridden, so not overridding the default");
}
}
}
@@ -50,7 +50,7 @@ import org.springframework.util.Assert;
* The default settings are for HSQLDB. If you are using a different database you will
* probably need to set the {@link #setSidIdentityQuery(String) sidIdentityQuery} and
* {@link #setClassIdentityQuery(String) classIdentityQuery} properties appropriately. The
* other queries, SQL inserts and updates can also be customized to accommodate schema
* other queries, SQL inserts and updates can also be customized to accomodate schema
* variations, but must produce results consistent with those expected by the defaults.
* <p>
* See the appendix of the Spring Security reference manual for more information on the
@@ -471,7 +471,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
this.insertClass = DEFAULT_INSERT_INTO_ACL_CLASS_WITH_ID;
}
else {
log.debug("Insert class statement has already been overridden, so not overriding the default");
log.debug("Insert class statement has already been overridden, so not overridding the default");
}
}
}
+4 -4
View File
@@ -2,12 +2,12 @@ apply plugin: 'io.spring.convention.spring-module'
apply plugin: 'io.freefair.aspectj'
compileAspectj {
sourceCompatibility = "17"
targetCompatibility = "17"
sourceCompatibility "17"
targetCompatibility "17"
}
compileTestAspectj {
sourceCompatibility = "17"
targetCompatibility = "17"
sourceCompatibility "17"
targetCompatibility "17"
}
dependencies {
+37 -10
View File
@@ -10,7 +10,7 @@ buildscript {
classpath libs.com.netflix.nebula.nebula.project.plugin
}
repositories {
maven { url='https://plugins.gradle.org/m2/' }
maven { url 'https://plugins.gradle.org/m2/' }
}
}
@@ -35,30 +35,57 @@ ext.milestoneBuild = !(snapshotBuild || releaseBuild)
repositories {
mavenCentral()
maven { url = "https://repo.spring.io/milestone" }
maven { url "https://repo.spring.io/milestone" }
}
springRelease {
weekOfMonth = 3
dayOfWeek = 1
referenceDocUrl = "https://docs.spring.io/spring-security/reference/{version}/index.html"
apiDocUrl = "https://docs.spring.io/spring-security/reference/{version}/api/java/index.html"
apiDocUrl = "https://docs.spring.io/spring-security/site/docs/{version}/api/"
replaceSnapshotVersionInReferenceDocUrl = true
}
def toolchainVersion() {
if (project.hasProperty('testToolchain')) {
return project.property('testToolchain').toString().toInteger()
}
return 17
}
subprojects {
java {
toolchain {
languageVersion = JavaLanguageVersion.of(toolchainVersion())
}
}
kotlin {
jvmToolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
tasks.withType(JavaCompile).configureEach {
options.encoding = "UTF-8"
options.compilerArgs.add("-parameters")
options.release.set(17)
}
}
allprojects {
if (!['spring-security-bom', 'spring-security-docs'].contains(project.name)) {
apply plugin: 'io.spring.javaformat'
apply plugin: 'checkstyle'
pluginManager.withPlugin("io.spring.convention.checkstyle") {
dependencies {
checkstyle libs.io.spring.javaformat.spring.javaformat.checkstyle
pluginManager.withPlugin("io.spring.convention.checkstyle", { plugin ->
configure(plugin) {
dependencies {
checkstyle libs.io.spring.javaformat.spring.javaformat.checkstyle
}
checkstyle {
toolVersion = '8.34'
}
}
checkstyle {
toolVersion = '8.34'
}
}
})
if (project.name.contains('sample')) {
tasks.whenTaskAdded { task ->
+1 -1
View File
@@ -12,7 +12,7 @@ java {
repositories {
gradlePluginPortal()
mavenCentral()
maven { url = 'https://repo.spring.io/snapshot' }
maven { url 'https://repo.spring.io/snapshot' }
}
sourceSets {
Binary file not shown.
+5
View File
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored Executable
+240
View File
@@ -0,0 +1,240 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
+91
View File
@@ -0,0 +1,91 @@
@rem
@rem Copyright 2004-present the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@@ -81,8 +81,8 @@ class ArtifactoryPlugin implements Plugin<Project> {
repository {
repoKey = isSnapshot ? snapshotRepository : isMilestone ? milestoneRepository : releaseRepository
if(project.hasProperty('artifactoryUsername')) {
username = project.artifactoryUsername
password = project.artifactoryPassword
username = artifactoryUsername
password = artifactoryPassword
}
}
}
@@ -0,0 +1,82 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package io.spring.gradle.convention
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.tasks.bundling.Zip
import org.gradle.api.Plugin
import org.gradle.api.Project
public class DeployDocsPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
project.getPluginManager().apply('org.hidetake.ssh')
project.ssh.settings {
knownHosts = allowAnyHosts
}
project.remotes {
docs {
role 'docs'
if (project.hasProperty('deployDocsHost')) {
host = project.findProperty('deployDocsHost')
} else {
host = 'docs.af.pivotal.io'
}
retryCount = 5 // retry 5 times (default is 0)
retryWaitSec = 10 // wait 10 seconds between retries (default is 0)
user = project.findProperty('deployDocsSshUsername')
if (project.hasProperty('deployDocsSshKeyPath')) {
identity = project.file(project.findProperty('deployDocsSshKeyPath'))
} else if (project.hasProperty('deployDocsSshKey')) {
identity = project.findProperty('deployDocsSshKey')
}
if(project.hasProperty('deployDocsSshPassphrase')) {
passphrase = project.findProperty('deployDocsSshPassphrase')
}
}
}
project.task('deployDocs') {
dependsOn 'docsZip'
doFirst {
project.ssh.run {
session(project.remotes.docs) {
def now = System.currentTimeMillis()
def name = project.rootProject.name
def version = project.rootProject.version
def tempPath = "/tmp/${name}-${now}-docs/".replaceAll(' ', '_')
execute "mkdir -p $tempPath"
project.tasks.docsZip.outputs.each { o ->
put from: o.files, into: tempPath
}
execute "unzip $tempPath*.zip -d $tempPath"
def extractPath = "/var/www/domains/spring.io/docs/htdocs/autorepo/docs/${name}/${version}/"
execute "rm -rf $extractPath"
execute "mkdir -p $extractPath"
execute "mv $tempPath/docs/* $extractPath"
execute "chmod -R g+w $extractPath"
}
}
}
}
}
}
@@ -17,6 +17,7 @@ public class DocsPlugin implements Plugin<Project> {
PluginManager pluginManager = project.getPluginManager();
pluginManager.apply(BasePlugin);
pluginManager.apply(DeployDocsPlugin);
pluginManager.apply(JavadocApiPlugin);
Task docsZip = project.tasks.create('docsZip', Zip) {
@@ -31,12 +32,12 @@ public class DocsPlugin implements Plugin<Project> {
into 'api'
}
into 'docs'
duplicatesStrategy = 'exclude'
duplicatesStrategy 'exclude'
}
Task docs = project.tasks.create("docs") {
group = 'Documentation'
description = 'An aggregator task to generate all the documentation'
description 'An aggregator task to generate all the documentation'
dependsOn docsZip
}
project.tasks.assemble.dependsOn docs
@@ -90,7 +90,7 @@ public class IntegrationTestPlugin implements Plugin<Project> {
project.plugins.withType(IdeaPlugin) {
project.idea {
module {
testSources.from(project.file('src/integration-test/java'))
testSourceDirs += project.file('src/integration-test/java')
scopes.TEST.plus += [ project.configurations.integrationTestCompileClasspath ]
}
}
@@ -105,7 +105,7 @@ public class IntegrationTestPlugin implements Plugin<Project> {
project.plugins.withType(IdeaPlugin) {
project.idea {
module {
testSources.from(project.file('src/integration-test/groovy'))
testSourceDirs += project.file('src/integration-test/groovy')
}
}
}
@@ -26,7 +26,7 @@ import org.gradle.api.Action;
import org.gradle.api.JavaVersion
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.javadoc.Javadoc;
import org.slf4j.Logger;
@@ -71,7 +71,7 @@ public class JavadocApiPlugin implements Plugin<Project> {
}
api.setMaxMemory("1024m");
api.setDestinationDir(project.layout.getBuildDirectory().dir("api").get().getAsFile());
api.setDestinationDir(new File(project.getBuildDir(), "api"));
project.getPluginManager().apply("io.spring.convention.javadoc-options");
}
@@ -99,7 +99,7 @@ public class JavadocApiPlugin implements Plugin<Project> {
public void execute(SpringModulePlugin plugin) {
logger.info("Added sources for {}", project);
JavaPluginExtension java = project.getExtensions().getByType(JavaPluginExtension.class);
JavaPluginConvention java = project.getConvention().getPlugin(JavaPluginConvention.class);
SourceSet mainSourceSet = java.getSourceSets().getByName("main");
api.setSource(api.getSource().plus(mainSourceSet.getAllJava()));
@@ -40,7 +40,7 @@ public class SchemaZipPlugin implements Plugin<Project> {
throw new IllegalStateException("Could not find schema file for resource name " + schemaResourceName + " in src/main/resources")
}
schemaZip.into (shortName) {
duplicatesStrategy = 'exclude'
duplicatesStrategy 'exclude'
from xsdFile.path
}
versionlessXsd.getInputFiles().from(xsdFile.path)
@@ -35,7 +35,6 @@ class SpringModulePlugin extends AbstractSpringJavaPlugin {
pluginManager.apply(SpringMavenPlugin.class);
pluginManager.apply(CheckClasspathForProhibitedDependenciesPlugin.class);
pluginManager.apply("io.spring.convention.jacoco");
pluginManager.apply("java-toolchain");
def deployArtifacts = project.task("deployArtifacts")
deployArtifacts.group = 'Deploy tasks'
@@ -1,36 +0,0 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
def toolchainVersion() {
if (project.hasProperty('testToolchain')) {
return project.property('testToolchain').toString().toInteger()
}
return 17
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(toolchainVersion())
}
}
tasks.withType(JavaCompile).configureEach {
options.encoding = "UTF-8"
options.compilerArgs.add("-parameters")
options.release = 17
}
pluginManager.withPlugin("org.jetbrains.kotlin.jvm") {
kotlin {
jvmToolchain {
languageVersion = JavaLanguageVersion.of(toolchainVersion())
}
}
tasks.withType(KotlinCompile).configureEach {
compilerOptions {
javaParameters = true
jvmTarget.set(JvmTarget.JVM_17)
}
}
}
+11 -1
View File
@@ -30,6 +30,16 @@ ossrh: {
}
}
},
docs: {
stage('Deploy Docs') {
node {
checkout scm
withCredentials([file(credentialsId: 'docs.spring.io-jenkins_private_ssh_key', variable: 'DEPLOY_SSH_KEY')]) {
sh "./gradlew deployDocs -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME --refresh-dependencies --no-daemon --stacktrace"
}
}
}
},
schema: {
stage('Deploy Schema') {
node {
@@ -39,4 +49,4 @@ schema: {
}
}
}
}
}
@@ -326,7 +326,7 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
/**
* Use this {@code RequestMatcher} to match proxy receptor requests. Without setting
* this matcher, {@link CasAuthenticationFilter} will not capture any proxy receptor
* requests.
* requets.
* @param proxyReceptorMatcher the {@link RequestMatcher} to use
* @since 6.5
*/
@@ -383,8 +383,8 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
}
/**
* Indicates if the request is eligible to process a service ticket. This method
* exists for readability.
* Indicates if the request is elgible to process a service ticket. This method exists
* for readability.
* @param request
* @param response
* @return
@@ -396,7 +396,7 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
}
/**
* Indicates if the request is eligible to process a proxy ticket.
* Indicates if the request is elgible to process a proxy ticket.
* @param request
* @return
*/
@@ -419,7 +419,7 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
}
/**
* Indicates if the request is eligible to be processed as the proxy receptor.
* Indicates if the request is elgible to be processed as the proxy receptor.
* @param request
* @return
*/
+2 -2
View File
@@ -144,14 +144,14 @@ tasks.named('processResources', ProcessResources).configure {
into 'org/springframework/security/config/'
}
from(rncToXsd) {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
duplicatesStrategy DuplicatesStrategy.EXCLUDE
into 'org/springframework/security/config/'
}
}
tasks.named('sourcesJar', Jar).configure {
from(rncToXsd) {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
duplicatesStrategy DuplicatesStrategy.EXCLUDE
into 'org/springframework/security/config/'
}
}
@@ -31,7 +31,6 @@ import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriverException;
@@ -56,7 +55,6 @@ import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.util.StringUtils;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@@ -69,7 +67,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Daniel Garnier-Moiroux
*/
@Disabled
@org.junit.jupiter.api.Disabled
class WebAuthnWebDriverTests {
private String baseUrl;
@@ -84,8 +82,6 @@ class WebAuthnWebDriverTests {
private static final String PASSWORD = "password";
private String authenticatorId = null;
@BeforeAll
static void startChromeDriverService() throws Exception {
driverService = new ChromeDriverService.Builder().usingAnyFreePort().build();
@@ -148,7 +144,7 @@ class WebAuthnWebDriverTests {
@Test
void loginWhenNoValidAuthenticatorCredentialsThenRejects() {
createVirtualAuthenticator(true);
this.getAndWait("/", "/login");
this.driver.get(this.baseUrl);
this.driver.findElement(signinWithPasskeyButton()).click();
await(() -> assertThat(this.driver.getCurrentUrl()).endsWith("/login?error"));
}
@@ -157,7 +153,7 @@ class WebAuthnWebDriverTests {
void registerWhenNoLabelThenRejects() {
login();
this.getAndWait("/webauthn/register");
this.driver.get(this.baseUrl + "/webauthn/register");
this.driver.findElement(registerPasskeyButton()).click();
assertHasAlertStartingWith("error", "Error: Passkey Label is required");
@@ -167,7 +163,7 @@ class WebAuthnWebDriverTests {
void registerWhenAuthenticatorNoUserVerificationThenRejects() {
createVirtualAuthenticator(false);
login();
this.getAndWait("/webauthn/register");
this.driver.get(this.baseUrl + "/webauthn/register");
this.driver.findElement(passkeyLabel()).sendKeys("Virtual authenticator");
this.driver.findElement(registerPasskeyButton()).click();
@@ -182,8 +178,7 @@ class WebAuthnWebDriverTests {
* <li>Step 1: Log in with username / password</li>
* <li>Step 2: Register a credential from the virtual authenticator</li>
* <li>Step 3: Log out</li>
* <li>Step 4: Log in with the authenticator (no allowCredentials)</li>
* <li>Step 5: Log in again with the same authenticator (with allowCredentials)</li>
* <li>Step 4: Log in with the authenticator</li>
* </ul>
*/
@Test
@@ -195,7 +190,7 @@ class WebAuthnWebDriverTests {
login();
// Step 2: register a credential from the virtual authenticator
this.getAndWait("/webauthn/register");
this.driver.get(this.baseUrl + "/webauthn/register");
this.driver.findElement(passkeyLabel()).sendKeys("Virtual authenticator");
this.driver.findElement(registerPasskeyButton()).click();
@@ -217,58 +212,9 @@ class WebAuthnWebDriverTests {
logout();
// Step 4: log in with the virtual authenticator
this.getAndWait("/webauthn/register", "/login");
this.driver.get(this.baseUrl + "/webauthn/register");
this.driver.findElement(signinWithPasskeyButton()).click();
await(() -> assertThat(this.driver.getCurrentUrl()).endsWith("/webauthn/register?continue"));
// Step 5: authenticate while being already logged in
// This simulates some use-cases with MFA. Since the user is already logged in,
// the "allowCredentials" property is populated
this.getAndWait("/login");
this.driver.findElement(signinWithPasskeyButton()).click();
await(() -> assertThat(this.driver.getCurrentUrl()).endsWith("/"));
}
@Test
void registerWhenAuthenticatorAlreadyRegisteredThenRejects() {
createVirtualAuthenticator(true);
login();
registerAuthenticator("Virtual authenticator");
// Cannot re-register the same authenticator because excludeCredentials
// is not empty and contains the given authenticator
this.driver.findElement(passkeyLabel()).sendKeys("Same authenticator");
this.driver.findElement(registerPasskeyButton()).click();
await(() -> assertHasAlertStartingWith("error", "Registration failed"));
}
@Test
void registerSecondAuthenticatorThenSucceeds() {
createVirtualAuthenticator(true);
login();
registerAuthenticator("Virtual authenticator");
this.getAndWait("/webauthn/register");
List<WebElement> passkeyRows = this.driver.findElements(passkeyTableRows());
assertThat(passkeyRows).hasSize(1)
.first()
.extracting((row) -> row.findElement(firstCell()))
.extracting(WebElement::getText)
.isEqualTo("Virtual authenticator");
// Create second authenticator and register
removeAuthenticator();
createVirtualAuthenticator(true);
registerAuthenticator("Second virtual authenticator");
this.getAndWait("/webauthn/register");
passkeyRows = this.driver.findElements(passkeyTableRows());
assertThat(passkeyRows).hasSize(2)
.extracting((row) -> row.findElement(firstCell()))
.extracting(WebElement::getText)
.contains("Second virtual authenticator");
}
/**
@@ -285,14 +231,11 @@ class WebAuthnWebDriverTests {
* "https://chromedevtools.github.io/devtools-protocol/tot/WebAuthn/">https://chromedevtools.github.io/devtools-protocol/tot/WebAuthn/</a>
*/
private void createVirtualAuthenticator(boolean userIsVerified) {
if (StringUtils.hasText(this.authenticatorId)) {
throw new IllegalStateException("Authenticator already exists, please remove it before re-creating one");
}
HasCdp cdpDriver = (HasCdp) this.driver;
cdpDriver.executeCdpCommand("WebAuthn.enable", Map.of("enableUI", false));
// this.driver.addVirtualAuthenticator(createVirtualAuthenticatorOptions());
//@formatter:off
Map<String, Object> cmdResponse = cdpDriver.executeCdpCommand("WebAuthn.addVirtualAuthenticator",
cdpDriver.executeCdpCommand("WebAuthn.addVirtualAuthenticator",
Map.of(
"options",
Map.of(
@@ -305,38 +248,21 @@ class WebAuthnWebDriverTests {
)
));
//@formatter:on
this.authenticatorId = cmdResponse.get("authenticatorId").toString();
}
private void removeAuthenticator() {
HasCdp cdpDriver = (HasCdp) this.driver;
cdpDriver.executeCdpCommand("WebAuthn.removeVirtualAuthenticator",
Map.of("authenticatorId", this.authenticatorId));
this.authenticatorId = null;
}
private void login() {
this.getAndWait("/", "/login");
this.driver.get(this.baseUrl);
this.driver.findElement(usernameField()).sendKeys(USERNAME);
this.driver.findElement(passwordField()).sendKeys(PASSWORD);
this.driver.findElement(signinWithUsernamePasswordButton()).click();
// Ensure login has completed
await(() -> assertThat(this.driver.getCurrentUrl()).doesNotContain("/login"));
}
private void logout() {
this.getAndWait("/logout");
this.driver.get(this.baseUrl + "/logout");
this.driver.findElement(logoutButton()).click();
await(() -> assertThat(this.driver.getCurrentUrl()).endsWith("/login?logout"));
}
private void registerAuthenticator(String passkeyName) {
this.getAndWait("/webauthn/register");
this.driver.findElement(passkeyLabel()).sendKeys(passkeyName);
this.driver.findElement(registerPasskeyButton()).click();
await(() -> assertThat(this.driver.getCurrentUrl()).endsWith("/webauthn/register?success"));
}
private AbstractStringAssert<?> assertHasAlertStartingWith(String alertType, String alertMessage) {
WebElement alert = this.driver.findElement(new By.ById(alertType));
assertThat(alert.isDisplayed())
@@ -363,15 +289,6 @@ class WebAuthnWebDriverTests {
});
}
private void getAndWait(String endpoint) {
this.getAndWait(endpoint, endpoint);
}
private void getAndWait(String endpoint, String redirectUrl) {
this.driver.get(this.baseUrl + endpoint);
this.await(() -> assertThat(this.driver.getCurrentUrl()).endsWith(redirectUrl));
}
private static By.ById passkeyLabel() {
return new By.ById("label");
}
@@ -408,10 +325,6 @@ class WebAuthnWebDriverTests {
return new By.ByCssSelector("button");
}
private static By.ByCssSelector deletePasskeyButton() {
return new By.ByCssSelector("table > tbody > tr > button");
}
/**
* The configuration for WebAuthN tests. It accesses the Server's current port, so we
* can configurer WebAuthnConfigurer#allowedOrigin
@@ -177,7 +177,7 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
}
/**
* Gets a shared Object. Note that object hierarchies are not considered.
* Gets a shared Object. Note that object heirarchies are not considered.
* @param sharedType the type of the shared Object
* @return the shared Object or null if it is not found
*/
@@ -360,7 +360,7 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
/**
* Subclasses must implement this method to build the object that is being returned.
* @return the Object to be built or null if the implementation allows it
* @return the Object to be buit or null if the implementation allows it
*/
protected abstract O performBuild();
@@ -414,13 +414,13 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
private enum BuildState {
/**
* This is the state before the {@link SecurityBuilder#build()} is invoked
* This is the state before the {@link Builder#build()} is invoked
*/
UNBUILT(0),
/**
* The state from when {@link SecurityBuilder#build()} is first invoked until all
* the {@link SecurityConfigurer#init(SecurityBuilder)} methods have been invoked.
* The state from when {@link Builder#build()} is first invoked until all the
* {@link SecurityConfigurer#init(SecurityBuilder)} methods have been invoked.
*/
INITIALIZING(1),
@@ -42,8 +42,7 @@ final class MethodSecuritySelector implements ImportSelector {
.isPresent("org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar", null);
private static final boolean isWebPresent = ClassUtils
.isPresent("org.springframework.web.servlet.DispatcherServlet", null)
&& ClassUtils.isPresent("org.springframework.security.web.util.ThrowableAnalyzer", null);
.isPresent("org.springframework.web.servlet.DispatcherServlet", null);
private static final boolean isObservabilityPresent = ClassUtils
.isPresent("io.micrometer.observation.ObservationRegistry", null);
@@ -82,7 +82,7 @@ public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>>
<C> void setSharedObject(Class<C> sharedType, C object);
/**
* Gets a shared Object. Note that object hierarchies are not considered.
* Gets a shared Object. Note that object heirarchies are not considered.
* @param sharedType the type of the shared Object
* @return the shared Object or null if it is not found
*/
@@ -133,7 +133,7 @@ final class FilterOrderRegistration {
/**
* Register a {@link Filter} with its specific position. If the {@link Filter} was
* already registered before, the position previously defined is not going to be
* overridden
* overriden
* @param filter the {@link Filter} to register
* @param position the position to associate with the {@link Filter}
*/
@@ -2052,6 +2052,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* http
* // ...
* .webAuthn((webAuthn) -&gt; webAuthn
* .rpName("Spring Security Relying Party")
* .rpId("example.com")
* .allowedOrigins("https://example.com")
* );
@@ -305,7 +305,7 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>>
}
/**
* Gets the logoutSuccessUrl or null if a
* Gets the logoutSuccesUrl or null if a
* {@link #logoutSuccessHandler(LogoutSuccessHandler)} was configured.
* @return the logoutSuccessUrl
*/
@@ -146,7 +146,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
/**
* This should not use RequestAttributeSecurityContextRepository since that is
* stateless and session management is about state management.
* stateless and sesison management is about state management.
*/
private SecurityContextRepository sessionManagementSecurityContextRepository = new HttpSessionSecurityContextRepository();
@@ -177,7 +177,6 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
WebAuthnAuthenticationFilter webAuthnAuthnFilter = new WebAuthnAuthenticationFilter();
webAuthnAuthnFilter.setAuthenticationManager(
new ProviderManager(new WebAuthnAuthenticationProvider(rpOperations, userDetailsService)));
webAuthnAuthnFilter = postProcess(webAuthnAuthnFilter);
WebAuthnRegistrationFilter webAuthnRegistrationFilter = new WebAuthnRegistrationFilter(userCredentials,
rpOperations);
PublicKeyCredentialCreationOptionsFilter creationOptionsFilter = new PublicKeyCredentialCreationOptionsFilter(
@@ -257,10 +256,9 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
PublicKeyCredentialUserEntityRepository userEntities, UserCredentialRepository userCredentials) {
Optional<WebAuthnRelyingPartyOperations> webauthnOperationsBean = getBeanOrNull(
WebAuthnRelyingPartyOperations.class);
String rpName = (this.rpName != null) ? this.rpName : this.rpId;
return webauthnOperationsBean
.orElseGet(() -> new Webauthn4JRelyingPartyOperations(userEntities, userCredentials,
PublicKeyCredentialRpEntity.builder().id(this.rpId).name(rpName).build(), this.allowedOrigins));
return webauthnOperationsBean.orElseGet(() -> new Webauthn4JRelyingPartyOperations(userEntities,
userCredentials, PublicKeyCredentialRpEntity.builder().id(this.rpId).name(this.rpName).build(),
this.allowedOrigins));
}
}
@@ -16,12 +16,10 @@
package org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import jakarta.servlet.Filter;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpMethod;
@@ -38,12 +36,10 @@ import org.springframework.security.oauth2.server.authorization.authentication.O
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationValidator;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeRequestAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationConsentAuthenticationConverter;
import org.springframework.security.web.access.intercept.AuthorizationFilter;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
@@ -54,7 +50,6 @@ import org.springframework.security.web.servlet.util.matcher.PathPatternRequestM
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
@@ -88,8 +83,6 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
private Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authorizationCodeRequestAuthenticationValidator;
private Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authorizationCodeRequestAuthenticationValidatorComposite;
private SessionAuthenticationStrategy sessionAuthenticationStrategy;
/**
@@ -255,16 +248,8 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
authenticationProviders.addAll(0, this.authenticationProviders);
}
this.authenticationProvidersConsumer.accept(authenticationProviders);
authenticationProviders.forEach((authenticationProvider) -> {
httpSecurity.authenticationProvider(postProcess(authenticationProvider));
if (authenticationProvider instanceof OAuth2AuthorizationCodeRequestAuthenticationProvider) {
Method method = ReflectionUtils.findMethod(OAuth2AuthorizationCodeRequestAuthenticationProvider.class,
"getAuthenticationValidatorComposite");
ReflectionUtils.makeAccessible(method);
this.authorizationCodeRequestAuthenticationValidatorComposite = (Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext>) ReflectionUtils
.invokeMethod(method, authenticationProvider);
}
});
authenticationProviders.forEach(
(authenticationProvider) -> httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
}
@Override
@@ -297,18 +282,7 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
if (this.sessionAuthenticationStrategy != null) {
authorizationEndpointFilter.setSessionAuthenticationStrategy(this.sessionAuthenticationStrategy);
}
httpSecurity.addFilterAfter(postProcess(authorizationEndpointFilter), AuthorizationFilter.class);
// Create and add
// OAuth2AuthorizationEndpointFilter.OAuth2AuthorizationCodeRequestValidatingFilter
Method method = ReflectionUtils.findMethod(OAuth2AuthorizationEndpointFilter.class,
"createAuthorizationCodeRequestValidatingFilter", RegisteredClientRepository.class, Consumer.class);
ReflectionUtils.makeAccessible(method);
RegisteredClientRepository registeredClientRepository = OAuth2ConfigurerUtils
.getRegisteredClientRepository(httpSecurity);
Filter authorizationCodeRequestValidatingFilter = (Filter) ReflectionUtils.invokeMethod(method,
authorizationEndpointFilter, registeredClientRepository,
this.authorizationCodeRequestAuthenticationValidatorComposite);
httpSecurity.addFilterBefore(postProcess(authorizationCodeRequestValidatingFilter),
httpSecurity.addFilterBefore(postProcess(authorizationEndpointFilter),
AbstractPreAuthenticatedProcessingFilter.class);
}
@@ -16,9 +16,14 @@
package org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;
import java.util.Map;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -40,6 +45,7 @@ import org.springframework.security.oauth2.server.authorization.token.OAuth2Toke
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Utility methods for the OAuth 2.0 Configurers.
@@ -201,16 +207,41 @@ final class OAuth2ConfigurerUtils {
}
static <T> T getBean(HttpSecurity httpSecurity, Class<T> type) {
return httpSecurity.getSharedObject(ApplicationContext.class).getBeanProvider(type).getObject();
return httpSecurity.getSharedObject(ApplicationContext.class).getBean(type);
}
@SuppressWarnings("unchecked")
static <T> T getBean(HttpSecurity httpSecurity, ResolvableType type) {
ApplicationContext context = httpSecurity.getSharedObject(ApplicationContext.class);
String[] names = context.getBeanNamesForType(type);
if (names.length == 1) {
return (T) context.getBean(names[0]);
}
if (names.length > 1) {
throw new NoUniqueBeanDefinitionException(type, names);
}
throw new NoSuchBeanDefinitionException(type);
}
static <T> T getOptionalBean(HttpSecurity httpSecurity, Class<T> type) {
return httpSecurity.getSharedObject(ApplicationContext.class).getBeanProvider(type).getIfUnique();
Map<String, T> beansMap = BeanFactoryUtils
.beansOfTypeIncludingAncestors(httpSecurity.getSharedObject(ApplicationContext.class), type);
if (beansMap.size() > 1) {
throw new NoUniqueBeanDefinitionException(type, beansMap.size(),
"Expected single matching bean of type '" + type.getName() + "' but found " + beansMap.size() + ": "
+ StringUtils.collectionToCommaDelimitedString(beansMap.keySet()));
}
return (!beansMap.isEmpty() ? beansMap.values().iterator().next() : null);
}
@SuppressWarnings("unchecked")
static <T> T getOptionalBean(HttpSecurity httpSecurity, ResolvableType type) {
return (T) httpSecurity.getSharedObject(ApplicationContext.class).getBeanProvider(type).getIfUnique();
ApplicationContext context = httpSecurity.getSharedObject(ApplicationContext.class);
String[] names = context.getBeanNamesForType(type);
if (names.length > 1) {
throw new NoUniqueBeanDefinitionException(type, names);
}
return (names.length == 1) ? (T) context.getBean(names[0]) : null;
}
}
@@ -95,7 +95,7 @@ public class Saml2MetadataConfigurer<H extends HttpSecurityBuilder<H>>
* If there is no {@code registrationId} and your
* {@link RelyingPartyRegistrationRepository} is {code Iterable}, the metadata
* endpoint will try and show all relying parties' metadata in a single
* {@code <md:EntitiesDescriptor} element.
* {@code <md:EntitiesDecriptor} element.
*
* <p>
* If you need a more sophisticated lookup strategy than these, use
@@ -167,7 +167,7 @@ class ServerHttpSecurityConfiguration {
}
/**
* Applies all {@code Customizer<ServerHttpSecurity>} Beans to
* Applies all {@code Custmizer<ServerHttpSecurity>} Beans to
* {@link ServerHttpSecurity}.
* @param context the {@link ApplicationContext}
* @param http the {@link ServerHttpSecurity}
@@ -538,7 +538,7 @@ final class AuthenticationConfigBuilder {
}
injectAuthenticationDetailsSource(x509Elt, filterBuilder);
filter = (RootBeanDefinition) filterBuilder.getBeanDefinition();
createPreauthEntryPoint(x509Elt);
createPrauthEntryPoint(x509Elt);
createX509Provider();
}
this.x509Filter = filter;
@@ -562,7 +562,7 @@ final class AuthenticationConfigBuilder {
this.x509ProviderRef = new RuntimeBeanReference(this.pc.getReaderContext().registerWithGeneratedName(provider));
}
private void createPreauthEntryPoint(Element source) {
private void createPrauthEntryPoint(Element source) {
if (this.preAuthEntryPoint == null) {
this.preAuthEntryPoint = new RootBeanDefinition(Http403ForbiddenEntryPoint.class);
this.preAuthEntryPoint.setSource(this.pc.extractSource(source));
@@ -595,7 +595,7 @@ final class AuthenticationConfigBuilder {
adsBldr.addPropertyValue("mappableRolesRetriever", mappableRolesRetriever);
filterBuilder.addPropertyValue("authenticationDetailsSource", adsBldr.getBeanDefinition());
filter = (RootBeanDefinition) filterBuilder.getBeanDefinition();
createPreauthEntryPoint(jeeElt);
createPrauthEntryPoint(jeeElt);
createJeeProvider();
}
this.jeeFilter = filter;
@@ -165,20 +165,20 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
}
private RootBeanDefinition getRootBeanDefinition(String mode) {
if (isUnboundIdEnabled(mode)) {
if (isUnboundidEnabled(mode)) {
return new RootBeanDefinition(UNBOUNDID_CONTAINER_CLASSNAME, null, null);
}
throw new IllegalStateException("Embedded LDAP server is not provided");
}
private String resolveBeanId(String mode) {
if (isUnboundIdEnabled(mode)) {
if (isUnboundidEnabled(mode)) {
return BeanIds.EMBEDDED_UNBOUNDID;
}
return null;
}
private boolean isUnboundIdEnabled(String mode) {
private boolean isUnboundidEnabled(String mode) {
return "unboundid".equals(mode) || unboundIdPresent;
}
@@ -1340,7 +1340,7 @@ public class AuthorizeHttpRequestsConfigurerTests {
static class ServletPathConfig {
@Bean
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
PathPatternRequestMatcherBuilderFactoryBean requesMatcherBuilder() {
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
bean.setBasePath("/spring");
return bean;
@@ -19,13 +19,10 @@ package org.springframework.security.config.annotation.web.configurers;
import java.nio.charset.StandardCharsets;
import java.util.List;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpOutputMessage;
@@ -45,7 +42,6 @@ import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.ui.DefaultResourcesFilter;
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
import org.springframework.security.web.webauthn.api.TestPublicKeyCredentialCreationOptions;
import org.springframework.security.web.webauthn.authentication.WebAuthnAuthenticationFilter;
import org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;
import org.springframework.security.web.webauthn.registration.HttpSessionPublicKeyCredentialCreationOptionsRepository;
import org.springframework.test.web.servlet.MockMvc;
@@ -56,8 +52,6 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.willAnswer;
import static org.mockito.Mockito.mock;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
@@ -94,14 +88,6 @@ public class WebAuthnConfigurerTests {
.andExpect(content().string(containsString("body {")));
}
// gh-18128
@Test
public void webAuthnAuthenticationFilterIsPostProcessed() throws Exception {
this.spring.register(DefaultWebauthnConfiguration.class, PostProcessorConfiguration.class).autowire();
PostProcessorConfiguration postProcess = this.spring.getContext().getBean(PostProcessorConfiguration.class);
assertThat(postProcess.webauthnFilter).isNotNull();
}
@Test
public void webauthnWhenNoFormLoginAndDefaultRegistrationPageConfiguredThenServesJavascript() throws Exception {
this.spring.register(NoFormLoginAndDefaultRegistrationPageConfiguration.class).autowire();
@@ -141,42 +127,6 @@ public class WebAuthnConfigurerTests {
.hasSize(1);
}
@Test
void webauthnWhenConfiguredDefaultsRpNameToRpId() throws Exception {
ObjectMapper mapper = new ObjectMapper();
this.spring.register(DefaultWebauthnConfiguration.class).autowire();
String response = this.mvc
.perform(post("/webauthn/register/options").with(csrf())
.with(authentication(new TestingAuthenticationToken("test", "ignored", "ROLE_user"))))
.andExpect(status().is2xxSuccessful())
.andReturn()
.getResponse()
.getContentAsString();
JsonNode parsedResponse = mapper.readTree(response);
assertThat(parsedResponse.get("rp").get("id").asText()).isEqualTo("example.com");
assertThat(parsedResponse.get("rp").get("name").asText()).isEqualTo("example.com");
}
@Test
void webauthnWhenRpNameConfiguredUsesRpName() throws Exception {
ObjectMapper mapper = new ObjectMapper();
this.spring.register(CustomRpNameWebauthnConfiguration.class).autowire();
String response = this.mvc
.perform(post("/webauthn/register/options").with(csrf())
.with(authentication(new TestingAuthenticationToken("test", "ignored", "ROLE_user"))))
.andExpect(status().is2xxSuccessful())
.andReturn()
.getResponse()
.getContentAsString();
JsonNode parsedResponse = mapper.readTree(response);
assertThat(parsedResponse.get("rp").get("id").asText()).isEqualTo("example.com");
assertThat(parsedResponse.get("rp").get("name").asText()).isEqualTo("Test RP Name");
}
@Test
public void webauthnWhenConfiguredAndFormLoginThenDoesServesJavascript() throws Exception {
this.spring.register(FormLoginAndNoDefaultRegistrationPageConfiguration.class).autowire();
@@ -339,26 +289,6 @@ public class WebAuthnConfigurerTests {
}
@Configuration(proxyBeanMethods = false)
static class PostProcessorConfiguration {
WebAuthnAuthenticationFilter webauthnFilter;
@Bean
BeanPostProcessor beanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof WebAuthnAuthenticationFilter filter) {
PostProcessorConfiguration.this.webauthnFilter = filter;
}
return bean;
}
};
}
}
@Configuration
@EnableWebSecurity
static class DefaultWebauthnConfiguration {
@@ -374,7 +304,8 @@ public class WebAuthnConfigurerTests {
http
.formLogin(Customizer.withDefaults())
.webAuthn((authn) -> authn
.rpId("example.com")
.rpId("spring.io")
.rpName("spring")
);
// @formatter:on
return http.build();
@@ -382,24 +313,6 @@ public class WebAuthnConfigurerTests {
}
@Configuration
@EnableWebSecurity
static class CustomRpNameWebauthnConfiguration {
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager();
}
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.formLogin(Customizer.withDefaults())
.webAuthn((webauthn) -> webauthn.rpId("example.com").rpName("Test RP Name"))
.build();
}
}
@Configuration
@EnableWebSecurity
static class NoFormLoginAndDefaultRegistrationPageConfiguration {
@@ -307,8 +307,8 @@ public class OAuth2AuthorizationCodeGrantTests {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
this.mvc
.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)
.queryParams(getAuthorizationRequestParameters(registeredClient)))
.perform(
get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).params(getAuthorizationRequestParameters(registeredClient)))
.andExpect(status().isBadRequest())
.andReturn();
}
@@ -851,31 +851,21 @@ public class OAuth2AuthorizationCodeGrantTests {
this.spring.register(AuthorizationServerConfigurationCustomAuthorizationEndpoint.class).autowire();
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
this.registeredClientRepository.save(registeredClient);
TestingAuthenticationToken principal = new TestingAuthenticationToken("principalName", "password");
Map<String, Object> additionalParameters = new HashMap<>();
additionalParameters.put(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE);
additionalParameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, "S256");
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(
"https://provider.com/oauth2/authorize", registeredClient.getClientId(), principal,
registeredClient.getRedirectUris().iterator().next(), STATE_URL_UNENCODED, registeredClient.getScopes(),
additionalParameters);
OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode("code", Instant.now(),
Instant.now().plus(5, ChronoUnit.MINUTES));
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken(
"https://provider.com/oauth2/authorize", registeredClient.getClientId(), principal, authorizationCode,
registeredClient.getRedirectUris().iterator().next(), STATE_URL_UNENCODED,
registeredClient.getScopes());
given(authorizationRequestConverter.convert(any())).willReturn(authorizationCodeRequestAuthentication);
given(authorizationRequestConverter.convert(any())).willReturn(authorizationCodeRequestAuthenticationResult);
given(authorizationRequestAuthenticationProvider
.supports(eq(OAuth2AuthorizationCodeRequestAuthenticationToken.class))).willReturn(true);
given(authorizationRequestAuthenticationProvider.authenticate(any()))
.willReturn(authorizationCodeRequestAuthenticationResult);
this.mvc
.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)
.queryParams(getAuthorizationRequestParameters(registeredClient))
.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).params(getAuthorizationRequestParameters(registeredClient))
.with(user("user")))
.andExpect(status().isOk());
@@ -890,7 +880,8 @@ public class OAuth2AuthorizationCodeGrantTests {
|| converter instanceof OAuth2AuthorizationCodeRequestAuthenticationConverter
|| converter instanceof OAuth2AuthorizationConsentAuthenticationConverter);
verify(authorizationRequestAuthenticationProvider).authenticate(eq(authorizationCodeRequestAuthentication));
verify(authorizationRequestAuthenticationProvider)
.authenticate(eq(authorizationCodeRequestAuthenticationResult));
@SuppressWarnings("unchecked")
ArgumentCaptor<List<AuthenticationProvider>> authenticationProvidersCaptor = ArgumentCaptor
@@ -45,7 +45,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpHeaders;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -159,8 +158,6 @@ public class OAuth2ClientCredentialsGrantTests {
private static AuthenticationFailureHandler authenticationFailureHandler;
private static PasswordEncoder passwordEncoder;
public final SpringTestContext spring = new SpringTestContext(this);
@Autowired
@@ -186,9 +183,6 @@ public class OAuth2ClientCredentialsGrantTests {
authenticationProvidersConsumer = mock(Consumer.class);
authenticationSuccessHandler = mock(AuthenticationSuccessHandler.class);
authenticationFailureHandler = mock(AuthenticationFailureHandler.class);
passwordEncoder = mock(PasswordEncoder.class);
given(passwordEncoder.matches(any(), any())).willReturn(true);
given(passwordEncoder.upgradeEncoding(any())).willReturn(false);
db = new EmbeddedDatabaseBuilder().generateUniqueName(true)
.setType(EmbeddedDatabaseType.HSQL)
.setScriptEncoding("UTF-8")
@@ -502,26 +496,6 @@ public class OAuth2ClientCredentialsGrantTests {
.andExpect(jsonPath("$.token_type").value(OAuth2AccessToken.TokenType.DPOP.getValue()));
}
@Test
public void requestWhenTokenRequestWithMultiplePasswordEncodersThenPrimaryPasswordEncoderUsed() throws Exception {
this.spring.register(AuthorizationServerConfigurationWithMultiplePasswordEncoders.class).autowire();
RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();
this.registeredClientRepository.save(registeredClient);
this.mvc
.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.param(OAuth2ParameterNames.SCOPE, "scope1 scope2")
.header(HttpHeaders.AUTHORIZATION,
"Basic " + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))
.andExpect(status().isOk())
.andExpect(jsonPath("$.access_token").isNotEmpty())
.andExpect(jsonPath("$.scope").value("scope1 scope2"));
verify(passwordEncoder).matches(any(), any());
}
private static String generateDPoPProof(String tokenEndpointUri) {
// @formatter:off
Map<String, Object> publicJwk = TestJwks.DEFAULT_EC_JWK
@@ -684,16 +658,4 @@ public class OAuth2ClientCredentialsGrantTests {
}
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
static class AuthorizationServerConfigurationWithMultiplePasswordEncoders extends AuthorizationServerConfiguration {
@Primary
@Bean
PasswordEncoder primaryPasswordEncoder() {
return passwordEncoder;
}
}
}
@@ -275,7 +275,7 @@ class ServerJwtDslTests {
}
class NullConverter: Converter<Jwt, Mono<AbstractAuthenticationToken>> {
override fun convert(source: Jwt): Mono<AbstractAuthenticationToken> {
override fun convert(source: Jwt): Mono<AbstractAuthenticationToken>? {
return Mono.empty()
}
@@ -100,7 +100,7 @@ public interface SecurityExpressionOperations {
boolean isAnonymous();
/**
* Determines if the {@link #getAuthentication()} is authenticated
* Determines ifthe {@link #getAuthentication()} is authenticated
* @return true if the {@link #getAuthentication()} is authenticated, else false
*/
boolean isAuthenticated();
@@ -75,7 +75,7 @@ public class DefaultSecurityParameterNameDiscoverer extends PrioritizedParameter
annotationClassesToUse.add(DATA_PARAM_CLASSNAME);
}
addDiscoverer(new AnnotationParameterNameDiscoverer(annotationClassesToUse));
addDiscoverer(DefaultParameterNameDiscoverer.getSharedInstance());
addDiscoverer(new DefaultParameterNameDiscoverer());
}
}
@@ -16,7 +16,6 @@
package org.springframework.security.crypto.bcrypt;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import org.junit.jupiter.api.BeforeEach;
@@ -26,7 +25,6 @@ import org.springframework.security.crypto.password.AbstractPasswordEncoderValid
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatNoException;
/**
* @author Dave Syer
@@ -238,23 +236,4 @@ public class BCryptPasswordEncoderTests extends AbstractPasswordEncoderValidatio
assertThat(getEncoder().matches(password73chars, encodedPassword73chars)).isTrue();
}
/**
* Fixes gh-18133
* @author StringManolo
*/
@Test
void passwordLargerThan72BytesShouldThrowIllegalArgumentException() {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String singleByteChars = "a".repeat(68);
String password72Bytes = singleByteChars + "😀";
assertThat(password72Bytes.length()).isEqualTo(70);
assertThat(password72Bytes.getBytes(StandardCharsets.UTF_8).length).isEqualTo(72);
assertThatNoException().isThrownBy(() -> encoder.encode(password72Bytes));
String singleByteCharsTooLong = "a".repeat(69);
String password73Bytes = singleByteCharsTooLong + "😀";
assertThat(password73Bytes.getBytes(StandardCharsets.UTF_8).length).isEqualTo(73);
assertThatIllegalArgumentException().isThrownBy(() -> encoder.encode(password73Bytes))
.withMessageContaining("password cannot be more than 72 bytes");
}
}
+1 -1
View File
@@ -31,7 +31,7 @@ urls:
redirect_facility: httpd
ui:
bundle:
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.25/ui-bundle.zip
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.18/ui-bundle.zip
snapshot: true
runtime:
log:
-1
View File
@@ -90,7 +90,6 @@
**** xref:servlet/oauth2/resource-server/multitenancy.adoc[Multitenancy]
**** xref:servlet/oauth2/resource-server/bearer-tokens.adoc[Bearer Tokens]
**** xref:servlet/oauth2/resource-server/dpop-tokens.adoc[DPoP-bound Access Tokens]
**** xref:servlet/oauth2/resource-server/protected-resource-metadata.adoc[Protected Resource Metadata]
*** xref:servlet/oauth2/authorization-server/index.adoc[OAuth2 Authorization Server]
**** xref:servlet/oauth2/authorization-server/getting-started.adoc[Getting Started]
**** xref:servlet/oauth2/authorization-server/configuration-model.adoc[Configuration Model]
@@ -1,3 +1,4 @@
[[webflux-cors]]
= CORS
@@ -74,11 +75,3 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
----
======
[WARNING]
====
CORS is a browser-based security feature.
By disabling CORS in Spring Security, you are not removing CORS protection from your browser.
Instead, you are removing CORS support from Spring Security, and users will not be able to interact with your Spring backend from a cross-origin browser application.
To fix CORS errors in your application, you must enable CORS support, and provide an appropriate configuration source.
====
@@ -77,7 +77,7 @@ Public Clients are supported using https://tools.ietf.org/html/rfc7636[Proof Key
If the client is running in an untrusted environment (eg. native application or web browser-based application) and therefore incapable of maintaining the confidentiality of it's credentials, PKCE will automatically be used when the following conditions are true:
. `client-secret` is omitted (or empty)
. `client-authentication-method` is set to `none` (`ClientAuthenticationMethod.NONE`)
. `client-authentication-method` is set to "none" (`ClientAuthenticationMethod.NONE`)
or
@@ -85,7 +85,7 @@ or
[TIP]
====
If the OAuth 2.0 Provider doesn't support PKCE for https://tools.ietf.org/html/rfc6749#section-2.1[Confidential Clients], you need to disable it by setting `ClientRegistration.clientSettings.requireProofKey` to `false`.
If the OAuth 2.0 Provider supports PKCE for https://tools.ietf.org/html/rfc6749#section-2.1[Confidential Clients], you may (optionally) configure it using `DefaultServerOAuth2AuthorizationRequestResolver.setAuthorizationRequestCustomizer(OAuth2AuthorizationRequestCustomizers.withPkce())`.
====
[[oauth2-client-authorization-code-redirect-uri]]
@@ -68,7 +68,7 @@ The name may be used in certain scenarios, such as when displaying the name of t
<15> `(userInfoEndpoint)authenticationMethod`: The authentication method used when sending the access token to the UserInfo Endpoint.
The supported values are *header*, *form* and *query*.
<16> `userNameAttributeName`: The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.
<17> [[oauth2Client-client-registration-requireProofKey]]`requireProofKey`: If `true` or if `clientAuthenticationMethod` is `none`, then PKCE will be enabled. Defaults to `true` for `authorization_code` grant type and `false` for other grant types.
<17> [[oauth2Client-client-registration-requireProofKey]]`requireProofKey`: If `true` or if `authorizationGrantType` is `none`, then PKCE will be enabled by default.
A `ClientRegistration` can be initially configured using discovery of an OpenID Connect Provider's https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig[Configuration endpoint] or an Authorization Server's https://tools.ietf.org/html/rfc8414#section-3[Metadata endpoint].
@@ -414,7 +414,7 @@ If you build your project with Maven, adding the appropriate Spring Security mod
Any that are marked as "`optional`" in the Spring Security `pom.xml` files have to be added to your own `pom.xml` file if you need them.
[[appendix-faq-unboundid-deps]]
=== What dependencies are needed to run an embedded UnboundID LDAP server?
=== What dependences are needed to run an embedded UnboundID LDAP server?
You need to add the following dependency to your project:
@@ -137,8 +137,7 @@ fun method(authentication: Authentication?): String {
will always return "not anonymous", even for anonymous requests.
The reason is that Spring MVC resolves the parameter using `HttpServletRequest#getPrincipal`, which is `null` when the request is anonymous.
If you'd like to obtain the `Authentication` in anonymous requests, use
xref:servlet/integrations/mvc.adoc#mvc-current-security-context[`@CurrentSecurityContext`] instead:
If you'd like to obtain the `Authentication` in anonymous requests, use `@CurrentSecurityContext` instead:
.Use CurrentSecurityContext for Anonymous requests
[tabs]
@@ -97,11 +97,7 @@ val authorities = authentication.authorities
----
======
In Spring MVC, you can resolve the current principal with
xref:servlet/integrations/mvc.adoc#mvc-authentication-principal[`@AuthenticationPrincipal`]
and the full `SecurityContext` with
xref:servlet/integrations/mvc.adoc#mvc-current-security-context[`@CurrentSecurityContext`].
For Servlet API access, use `HttpServletRequest#getRemoteUser`.
// FIXME: Add links to and relevant description of HttpServletRequest.getRemoteUser() and @CurrentSecurityContext @AuthenticationPrincipal
By default, `SecurityContextHolder` uses a `ThreadLocal` to store these details, which means that the `SecurityContext` is always available to methods in the same thread, even if the `SecurityContext` is not explicitly passed around as an argument to those methods.
Using a `ThreadLocal` in this way is quite safe if you take care to clear the thread after the present principal's request is processed.
@@ -12,7 +12,7 @@ OWASP places factors into the following categories:
== `FactorGrantedAuthority`
At the time of authentication, Spring Security's authentication mechanisms add a javadoc:org.springframework.security.core.authority.FactorGrantedAuthority[].
For example, when a user authenticates using a password a `FactorGrantedAuthority` with the `authority` of `FactorGrantedAuthority.PASSWORD_AUTHORITY` is automatically added to the `Authentication`.
For example, when a user authenticates using a password a `FactorGrantedAuthority` with the `authority` of `FactorGrantedAuthority.PASSWORD_AUTHORITY` is automatically added to the `Authentiation`.
In order to require MFA with Spring Security you must:
- Specify an authorization rule that requires multiple factors
@@ -51,7 +51,7 @@ include-code::./UseAuthorizationManagerFactoryConfiguration[tag=authorizationMan
[[selective-mfa]]
== Selectively Requiring MFA
We have demonstrated how to configure an entire application to require MFA by using xref:./mfa.adoc#emfa[``@EnableMultiFactorAuthentication``s] `authorities` property.
We have demonstrated how to configure an entire application to require MFA by using xref:./mfa.adoc#emfa[`@EnableMultiFactorAuthentication`]s `authorities` property.
However, there are times that an application only wants parts of the application to require MFA.
Consider the following requirements:
@@ -118,7 +118,7 @@ To enable the MFA rules globally, we can publish an `AuthorizationManagerFactory
include-code::./AdminMfaAuthorizationManagerConfiguration[tag=authorizationManagerFactory,indent=0]
<1> Inject the custom `AuthorizationManager` as the javadoc:org.springframework.security.authorization.DefaultAuthorizationManagerFactory#setAdditionalAuthorization(org.springframework.security.authorization.AuthorizationManager)[DefaultAuthorization.additionalAuthorization].
This instructs `DefaultAuthorizationManagerFactory` that any authorization rule should apply our custom `AuthorizationManager` along with any authorization requirements defined by the application (e.g. `hasRole("ADMIN")`).
This instructs `DefaultAuthorizationManagerFactory` that any authorization rule should apply our custom `AuthorizationManager` along with any authorization requirements defined by the application (e.g. `hasRole("ADMIN")).
<2> Publish `DefaultAuthorizationManagerFactory` as a Bean, so it is used globally
This should feel very similar to our previous example in xref:./mfa.adoc#authorization-manager-factory[].
@@ -153,11 +153,10 @@ Next we can define an `AuthorizationManagerFactory` that uses the `RequiredAutho
include-code::./RequiredAuthoritiesAuthorizationManagerConfiguration[tag=authorizationManagerFactory,indent=0]
<1> Inject the `RequiredAuthoritiesAuthorizationManager` as the javadoc:org.springframework.security.authorization.DefaultAuthorizationManagerFactory#setAdditionalAuthorization(org.springframework.security.authorization.AuthorizationManager)[DefaultAuthorization.additionalAuthorization].
This instructs `DefaultAuthorizationManagerFactory` that any authorization rule should apply `RequiredAuthoritiesAuthorizationManager` along with any authorization requirements defined by the application (e.g. `hasRole("ADMIN")`).
This instructs `DefaultAuthorizationManagerFactory` that any authorization rule should apply `RequiredAuthoritiesAuthorizationManager` along with any authorization requirements defined by the application (e.g. `hasRole("ADMIN")).
<2> Publish `DefaultAuthorizationManagerFactory` as a Bean, so it is used globally
We can now define our authorization rules which are combined with `RequiredAuthoritiesAuthorizationManager`.
include-code::./RequiredAuthoritiesAuthorizationManagerConfiguration[tag=httpSecurity,indent=0]
<1> URLs that begin with `/admin/**` require `ROLE_ADMIN`.
If the username is `admin`, then `FACTOR_OTT` and `FACTOR_PASSWORD` are also required.
@@ -168,7 +167,7 @@ Our example uses an in memory mapping of usernames to the additional required au
For more dynamic use cases that can be determined by the username, a custom implementation of javadoc:org.springframework.security.authorization.RequiredAuthoritiesRepository[] can be created.
Possible examples would be looking up if a user has enabled MFA in an explicit setting, determining if a user has registered a passkey, etc.
For cases that need to determine MFA based upon the `Authentication`, a custom `AuthorizationManger` can be used as demonstrated in xref:./mfa.adoc#programmatic-mfa[].
For cases that need to determine MFA based upon the `Authentication`, a custom `AuthorizationManger` can be used as demonstrated in xref:./mfa.adoc#programmatic-mfa[]
[[hasallauthorities]]
@@ -197,7 +196,7 @@ Can you imagine what it would be like to declare hundreds of rules like this?
What's more that it becomes difficult to express more complicated authorization rules.
For example, how would you require two factors and either `ROLE_ADMIN` or `ROLE_USER`?
The answer to these questions, as we have already seen, is to use xref:./mfa.adoc#emfa[]
The answer to these questions, as we have already seen, is to use xref:./mfa.adoc#egmfa[]
[[re-authentication]]
== Re-authentication
@@ -212,7 +211,7 @@ By default, this application has two authentication mechanisms that it allows, m
If there is a set of endpoints that require a specific factor, we can specify that in `authorizeHttpRequests` as follows:
include-code::./RequireOttConfiguration[tag=httpSecurity,indent=0]
<1> States that all `/profile/**` endpoints require one-time-token login to be authorized
<1> - States that all `/profile/**` endpoints require one-time-token login to be authorized
Given the above configuration, users can log in with any mechanism that you support.
And, if they want to visit the profile page, then Spring Security will redirect them to the One-Time-Token Login page to obtain it.
@@ -39,7 +39,7 @@ Gradle::
+
[source,groovy,role="secondary",subs="verbatim,attributes"]
----
dependencies {
depenendencies {
implementation "org.springframework.security:spring-security-web"
implementation "com.webauthn4j:webauthn4j-core:{webauthn4j-core-version}"
}
@@ -65,6 +65,7 @@ SecurityFilterChain filterChain(HttpSecurity http) {
// ...
.formLogin(withDefaults())
.webAuthn((webAuthn) -> webAuthn
.rpName("Spring Security Relying Party")
.rpId("example.com")
.allowedOrigins("https://example.com")
// optional properties
@@ -95,6 +96,7 @@ open fun filterChain(http: HttpSecurity): SecurityFilterChain {
// ...
http {
webAuthn {
rpName = "Spring Security Relying Party"
rpId = "example.com"
allowedOrigins = setOf("https://example.com")
// optional properties
@@ -24,7 +24,7 @@ The `RequestCache` is typically a `NullRequestCache` that does not save the requ
[NOTE]
====
The default HTTP Basic Auth Provider will suppress both Response body and `WWW-Authenticate` header in the 401 response when the request was made with a `X-Requested-With: XMLHttpRequest` header.
The default HTTP Basic Auth Provider will suppress both Response body and `WWW-Authenticate` header in the 401 response when the request was made with a `X-Requested-By: XMLHttpRequest` header.
This allows frontends to implement their own authentication code, instead of triggering the browser login dialog.
To override, implement your own javadoc:org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint[].
====
@@ -38,7 +38,7 @@ Gradle::
+
[source,groovy,role="secondary"]
----
dependencies {
depenendencies {
implementation "org.springframework.boot:spring-boot-starter-data-ldap"
implementation "org.springframework.security:spring-security-ldap"
}
@@ -150,7 +150,7 @@ Gradle::
+
[source,groovy,role="secondary",subs="verbatim,attributes"]
----
dependencies {
depenendencies {
runtimeOnly "com.unboundid:unboundid-ldapsdk:{unboundid-ldapsdk-version}"
}
----
@@ -107,7 +107,7 @@ default void verify(Supplier<Authentication> authentication, Object secureObject
}
----
The ``AuthorizationManager``'s `authorize` method is passed all the relevant information it needs in order to make an authorization decision.
The ``AuthorizationManager``'s `check` method is passed all the relevant information it needs in order to make an authorization decision.
In particular, passing the secure `Object` enables those arguments contained in the actual secure object invocation to be inspected.
For example, let's assume the secure object was a `MethodInvocation`.
It would be easy to query the `MethodInvocation` for any `Customer` argument, and then implement some sort of security logic in the `AuthorizationManager` to ensure the principal is permitted to operate on that customer.
@@ -639,7 +639,7 @@ This is because Spring Security requires all URIs to be absolute (minus the cont
[TIP]
=====
There are several other components that create request matchers for you like {spring-boot-api-url}org/springframework/boot/security/autoconfigure/web/servlet/PathRequest.html[`PathRequest#toStaticResources#atCommonLocations`]
There are several other components that create request matchers for you like {spring-boot-api-url}org/springframework/boot/autoconfigure/security/servlet/PathRequest.html[`PathRequest#toStaticResources#atCommonLocations`]
=====
[[match-by-custom]]
@@ -118,7 +118,7 @@ A given invocation to `MyCustomerService#readCustomer` may look something like t
image::{figures}/methodsecurity.png[]
1. Spring AOP invokes its proxy method for `readCustomer`. Among the proxy's other advisors, it invokes an javadoc:org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor[] that matches <<annotation-method-pointcuts,the `@PreAuthorize` pointcut>>
2. The interceptor invokes javadoc:org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager[`PreAuthorizeAuthorizationManager#authorize`]
2. The interceptor invokes javadoc:org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager[`PreAuthorizeAuthorizationManager#check`]
3. The authorization manager uses a `MethodSecurityExpressionHandler` to parse the annotation's <<authorization-expressions,SpEL expression>> and constructs a corresponding `EvaluationContext` from a `MethodSecurityExpressionRoot` containing xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[a `Supplier<Authentication>`] and `MethodInvocation`.
4. The interceptor uses this context to evaluate the expression; specifically, it reads xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[the `Authentication`] from the `Supplier` and checks whether it has `permission:read` in its collection of xref:servlet/authorization/architecture.adoc#authz-authorities[authorities]
5. If the evaluation passes, then Spring AOP proceeds to invoke the method.
@@ -493,14 +493,14 @@ public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurit
private boolean flag;
@Override
public void init(HttpSecurity http) {
public void init(HttpSecurity http) throws Exception {
// any method that adds another configurer
// must be done in the init method
http.csrf(csrf -> csrf.disable());
http.csrf().disable();
}
@Override
public void configure(HttpSecurity http) {
public void configure(HttpSecurity http) throws Exception {
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
// here we lookup from the ApplicationContext. You can also just create a new instance.
@@ -399,7 +399,7 @@ Second, each xref:#httpsecuritydsl-bean[HttpSecurityDsl.() -> Unit Beans] is app
This means that if there are multiple `HttpSecurity.() -> Unit` Beans, the https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/annotation/Order.html[@Order] annotation can be added to the Bean definitions to control the ordering.
Next, every xref:#top-level-dsl-bean[Top Level Security Dsl Beans] type is looked up and each is is applied using `ObjectProvider#orderedStream()`.
If there is are different types of top level security Beans (.e.g. `HeadersDsl.() -> Unit` and `HttpsRedirectDsl.() -> Unit`), then the order that each Dsl type is invoked is undefined.
If there is are differt types of top level security Beans (.e.g. `HeadersDsl.() -> Unit` and `HttpsRedirectDsl.() -> Unit`), then the order that each Dsl type is invoked is undefined.
However, the order that each instance of of the same top level security Bean type is defined by `ObjectProvider#orderedStream()` and can be controlled using `@Order` on the Bean the definitions.
Finally, the `HttpSecurityDsl` Bean is injected as a Bean.
@@ -118,7 +118,7 @@ The default arrangement of Spring Boot and Spring Security affords the following
* Publishes xref:servlet/authentication/events.adoc[authentication success and failure events]
It can be helpful to understand how Spring Boot is coordinating with Spring Security to achieve this.
Taking a look at {spring-boot-api-url}org/springframework/boot/security/autoconfigure/SecurityAutoConfiguration.html[Boot's security auto configuration], it does the following (simplified for illustration):
Taking a look at {spring-boot-api-url}org/springframework/boot/autoconfigure/security/servlet/SecurityAutoConfiguration.html[Boot's security auto configuration], it does the following (simplified for illustration):
.Spring Boot Security Auto Configuration
[source,java]
@@ -183,11 +183,3 @@ fun corsConfigurationSource(): UrlBasedCorsConfigurationSource {
}
----
======
[WARNING]
====
CORS is a browser-based security feature.
By disabling CORS in Spring Security with `.cors(CorsConfigurer::disable)`, you are not removing CORS protection from your browser.
Instead, you are removing CORS support from Spring Security, and users will not be able to interact with your Spring backend from a cross-origin browser application.
To fix CORS errors in your application, you must enable CORS support, and provide an appropriate configuration source.
====
@@ -370,7 +370,7 @@ Java::
----
@Bean
public AnnotationTemplateExpressionDefaults templateDefaults() {
return new AnnotationTemplateExpressionDefaults();
return new AnnotationTemplateExpressionDeafults();
}
----
@@ -380,7 +380,7 @@ Kotlin::
----
@Bean
fun templateDefaults(): AnnotationTemplateExpressionDefaults {
return AnnotationTemplateExpressionDefaults()
return AnnotationTemplateExpressionDeafults()
}
----
@@ -448,72 +448,6 @@ open fun findMessagesForUser(@CurrentUser("user_id") userId: String?): ModelAndV
----
======
[[mvc-current-security-context]]
== @CurrentSecurityContext
Spring Security provides `CurrentSecurityContextArgumentResolver`, which can automatically resolve the current `SecurityContext` for Spring MVC arguments.
By using `@EnableWebSecurity`, you automatically have this added to your Spring MVC configuration.
If you use XML-based configuration, you must add this yourself:
[source,xml]
----
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="org.springframework.security.web.method.annotation.CurrentSecurityContextArgumentResolver" />
</mvc:argument-resolvers>
</mvc:annotation-driven>
----
Once `CurrentSecurityContextArgumentResolver` is configured, you can access the `SecurityContext` directly:
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@GetMapping("/me")
public String me(@CurrentSecurityContext SecurityContext context) {
return context.getAuthentication().getName();
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@GetMapping("/me")
fun me(@CurrentSecurityContext context: SecurityContext): String {
return context.authentication.name
}
----
======
You can also use a SpEL expression that is rooted at the `SecurityContext`:
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@GetMapping("/me")
public String me(@CurrentSecurityContext(expression = "authentication") Authentication authentication) {
return authentication.getName();
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@GetMapping("/me")
fun me(@CurrentSecurityContext(expression = "authentication") authentication: Authentication): String {
return authentication.name
}
----
======
[[mvc-async]]
== Spring MVC Async Integration
@@ -108,34 +108,6 @@ spring:
require-authorization-consent: true
----
If you want to customize the default `HttpSecurity` configuration, you may override Spring Boot's auto-configuration with the following example:
[[oauth2AuthorizationServer-minimal-sample-gettingstarted]]
.SecurityConfig.java
[source,java]
----
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) {
http
.authorizeHttpRequests((authorize) ->
authorize
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults())
.oauth2AuthorizationServer((authorizationServer) ->
authorizationServer
.oidc(Customizer.withDefaults()) // Enable OpenID Connect 1.0
);
return http.build();
}
}
----
TIP: Beyond the Getting Started experience, most users will want to customize the default configuration. The xref:servlet/oauth2/authorization-server/getting-started.adoc#oauth2AuthorizationServer-defining-required-components[next section] demonstrates providing all of the necessary beans yourself.
[[oauth2AuthorizationServer-defining-required-components]]
@@ -77,7 +77,7 @@ spring:
Public Clients are supported by using https://tools.ietf.org/html/rfc7636[Proof Key for Code Exchange] (PKCE).
If the client is running in an untrusted environment (such as a native application or web browser-based application) and is therefore incapable of maintaining the confidentiality of its credentials, PKCE is automatically used when the following conditions are true:
. `client-secret` is omitted (or empty)
. `client-secret` is omitted (or empty) and
. `client-authentication-method` is set to `none` (`ClientAuthenticationMethod.NONE`)
or
@@ -87,7 +87,7 @@ or
[TIP]
====
If the OAuth 2.0 Provider doesn't support PKCE for https://tools.ietf.org/html/rfc6749#section-2.1[Confidential Clients], you need to disable it by setting `ClientRegistration.clientSettings.requireProofKey` to `false`.
If the OAuth 2.0 Provider supports PKCE for https://tools.ietf.org/html/rfc6749#section-2.1[Confidential Clients], you may (optionally) configure it using `DefaultOAuth2AuthorizationRequestResolver.setAuthorizationRequestCustomizer(OAuth2AuthorizationRequestCustomizers.withPkce())`.
====
[[oauth2-client-authorization-code-redirect-uri]]
@@ -69,7 +69,7 @@ This information is available only if the Spring Boot property `spring.security.
<15> `(userInfoEndpoint)authenticationMethod`: The authentication method used when sending the access token to the UserInfo Endpoint.
The supported values are *header*, *form*, and *query*.
<16> `userNameAttributeName`: The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.
<17> [[oauth2Client-client-registration-requireProofKey]]`requireProofKey`: If `true` or if `clientAuthenticationMethod` is `none`, then PKCE will be enabled. Defaults to `true` for `authorization_code` grant type and `false` for other grant types.
<17> [[oauth2Client-client-registration-requireProofKey]]`requireProofKey`: If `true` or if `clientAuthenticationMethod` is `none`, then PKCE will be enabled.
You can initially configure a `ClientRegistration` by using discovery of an OpenID Connect Provider's https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig[Configuration endpoint] or an Authorization Server's https://tools.ietf.org/html/rfc8414#section-3[Metadata endpoint].
@@ -41,7 +41,7 @@ See xref:servlet/oauth2/resource-server/index.adoc[OAuth 2.0 Resource Server] fo
To get started, add the `spring-security-oauth2-resource-server` dependency to your project.
When using Spring Boot, add the following starter:
.OAuth2 Resource Server with Spring Boot
.OAuth2 Client with Spring Boot
[tabs]
======
Gradle::
@@ -1,28 +0,0 @@
[[oauth2resourceserver-protected-resource-metadata]]
= OAuth 2.0 Protected Resource Metadata
`OAuth2ResourceServerConfigurer.ProtectedResourceMetadataConfigurer` provides the ability to customize the https://www.rfc-editor.org/rfc/rfc9728.html#section-3[OAuth 2.0 Protected Resource Metadata endpoint].
It defines an extension point that lets you customize the https://www.rfc-editor.org/rfc/rfc9728.html#section-3.2[OAuth 2.0 Protected Resource Metadata response].
`OAuth2ResourceServerConfigurer.ProtectedResourceMetadataConfigurer` provides the following configuration option:
[source,java]
----
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.oauth2ResourceServer((resourceServer) ->
resourceServer
.protectedResourceMetadata(protectedResourceMetadata ->
protectedResourceMetadata
.protectedResourceMetadataCustomizer(protectedResourceMetadataCustomizer) <1>
)
);
return http.build();
}
----
<1> `protectedResourceMetadataCustomizer()`: The `Consumer` providing access to the `OAuth2ProtectedResourceMetadata.Builder` allowing the ability to customize the claims of the Resource Server's configuration.
`OAuth2ResourceServerConfigurer.ProtectedResourceMetadataConfigurer` configures the `OAuth2ProtectedResourceMetadataFilter` and registers it with the Resource Server `SecurityFilterChain` `@Bean`.
`OAuth2ProtectedResourceMetadataFilter` is the `Filter` that returns the https://www.rfc-editor.org/rfc/rfc9728.html#section-3.2[OAuth2ProtectedResourceMetadata response].
+1 -1
View File
@@ -1,6 +1,6 @@
{
"dependencies": {
"antora": "3.2.0-alpha.11",
"antora": "3.2.0-alpha.10",
"@antora/atlas-extension": "1.0.0-alpha.5",
"@antora/collector-extension": "1.0.2",
"@asciidoctor/tabs": "1.0.0-beta.6",
-1
View File
@@ -3,7 +3,6 @@ plugins {
id 'io.spring.antora.generate-antora-yml' version '0.0.1'
id 'io.spring.convention.repository'
id 'security-kotlin'
id 'java-toolchain'
}
apply plugin: 'io.spring.convention.docs'
@@ -34,9 +34,7 @@ class AdminMfaAuthorizationManagerConfiguration {
// @formatter:off
http
.authorizeHttpRequests((authorize) -> authorize
// <1>
.requestMatchers("/admin/**").hasRole("ADMIN")
// <2>
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults())
@@ -28,8 +28,8 @@ class RequiredAuthoritiesAuthorizationManagerConfiguration {
// @formatter:off
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/admin/**").hasRole("ADMIN") // <1>
.anyRequest().authenticated() // <2>
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin(Customizer.withDefaults());
@@ -27,8 +27,8 @@ internal class RequiredAuthoritiesAuthorizationManagerConfiguration {
// @formatter:off
http {
authorizeHttpRequests {
authorize("/admin/**", hasRole("ADMIN")) // <1>
authorize(anyRequest, authenticated) // <2>
authorize("/admin/**", hasRole("ADMIN"))
authorize(anyRequest, authenticated)
}
formLogin { }
oneTimeTokenLogin { }
+1 -1
View File
@@ -14,7 +14,7 @@
# limitations under the License.
#
springBootVersion=4.0.0-SNAPSHOT
version=7.0.3
version=7.0.0-RC2
samplesBranch=main
org.gradle.jvmargs=-Xmx3g -XX:+HeapDumpOnOutOfMemoryError
org.gradle.parallel=true
+25 -25
View File
@@ -4,39 +4,39 @@ io-rsocket = "1.1.5"
io-spring-javaformat = "0.0.47"
io-spring-nohttp = "0.0.11"
jakarta-websocket = "2.2.0"
org-apache-maven-resolver = "1.9.25"
org-aspectj = "1.9.25.1"
org-apache-maven-resolver = "1.9.24"
org-aspectj = "1.9.24"
org-bouncycastle = "1.80"
org-eclipse-jetty = "11.0.26"
org-jetbrains-kotlin = "2.2.21"
org-jetbrains-kotlin = "2.2.20"
org-jetbrains-kotlinx = "1.10.2"
org-mockito = "5.17.0"
org-opensaml5 = "5.1.6"
org-springframework = "7.0.4"
org-springframework = "7.0.0-RC1"
com-password4j = "1.8.4"
[libraries]
ch-qos-logback-logback-classic = "ch.qos.logback:logback-classic:1.5.29"
com-fasterxml-jackson-jackson-bom = "com.fasterxml.jackson:jackson-bom:2.20.2"
ch-qos-logback-logback-classic = "ch.qos.logback:logback-classic:1.5.20"
com-fasterxml-jackson-jackson-bom = "com.fasterxml.jackson:jackson-bom:2.20.0"
com-google-inject-guice = "com.google.inject:guice:3.0"
com-netflix-nebula-nebula-project-plugin = "com.netflix.nebula:nebula-project-plugin:8.2.0"
com-nimbusds-nimbus-jose-jwt = "com.nimbusds:nimbus-jose-jwt:10.4"
com-nimbusds-oauth2-oidc-sdk = "com.nimbusds:oauth2-oidc-sdk:11.26.1"
com-squareup-okhttp3-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "com-squareup-okhttp3" }
com-squareup-okhttp3-okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "com-squareup-okhttp3" }
com-unboundid-unboundid-ldapsdk = "com.unboundid:unboundid-ldapsdk:7.0.4"
com-unboundid-unboundid-ldapsdk = "com.unboundid:unboundid-ldapsdk:7.0.3"
com-jayway-jsonpath-json-path = "com.jayway.jsonpath:json-path:2.9.0"
commons-collections = "commons-collections:commons-collections:3.2.2"
io-micrometer-context-propagation = "io.micrometer:context-propagation:1.1.4"
io-micrometer-micrometer-observation = "io.micrometer:micrometer-observation:1.14.14"
io-mockk = "io.mockk:mockk:1.14.9"
io-projectreactor-reactor-bom = "io.projectreactor:reactor-bom:2025.0.3"
io-micrometer-context-propagation = "io.micrometer:context-propagation:1.1.3"
io-micrometer-micrometer-observation = "io.micrometer:micrometer-observation:1.14.12"
io-mockk = "io.mockk:mockk:1.14.6"
io-projectreactor-reactor-bom = "io.projectreactor:reactor-bom:2025.0.0-RC1"
io-rsocket-rsocket-bom = { module = "io.rsocket:rsocket-bom", version.ref = "io-rsocket" }
io-spring-javaformat-spring-javaformat-checkstyle = { module = "io.spring.javaformat:spring-javaformat-checkstyle", version.ref = "io-spring-javaformat" }
io-spring-javaformat-spring-javaformat-gradle-plugin = { module = "io.spring.javaformat:spring-javaformat-gradle-plugin", version.ref = "io-spring-javaformat" }
io-spring-nohttp-nohttp-checkstyle = { module = "io.spring.nohttp:nohttp-checkstyle", version.ref = "io-spring-nohttp" }
io-spring-nohttp-nohttp-gradle = { module = "io.spring.nohttp:nohttp-gradle", version.ref = "io-spring-nohttp" }
io-spring-security-release-plugin = "io.spring.gradle:spring-security-release-plugin:1.0.14"
io-spring-security-release-plugin = "io.spring.gradle:spring-security-release-plugin:1.0.10"
jakarta-annotation-jakarta-annotation-api = "jakarta.annotation:jakarta.annotation-api:3.0.0"
jakarta-inject-jakarta-inject-api = "jakarta.inject:jakarta.inject-api:2.0.1"
jakarta-persistence-jakarta-persistence-api = "jakarta.persistence:jakarta.persistence-api:3.2.0"
@@ -45,13 +45,13 @@ jakarta-servlet-jsp-jakarta-servlet-jsp-api = "jakarta.servlet.jsp:jakarta.servl
jakarta-servlet-jsp-jstl-jakarta-servlet-jsp-jstl-api = "jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api:3.0.2"
jakarta-websocket-jakarta-websocket-api = { module = "jakarta.websocket:jakarta.websocket-api", version.ref = "jakarta-websocket" }
jakarta-websocket-jakarta-websocket-client-api = { module = "jakarta.websocket:jakarta.websocket-client-api", version.ref = "jakarta-websocket" }
jakarta-xml-bind-jakarta-xml-bind-api = "jakarta.xml.bind:jakarta.xml.bind-api:4.0.5"
jakarta-xml-bind-jakarta-xml-bind-api = "jakarta.xml.bind:jakarta.xml.bind-api:4.0.4"
ldapsdk = "ldapsdk:ldapsdk:4.1"
net-sourceforge-htmlunit = "net.sourceforge.htmlunit:htmlunit:2.70.0"
org-htmlunit-htmlunit = "org.htmlunit:htmlunit:4.11.1"
org-apache-httpcomponents-httpclient = "org.apache.httpcomponents.client5:httpclient5:5.5.2"
org-apache-kerby-simplekdc='org.apache.kerby:kerb-simplekdc:2.1.1'
org-apache-maven-maven-resolver-provider = "org.apache.maven:maven-resolver-provider:3.9.12"
org-apache-httpcomponents-httpclient = "org.apache.httpcomponents.client5:httpclient5:5.5.1"
org-apache-kerby-simplekdc='org.apache.kerby:kerb-simplekdc:2.1.0'
org-apache-maven-maven-resolver-provider = "org.apache.maven:maven-resolver-provider:3.9.11"
org-apache-maven-resolver-maven-resolver-connector-basic = { module = "org.apache.maven.resolver:maven-resolver-connector-basic", version.ref = "org-apache-maven-resolver" }
org-apache-maven-resolver-maven-resolver-impl = { module = "org.apache.maven.resolver:maven-resolver-impl", version.ref = "org-apache-maven-resolver" }
org-apache-maven-resolver-maven-resolver-transport-http = { module = "org.apache.maven.resolver:maven-resolver-transport-http", version.ref = "org-apache-maven-resolver" }
@@ -59,7 +59,7 @@ org-apereo-cas-client-cas-client-core = "org.apereo.cas.client:cas-client-core:4
io-freefair-gradle-aspectj-plugin = "io.freefair.gradle:aspectj-plugin:8.13.1"
org-aspectj-aspectjrt = { module = "org.aspectj:aspectjrt", version.ref = "org-aspectj" }
org-aspectj-aspectjweaver = { module = "org.aspectj:aspectjweaver", version.ref = "org-aspectj" }
org-assertj-assertj-core = "org.assertj:assertj-core:3.27.7"
org-assertj-assertj-core = "org.assertj:assertj-core:3.27.6"
org-bouncycastle-bcpkix-jdk15on = { module = "org.bouncycastle:bcpkix-jdk18on", version.ref = "org-bouncycastle" }
org-bouncycastle-bcprov-jdk15on = { module = "org.bouncycastle:bcprov-jdk18on", version.ref = "org-bouncycastle" }
org-eclipse-jetty-jetty-server = { module = "org.eclipse.jetty:jetty-server", version.ref = "org-eclipse-jetty" }
@@ -68,9 +68,9 @@ org-hamcrest = "org.hamcrest:hamcrest:2.2"
org-hibernate-orm-hibernate-core = "org.hibernate.orm:hibernate-core:7.0.10.Final"
org-hsqldb = "org.hsqldb:hsqldb:2.7.4"
org-jetbrains-kotlin-kotlin-bom = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "org-jetbrains-kotlin" }
org-jetbrains-kotlin-kotlin-gradle-plugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:2.2.21"
org-jetbrains-kotlin-kotlin-gradle-plugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:2.2.20"
org-jetbrains-kotlinx-kotlinx-coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version.ref = "org-jetbrains-kotlinx" }
org-junit-junit-bom = "org.junit:junit-bom:6.0.2"
org-junit-junit-bom = "org.junit:junit-bom:6.0.0"
org-mockito-mockito-bom = { module = "org.mockito:mockito-bom", version.ref = "org-mockito" }
org-opensaml-opensaml5-saml-api = { module = "org.opensaml:opensaml-saml-api", version.ref = "org-opensaml5" }
org-opensaml-opensaml5-saml-impl = { module = "org.opensaml:opensaml-saml-impl", version.ref = "org-opensaml5" }
@@ -81,28 +81,28 @@ org-seleniumhq-selenium-selenium-support = "org.seleniumhq.selenium:selenium-sup
org-skyscreamer-jsonassert = "org.skyscreamer:jsonassert:1.5.3"
org-slf4j-log4j-over-slf4j = "org.slf4j:log4j-over-slf4j:1.7.36"
org-slf4j-slf4j-api = "org.slf4j:slf4j-api:2.0.17"
org-springframework-data-spring-data-bom = "org.springframework.data:spring-data-bom:2025.1.3"
org-springframework-ldap-spring-ldap-core = "org.springframework.ldap:spring-ldap-core:4.0.2"
org-springframework-data-spring-data-bom = "org.springframework.data:spring-data-bom:2025.1.0-RC1"
org-springframework-ldap-spring-ldap-core = "org.springframework.ldap:spring-ldap-core:4.0.0-RC1"
org-springframework-spring-framework-bom = { module = "org.springframework:spring-framework-bom", version.ref = "org-springframework" }
org-synchronoss-cloud-nio-multipart-parser = "org.synchronoss.cloud:nio-multipart-parser:1.1.0"
tools-jackson-jackson-bom = "tools.jackson:jackson-bom:3.0.4"
tools-jackson-jackson-bom = "tools.jackson:jackson-bom:3.0.1"
com-google-code-gson-gson = "com.google.code.gson:gson:2.13.2"
com-thaiopensource-trag = "com.thaiopensource:trang:20091111"
net-sourceforge-saxon-saxon = "net.sourceforge.saxon:saxon:9.1.0.8"
org-yaml-snakeyaml = "org.yaml:snakeyaml:1.33"
org-apache-commons-commons-io = "org.apache.commons:commons-io:1.3.2"
io-github-gradle-nexus-publish-plugin = "io.github.gradle-nexus:publish-plugin:2.0.0"
io-github-gradle-nexus-publish-plugin = "io.github.gradle-nexus:publish-plugin:1.3.0"
org-gretty-gretty = "org.gretty:gretty:4.1.10"
com-github-ben-manes-gradle-versions-plugin = "com.github.ben-manes:gradle-versions-plugin:0.52.0"
com-github-spullara-mustache-java-compiler = "com.github.spullara.mustache.java:compiler:0.9.14"
org-hidetake-gradle-ssh-plugin = "org.hidetake:gradle-ssh-plugin:2.10.1"
org-jfrog-buildinfo-build-info-extractor-gradle = "org.jfrog.buildinfo:build-info-extractor-gradle:6.0.4"
org-jfrog-buildinfo-build-info-extractor-gradle = "org.jfrog.buildinfo:build-info-extractor-gradle:4.34.2"
org-sonarsource-scanner-gradle-sonarqube-gradle-plugin = "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.8.0.1969"
org-instancio-instancio-junit = "org.instancio:instancio-junit:3.7.1"
spring-nullability = 'io.spring.nullability:io.spring.nullability.gradle.plugin:0.0.6'
webauthn4j-core = 'com.webauthn4j:webauthn4j-core:0.31.0.RELEASE'
webauthn4j-core = 'com.webauthn4j:webauthn4j-core:0.29.7.RELEASE'
com-password4j-password4j = { module = "com.password4j:password4j", version.ref = "com-password4j" }
[plugins]
+2 -2
View File
@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=f1771298a70f6db5a29daf62378c4e18a17fc33c9ba6b14362e0cdf40610380d
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.4-bin.zip
distributionSha256Sum=61ad310d3c7d3e5da131b76bbf22b5a4c0786e9d892dae8c1658d4b484de3caa
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
+1 -1
View File
@@ -1,5 +1,5 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem Copyright 2004-present the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@@ -1,5 +1,4 @@
apply plugin: 'io.spring.convention.spring-test'
apply plugin: 'java-toolchain'
dependencies {
implementation platform(project(":spring-security-dependencies"))
+6 -6
View File
@@ -1944,9 +1944,9 @@
"dev": true
},
"node_modules/js-yaml": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
"dependencies": {
"argparse": "^2.0.1"
@@ -4524,9 +4524,9 @@
"dev": true
},
"js-yaml": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
"requires": {
"argparse": "^2.0.1"
@@ -39,7 +39,7 @@ import org.springframework.util.Assert;
* It needs a <code>KerberosTicketValidator</code>, which contains the code to validate
* the ticket, as this code is different between SUN and IBM JRE.<br>
* It also needs an <code>UserDetailsService</code> to load the user properties and the
* <code>GrantedAuthorities</code>, as we only get back the username from Kerberos
* <code>GrantedAuthorities</code>, as we only get back the username from Kerbeos
* </p>
*
* You can see an example configuration in
@@ -31,7 +31,7 @@ public interface KerberosTicketValidator {
/**
* Validates a Kerberos/SPNEGO ticket.
* @param token Kerberos/SPNEGO ticket
* @param token Kerbeos/SPNEGO ticket
* @return authenticated kerberos principal
* @throws BadCredentialsException if the ticket is not valid
*/
@@ -76,7 +76,7 @@ import org.springframework.web.filter.OncePerRequestFilter;
* &lt;/sec:authentication-manager&gt;
*
* &lt;bean id=&quot;kerberosServiceAuthenticationProvider&quot;
* class=&quot;org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider&quot;&gt;
* class=&quot;org.springframework.security.kerberos.authenitcation.KerberosServiceAuthenticationProvider&quot;&gt;
* &lt;property name=&quot;ticketValidator&quot;&gt;
* &lt;bean class=&quot;org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator&quot;&gt;
* &lt;property name=&quot;servicePrincipal&quot; value=&quot;HTTP/web.springsource.com&quot; /&gt;
@@ -103,7 +103,7 @@ import org.springframework.web.filter.OncePerRequestFilter;
* <a href="https://bugs.sun.com/view_bug.do?bug_id=6851973">bug</a>.
* </p>
* <p>
* A workaround until this is fixed in the JVM is to change
* A workaround unti this is fixed in the JVM is to change
* </p>
* HKEY_LOCAL_MACHINE\System \CurrentControlSet\Control\LSA\SuppressExtendedProtection to
* 0x02
@@ -134,9 +134,9 @@ public final class PathPatternMessageMatcher implements MessageMatcher<Object> {
* The following are valid patterns and their meaning
* <ul>
* <li>{@code /path} - match exactly and only `/path`</li>
* <li>{@code /path/**} - match `/path` and any of its descendants</li>
* <li>{@code /path/**} - match `/path` and any of its descendents</li>
* <li>{@code /path/{value}/**} - match `/path/subdirectory` and any of its
* descendants, capturing the value of the subdirectory in
* descendents, capturing the value of the subdirectory in
* {@link MessageAuthorizationContext#getVariables()}</li>
* </ul>
*
@@ -169,9 +169,9 @@ public final class PathPatternMessageMatcher implements MessageMatcher<Object> {
* The following are valid patterns and their meaning
* <ul>
* <li>{@code /path} - match exactly and only `/path`</li>
* <li>{@code /path/**} - match `/path` and any of its descendants</li>
* <li>{@code /path/**} - match `/path` and any of its descendents</li>
* <li>{@code /path/{value}/**} - match `/path/subdirectory` and any of its
* descendants, capturing the value of the subdirectory in
* descendents, capturing the value of the subdirectory in
* {@link MessageAuthorizationContext#getVariables()}</li>
* </ul>
*
@@ -40,8 +40,10 @@ import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
@@ -130,6 +132,8 @@ public final class ResolvableMethod {
private static final SpringObjenesis objenesis = new SpringObjenesis();
private static final ParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
// Matches ValueConstants.DEFAULT_NONE (spring-web and spring-messaging)
private static final String DEFAULT_VALUE_NONE = "\n\t\t\n\t\t\n\uE000\uE001\uE002\n\t\t\t\t\n";
@@ -630,6 +634,7 @@ public final class ResolvableMethod {
List<MethodParameter> matches = new ArrayList<>();
for (int i = 0; i < ResolvableMethod.this.method.getParameterCount(); i++) {
MethodParameter param = new SynthesizingMethodParameter(ResolvableMethod.this.method, i);
param.initParameterNameDiscovery(nameDiscoverer);
if (this.filters.stream().allMatch((p) -> p.test(param))) {
matches.add(param);
}
@@ -466,7 +466,7 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
/**
* The default {@link RowMapper} that maps the current row in
* {@code java.sql.ResultSet} to {@link OAuth2Authorization} using Jackson 3's
* {@link JsonMapper}.
* {@link JsonMapper} to read all {@code Map<String,Object>} within the result.
*
* @author Rob Winch
* @since 7.0
@@ -482,7 +482,6 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
public JsonMapperOAuth2AuthorizationRowMapper(RegisteredClientRepository registeredClientRepository,
JsonMapper jsonMapper) {
super(registeredClientRepository);
Assert.notNull(jsonMapper, "jsonMapper cannot be null");
this.jsonMapper = jsonMapper;
}
@@ -545,7 +544,7 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
private LobHandler lobHandler = new DefaultLobHandler();
private AbstractOAuth2AuthorizationRowMapper(RegisteredClientRepository registeredClientRepository) {
AbstractOAuth2AuthorizationRowMapper(RegisteredClientRepository registeredClientRepository) {
Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null");
this.registeredClientRepository = registeredClientRepository;
}
@@ -714,36 +713,42 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
}
/**
* The default {@code Function} that maps {@link OAuth2Authorization} to a
* {@code List} of {@link SqlParameterValue} using an instance of Jackson 3's
* {@link JsonMapper}.
* Nested class to protect from getting {@link NoClassDefFoundError} when Jackson 2 is
* not on the classpath.
*
* @deprecated This is used to allow transition to Jackson 3. Use {@link Jackson3}
* instead.
*/
public static class JsonMapperOAuth2AuthorizationParametersMapper
extends AbstractOAuth2AuthorizationParametersMapper {
@Deprecated(forRemoval = true, since = "7.0")
private static final class Jackson2 {
private final JsonMapper jsonMapper;
public JsonMapperOAuth2AuthorizationParametersMapper() {
this(Jackson3.createJsonMapper());
}
public JsonMapperOAuth2AuthorizationParametersMapper(JsonMapper jsonMapper) {
Assert.notNull(jsonMapper, "jsonMapper cannot be null");
this.jsonMapper = jsonMapper;
}
@Override
String writeValueAsString(Map<String, Object> data) throws Exception {
return this.jsonMapper.writeValueAsString(data);
static ObjectMapper createObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
ClassLoader classLoader = Jackson2.class.getClassLoader();
List<Module> securityModules = SecurityJackson2Modules.getModules(classLoader);
objectMapper.registerModules(securityModules);
objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());
return objectMapper;
}
}
/**
* A {@code Function} that maps {@link OAuth2Authorization} to a {@code List} of
* {@link SqlParameterValue} using an instance of Jackson 2's {@link ObjectMapper}.
*
* @deprecated Use {@link JsonMapperOAuth2AuthorizationParametersMapper} to switch to
* Nested class used to get a common default instance of {@link JsonMapper}. It is in
* a nested class to protect from getting {@link NoClassDefFoundError} when Jackson 3
* is not on the classpath.
*/
private static final class Jackson3 {
static JsonMapper createJsonMapper() {
List<JacksonModule> modules = SecurityJacksonModules.getModules(Jackson3.class.getClassLoader());
return JsonMapper.builder().addModules(modules).build();
}
}
/**
* @deprecated Use {@link JsonMapperOAuth2AuthorizationParametersMapper} to migrate to
* Jackson 3.
*/
@Deprecated(forRemoval = true, since = "7.0")
@@ -767,6 +772,32 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
}
/**
* The default {@code Function} that maps {@link OAuth2Authorization} to a
* {@code List} of {@link SqlParameterValue} using an instance of Jackson 3's
* {@link JsonMapper}.
*/
public static final class JsonMapperOAuth2AuthorizationParametersMapper
extends AbstractOAuth2AuthorizationParametersMapper {
private final JsonMapper mapper;
public JsonMapperOAuth2AuthorizationParametersMapper() {
this(Jackson3.createJsonMapper());
}
public JsonMapperOAuth2AuthorizationParametersMapper(JsonMapper mapper) {
Assert.notNull(mapper, "mapper cannot be null");
this.mapper = mapper;
}
@Override
String writeValueAsString(Map<String, Object> data) throws Exception {
return this.mapper.writeValueAsString(data);
}
}
/**
* The base {@code Function} that maps {@link OAuth2Authorization} to a {@code List}
* of {@link SqlParameterValue}.
@@ -774,7 +805,7 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
private abstract static class AbstractOAuth2AuthorizationParametersMapper
implements Function<OAuth2Authorization, List<SqlParameterValue>> {
private AbstractOAuth2AuthorizationParametersMapper() {
protected AbstractOAuth2AuthorizationParametersMapper() {
}
@Override
@@ -885,41 +916,6 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
}
/**
* Nested class to protect from getting {@link NoClassDefFoundError} when Jackson 2 is
* not on the classpath.
*
* @deprecated This is used to allow transition to Jackson 3. Use {@link Jackson3}
* instead.
*/
@Deprecated(forRemoval = true, since = "7.0")
private static final class Jackson2 {
private static ObjectMapper createObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
ClassLoader classLoader = Jackson2.class.getClassLoader();
List<Module> securityModules = SecurityJackson2Modules.getModules(classLoader);
objectMapper.registerModules(securityModules);
objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());
return objectMapper;
}
}
/**
* Nested class used to get a common default instance of {@link JsonMapper}. It is in
* a nested class to protect from getting {@link NoClassDefFoundError} when Jackson 3
* is not on the classpath.
*/
private static final class Jackson3 {
private static JsonMapper createJsonMapper() {
List<JacksonModule> modules = SecurityJacksonModules.getModules(Jackson3.class.getClassLoader());
return JsonMapper.builder().addModules(modules).build();
}
}
private static final class LobCreatorArgumentPreparedStatementSetter extends ArgumentPreparedStatementSetter {
private final LobCreator lobCreator;
@@ -33,7 +33,6 @@ import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.jackson.CoreJacksonModule;
import org.springframework.security.jackson2.CoreJackson2Module;
import org.springframework.security.oauth2.core.AbstractOAuth2Token;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
@@ -49,11 +48,9 @@ import org.springframework.security.oauth2.server.authorization.JdbcOAuth2Author
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.jackson.OAuth2AuthorizationServerJacksonModule;
import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.security.web.jackson.WebServletJacksonModule;
import org.springframework.security.web.jackson2.WebServletJackson2Module;
import org.springframework.security.web.savedrequest.DefaultSavedRequest;
import org.springframework.util.ClassUtils;
@@ -70,18 +67,7 @@ import org.springframework.util.ClassUtils;
*/
class OAuth2AuthorizationServerBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor {
private static final boolean jackson2Present;
private static final boolean jackson3Present;
static {
ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader)
&& ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
jackson3Present = ClassUtils.isPresent("tools.jackson.databind.json.JsonMapper", classLoader);
}
private boolean jacksonContributed;
private boolean jackson2Contributed;
@Override
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
@@ -93,17 +79,17 @@ class OAuth2AuthorizationServerBeanRegistrationAotProcessor implements BeanRegis
// @formatter:off
if ((isJdbcBasedOAuth2AuthorizationService || isJdbcBasedRegisteredClientRepository)
&& !this.jacksonContributed) {
JacksonConfigurationBeanRegistrationAotContribution jacksonContribution =
new JacksonConfigurationBeanRegistrationAotContribution();
this.jacksonContributed = true;
return jacksonContribution;
&& !this.jackson2Contributed) {
Jackson2ConfigurationBeanRegistrationAotContribution jackson2Contribution =
new Jackson2ConfigurationBeanRegistrationAotContribution();
this.jackson2Contributed = true;
return jackson2Contribution;
}
// @formatter:on
return null;
}
private static class JacksonConfigurationBeanRegistrationAotContribution
private static class Jackson2ConfigurationBeanRegistrationAotContribution
implements BeanRegistrationAotContribution {
private final BindingReflectionHintsRegistrar reflectionHintsRegistrar = new BindingReflectionHintsRegistrar();
@@ -123,6 +109,7 @@ class OAuth2AuthorizationServerBeanRegistrationAotProcessor implements BeanRegis
.registerType(HashSet.class, MemberCategory.DECLARED_FIELDS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS);
// Spring Security and Spring Authorization Server
hints.reflection()
.registerTypes(Arrays.asList(TypeReference.of(AbstractAuthenticationToken.class),
TypeReference.of(DefaultSavedRequest.Builder.class),
@@ -141,143 +128,75 @@ class OAuth2AuthorizationServerBeanRegistrationAotProcessor implements BeanRegis
(builder) -> builder.withMembers(MemberCategory.DECLARED_FIELDS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS));
// Jackson Modules
if (jackson2Present) {
hints.reflection()
.registerTypes(
Arrays.asList(TypeReference.of(CoreJackson2Module.class),
TypeReference.of(WebServletJackson2Module.class),
TypeReference.of(OAuth2AuthorizationServerJackson2Module.class)),
(builder) -> builder.withMembers(MemberCategory.DECLARED_FIELDS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
MemberCategory.INVOKE_DECLARED_METHODS));
}
if (jackson3Present) {
hints.reflection()
.registerTypes(
Arrays.asList(TypeReference.of(CoreJacksonModule.class),
TypeReference.of(WebServletJacksonModule.class),
TypeReference.of(OAuth2AuthorizationServerJacksonModule.class)),
(builder) -> builder.withMembers(MemberCategory.DECLARED_FIELDS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
MemberCategory.INVOKE_DECLARED_METHODS));
}
// Jackson Modules - Spring Security and Spring Authorization Server
hints.reflection()
.registerTypes(
Arrays.asList(TypeReference.of(CoreJackson2Module.class),
TypeReference.of(WebServletJackson2Module.class),
TypeReference.of(OAuth2AuthorizationServerJackson2Module.class)),
(builder) -> builder.withMembers(MemberCategory.DECLARED_FIELDS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS));
// Jackson Mixins
if (jackson2Present) {
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.jackson2.UnmodifiableSetMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.jackson2.UnmodifiableListMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.jackson2.UnmodifiableMapMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
"org.springframework.security.oauth2.server.authorization.jackson2.UnmodifiableMapMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.server.authorization.jackson2.HashSetMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.web.jackson2.DefaultSavedRequestMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.web.jackson2.WebAuthenticationDetailsMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.jackson2.UsernamePasswordAuthenticationTokenMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.jackson2.UserMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.jackson2.SimpleGrantedAuthorityMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
"org.springframework.security.oauth2.server.authorization.jackson2.OAuth2TokenExchangeActorMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
"org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationRequestMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
"org.springframework.security.oauth2.server.authorization.jackson2.OAuth2TokenExchangeCompositeAuthenticationTokenMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
"org.springframework.security.oauth2.server.authorization.jackson2.OAuth2TokenFormatMixin"));
}
if (jackson3Present) {
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.web.jackson.DefaultSavedRequestMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.web.jackson.WebAuthenticationDetailsMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.jackson.UsernamePasswordAuthenticationTokenMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.jackson.UserMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.jackson.SimpleGrantedAuthorityMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
"org.springframework.security.oauth2.server.authorization.jackson.OAuth2TokenExchangeActorMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
"org.springframework.security.oauth2.server.authorization.jackson.OAuth2AuthorizationRequestMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
"org.springframework.security.oauth2.server.authorization.jackson.OAuth2TokenExchangeCompositeAuthenticationTokenMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
"org.springframework.security.oauth2.server.authorization.jackson.OAuth2TokenFormatMixin"));
}
// Jackson Mixins - Spring Security and Spring Authorization Server
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.jackson2.UnmodifiableSetMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.jackson2.UnmodifiableListMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.jackson2.UnmodifiableMapMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
"org.springframework.security.oauth2.server.authorization.jackson2.UnmodifiableMapMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.server.authorization.jackson2.HashSetMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.web.jackson2.DefaultSavedRequestMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.web.jackson2.WebAuthenticationDetailsMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.jackson2.UsernamePasswordAuthenticationTokenMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.jackson2.UserMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.jackson2.SimpleGrantedAuthorityMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
"org.springframework.security.oauth2.server.authorization.jackson2.OAuth2TokenExchangeActorMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
"org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationRequestMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
"org.springframework.security.oauth2.server.authorization.jackson2.OAuth2TokenExchangeCompositeAuthenticationTokenMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
"org.springframework.security.oauth2.server.authorization.jackson2.OAuth2TokenFormatMixin"));
// Check if OAuth2 Client is on classpath
// Check if Spring Security OAuth2 Client is on classpath
if (ClassUtils.isPresent("org.springframework.security.oauth2.client.registration.ClientRegistration",
ClassUtils.getDefaultClassLoader())) {
// Jackson Module (and required types) - Spring Security OAuth2 Client
hints.reflection()
.registerType(TypeReference
.of("org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken"),
.registerTypes(Arrays.asList(
TypeReference
.of("org.springframework.security.oauth2.client.jackson2.OAuth2ClientJackson2Module"),
TypeReference
.of("org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken")),
(builder) -> builder.withMembers(MemberCategory.DECLARED_FIELDS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
MemberCategory.INVOKE_DECLARED_METHODS));
// Jackson Module
if (jackson2Present) {
hints.reflection()
.registerType(TypeReference
.of("org.springframework.security.oauth2.client.jackson2.OAuth2ClientJackson2Module"),
(builder) -> builder.withMembers(MemberCategory.DECLARED_FIELDS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
MemberCategory.INVOKE_DECLARED_METHODS));
}
if (jackson3Present) {
hints.reflection()
.registerType(
TypeReference
.of("org.springframework.security.oauth2.client.jackson.OAuth2ClientJacksonModule"),
(builder) -> builder.withMembers(MemberCategory.DECLARED_FIELDS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
MemberCategory.INVOKE_DECLARED_METHODS));
}
// Jackson Mixins
if (jackson2Present) {
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
"org.springframework.security.oauth2.client.jackson2.OAuth2AuthenticationTokenMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.client.jackson2.DefaultOidcUserMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.client.jackson2.DefaultOAuth2UserMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.client.jackson2.OidcUserAuthorityMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.client.jackson2.OAuth2UserAuthorityMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.client.jackson2.OidcIdTokenMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.client.jackson2.OidcUserInfoMixin"));
}
if (jackson3Present) {
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
"org.springframework.security.oauth2.client.jackson.OAuth2AuthenticationTokenMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.client.jackson.DefaultOidcUserMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.client.jackson.DefaultOAuth2UserMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.client.jackson.OidcUserAuthorityMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.client.jackson.OAuth2UserAuthorityMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.client.jackson.OidcIdTokenMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.client.jackson.OidcUserInfoMixin"));
}
// Jackson Mixins - Spring Security OAuth2 Client
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
"org.springframework.security.oauth2.client.jackson2.OAuth2AuthenticationTokenMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.client.jackson2.DefaultOidcUserMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.client.jackson2.DefaultOAuth2UserMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.client.jackson2.OidcUserAuthorityMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.client.jackson2.OAuth2UserAuthorityMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.client.jackson2.OidcIdTokenMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.client.jackson2.OidcUserInfoMixin"));
}
}
@@ -1,53 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.server.authorization.aot.hint;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter;
/**
* {@link RuntimeHintsRegistrar} that contributes the required {@link RuntimeHints} for
* OAuth 2.1 Authorization Server. Statically registered via
* META-INF/spring/aot.factories.
*
* @author Joe Grandja
* @since 7.0
*/
class OAuth2AuthorizationServerRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.reflection()
.registerType(OAuth2AuthorizationCodeRequestAuthenticationProvider.class,
MemberCategory.INVOKE_DECLARED_METHODS);
hints.reflection()
.registerType(OAuth2AuthorizationEndpointFilter.class, MemberCategory.INVOKE_DECLARED_METHODS);
hints.reflection()
.registerType(TypeReference
.of("org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter$OAuth2AuthorizationCodeRequestValidatingFilter"),
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
hints.reflection()
.registerType(OAuth2AuthorizationCodeRequestAuthenticationToken.class, MemberCategory.DECLARED_FIELDS);
}
}

Some files were not shown because too many files have changed in this diff Show More