Merge branch 'main' into gh-16390

Signed-off-by: yoobin_mion <113106136+yybmion@users.noreply.github.com>
This commit is contained in:
yoobin_mion 2025-07-02 15:02:17 +09:00 committed by GitHub
commit 5d237a7822
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1082 changed files with 20479 additions and 14397 deletions

View File

@ -1,40 +0,0 @@
version: 2
registries:
spring-milestones:
type: maven-repository
url: https://repo.spring.io/milestone
updates:
- package-ecosystem: "gradle"
target-branch: "main"
directory: "/"
schedule:
interval: "daily"
time: "03:00"
timezone: "Etc/UTC"
labels: [ "type: dependency-upgrade" ]
registries:
- "spring-milestones"
ignore:
- dependency-name: "com.nimbusds:nimbus-jose-jwt" # nimbus-jose-jwt gets updated when oauth2-oidc-sdk is updated to ensure consistency
- dependency-name: "org.python:jython" # jython updates break integration tests
- dependency-name: "org.apache.directory.server:*" # ApacheDS version > 1.5.5 contains break changes
- dependency-name: "org.junit:junit-bom"
update-types: [ "version-update:semver-major" ]
- dependency-name: "org.mockito:mockito-bom"
update-types: [ "version-update:semver-major" ]
- dependency-name: "*"
update-types: [ "version-update:semver-major", "version-update:semver-minor" ]
# GitHub Actions
- package-ecosystem: github-actions
target-branch: "main"
directory: "/"
schedule:
interval: weekly
ignore:
- dependency-name: "spring-io/*"
- dependency-name: "spring-security-release-tools/*"

View File

@ -4,6 +4,32 @@ registries:
type: maven-repository type: maven-repository
url: https://repo.spring.io/milestone url: https://repo.spring.io/milestone
updates: updates:
- package-ecosystem: gradle
target-branch: 6.5.x
directory: /
schedule:
interval: daily
time: '03:00'
timezone: Etc/UTC
labels:
- 'type: dependency-upgrade'
registries:
- spring-milestones
ignore:
- dependency-name: com.nimbusds:nimbus-jose-jwt
- dependency-name: org.python:jython
- dependency-name: org.apache.directory.server:*
- dependency-name: org.apache.directory.shared:*
- dependency-name: org.junit:junit-bom
update-types:
- version-update:semver-major
- dependency-name: org.mockito:mockito-bom
update-types:
- version-update:semver-major
- dependency-name: '*'
update-types:
- version-update:semver-major
- version-update:semver-minor
- package-ecosystem: gradle - package-ecosystem: gradle
target-branch: 6.4.x target-branch: 6.4.x
directory: / directory: /
@ -85,17 +111,8 @@ updates:
- dependency-name: '*' - dependency-name: '*'
update-types: update-types:
- version-update:semver-major - version-update:semver-major
- version-update:semver-minor
- package-ecosystem: github-actions
target-branch: 6.4.x
directory: /
schedule:
interval: weekly
labels:
- 'type: task'
- 'in: build'
ignore:
- dependency-name: sjohnr/*
- package-ecosystem: github-actions - package-ecosystem: github-actions
target-branch: 6.3.x target-branch: 6.3.x
directory: / directory: /
@ -106,14 +123,6 @@ updates:
- 'in: build' - 'in: build'
ignore: ignore:
- dependency-name: sjohnr/* - dependency-name: sjohnr/*
- package-ecosystem: github-actions
target-branch: main
directory: /
schedule:
interval: weekly
labels:
- 'type: task'
- 'in: build'
- package-ecosystem: github-actions - package-ecosystem: github-actions
target-branch: docs-build target-branch: docs-build
directory: / directory: /

17
.github/workflows/codeql.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: "CodeQL Advanced"
on:
push:
pull_request:
workflow_dispatch:
schedule:
# https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#schedule
- cron: '0 5 * * *'
permissions: read-all
jobs:
codeql-analysis-call:
permissions:
actions: read
contents: read
security-events: write
uses: spring-io/github-actions/.github/workflows/codeql-analysis.yml@1

View File

@ -39,48 +39,25 @@ jobs:
toolchain: 17 toolchain: 17
with: with:
java-version: ${{ matrix.java-version }} java-version: ${{ matrix.java-version }}
test-args: --refresh-dependencies -PforceMavenRepositories=snapshot -PisOverrideVersionCatalog -PtestToolchain=${{ matrix.toolchain }} -PspringFrameworkVersion=6.2.+ -PreactorVersion=2023.0.+ -PspringDataVersion=2024.0.+ --stacktrace test-args: --refresh-dependencies -PforceMavenRepositories=snapshot,https://oss.sonatype.org/content/repositories/snapshots -PisOverrideVersionCatalog -PtestToolchain=${{ matrix.toolchain }} -PspringFrameworkVersion=7.+ -PreactorVersion=2025.+ -PspringDataVersion=2025.+ --stacktrace
secrets: inherit secrets: inherit
check-samples:
name: Check Samples
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'spring-projects' }}
steps:
- uses: actions/checkout@v4
- name: Set up gradle
uses: spring-io/spring-gradle-build-action@v2
with:
java-version: 17
distribution: temurin
- name: Check samples project
env:
LOCAL_REPOSITORY_PATH: ${{ github.workspace }}/build/publications/repos
SAMPLES_DIR: ../spring-security-samples
run: |
# Extract version from gradle.properties
version=$(cat gradle.properties | grep "version=" | awk -F'=' '{print $2}')
# Extract samplesBranch from gradle.properties
samples_branch=$(cat gradle.properties | grep "samplesBranch=" | awk -F'=' '{print $2}')
./gradlew publishMavenJavaPublicationToLocalRepository
./gradlew cloneRepository -PrepositoryName="spring-projects/spring-security-samples" -Pref="$samples_branch" -PcloneOutputDirectory="$SAMPLES_DIR"
./gradlew --refresh-dependencies --project-dir "$SAMPLES_DIR" --init-script spring-security-ci.gradle -PlocalRepositoryPath="$LOCAL_REPOSITORY_PATH" -PspringSecurityVersion="$version" test integrationTest
deploy-artifacts: deploy-artifacts:
name: Deploy Artifacts name: Deploy Artifacts
needs: [ build, test, check-samples ] needs: [ build, test]
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml@v1 uses: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml@v1
with: with:
should-deploy-artifacts: ${{ needs.build.outputs.should-deploy-artifacts }} should-deploy-artifacts: ${{ needs.build.outputs.should-deploy-artifacts }}
secrets: inherit secrets: inherit
deploy-docs: deploy-docs:
name: Deploy Docs name: Deploy Docs
needs: [ build, test, check-samples ] needs: [ build, test ]
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-docs.yml@v1 uses: spring-io/spring-security-release-tools/.github/workflows/deploy-docs.yml@v1
with: with:
should-deploy-docs: ${{ needs.build.outputs.should-deploy-artifacts }} should-deploy-docs: ${{ needs.build.outputs.should-deploy-artifacts }}
secrets: inherit secrets: inherit
deploy-schema: deploy-schema:
name: Deploy Schema name: Deploy Schema
needs: [ build, test, check-samples ] needs: [ build, test ]
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml@v1 uses: spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml@v1
with: with:
should-deploy-schema: ${{ needs.build.outputs.should-deploy-artifacts }} should-deploy-schema: ${{ needs.build.outputs.should-deploy-artifacts }}

View File

@ -1,57 +0,0 @@
name: Auto Merge Forward Dependabot Commits
on:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: dependabot-auto-merge-forward
jobs:
get-supported-branches:
uses: spring-io/spring-security-release-tools/.github/workflows/retrieve-spring-supported-versions.yml@actions-v1
with:
project: spring-security
type: oss
repository_name: spring-projects/spring-security
auto-merge-forward-dependabot:
name: Auto Merge Forward Dependabot Commits
runs-on: ubuntu-latest
needs: [get-supported-branches]
permissions:
contents: write
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4
with:
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
- name: Setup GitHub User
id: setup-gh-user
run: |
git config user.name 'github-actions[bot]'
git config user.email 'github-actions[bot]@users.noreply.github.com'
- name: Run Auto Merge Forward
id: run-auto-merge-forward
uses: spring-io/spring-security-release-tools/.github/actions/auto-merge-forward@actions-v1
with:
branches: ${{ needs.get-supported-branches.outputs.supported_versions }},main
from-author: dependabot[bot]
notify_result:
name: Check for failures
needs: [ auto-merge-forward-dependabot ]
if: failure()
runs-on: ubuntu-latest
permissions:
actions: read
steps:
- name: Send Slack message
uses: Gamesight/slack-workflow-status@v1.3.0
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }}
channel: '#spring-security-ci'
name: 'CI Notifier'

View File

@ -4,7 +4,8 @@ on:
schedule: schedule:
- cron: '0 2 * * *' # 2am UTC - cron: '0 2 * * *' # 2am UTC
workflow_dispatch: workflow_dispatch:
permissions:
pull-requests: write
jobs: jobs:
upgrade_wrapper: upgrade_wrapper:
name: Execution name: Execution

View File

@ -1,45 +0,0 @@
name: Mark Duplicate Dependabot PRs
on:
pull_request:
types: [closed]
jobs:
check_duplicate_prs:
runs-on: ubuntu-latest
if: github.event.pull_request.merged == true && github.event.pull_request.user.login == 'dependabot[bot]'
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Extract Dependency Name from PR Title
id: extract
run: |
PR_TITLE="${{ github.event.pull_request.title }}"
DEPENDENCY_NAME=$(echo "$PR_TITLE" | awk -F ' from ' '{print $1}')
echo "dependency_name=$DEPENDENCY_NAME" >> $GITHUB_OUTPUT
- name: Find PRs
id: find_duplicates
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PRS=$(gh pr list --search 'milestone:${{ github.event.pull_request.milestone.title }} is:merged in:title "${{ steps.extract.outputs.dependency_name }}"' --json number --jq 'map(.number) | join(",")')
echo "prs=$PRS" >> $GITHUB_OUTPUT
- name: Label Duplicate PRs
if: steps.find_duplicates.outputs.prs != ''
env:
PRS: ${{ steps.find_duplicates.outputs.prs }}
CURRENT_PR_NUMBER: ${{ github.event.pull_request.number }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
for i in ${PRS//,/ }
do
if [ ! $i -eq "$CURRENT_PR_NUMBER" ]; then
echo "Marking PR $i as duplicate"
gh pr edit "$i" --add-label "status: duplicate"
gh pr comment "$i" --body "Duplicate of #$CURRENT_PR_NUMBER"
fi
done

View File

@ -1,63 +0,0 @@
name: Merge Dependabot PR
on: pull_request_target
run-name: Merge Dependabot PR ${{ github.ref_name }}
permissions: write-all
jobs:
merge-dependabot-pr:
name: Merge Dependabot PR
runs-on: ubuntu-latest
if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' && github.repository == 'spring-projects/spring-security' }}
steps:
- uses: actions/checkout@v4
with:
show-progress: false
ref: ${{ github.event.pull_request.head.sha }}
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
- name: Set Milestone to Dependabot Pull Request
id: set-milestone
run: |
if test -f pom.xml
then
CURRENT_VERSION=$(mvn help:evaluate -Dexpression="project.version" -q -DforceStdout)
else
CURRENT_VERSION=$(cat gradle.properties | sed -n '/^version=/ { s/^version=//;p }')
fi
export CANDIDATE_VERSION=${CURRENT_VERSION/-SNAPSHOT}
MILESTONE=$(gh api repos/$GITHUB_REPOSITORY/milestones --jq 'map(select(.due_on != null and (.title | startswith(env.CANDIDATE_VERSION)))) | .[0] | .title')
if [ -z $MILESTONE ]
then
gh run cancel ${{ github.run_id }}
echo "::warning title=Cannot merge::No scheduled milestone for $CURRENT_VERSION version"
else
gh pr edit ${{ github.event.pull_request.number }} --milestone $MILESTONE
echo mergeEnabled=true >> $GITHUB_OUTPUT
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Merge Dependabot pull request
if: steps.set-milestone.outputs.mergeEnabled
run: gh pr merge ${{ github.event.pull_request.number }} --auto --rebase
env:
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
send-notification:
name: Send Notification
needs: [ merge-dependabot-pr ]
if: ${{ failure() || cancelled() }}
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 }}

View File

@ -11,7 +11,7 @@ jobs:
strategy: strategy:
matrix: matrix:
# List of active maintenance branches. # List of active maintenance branches.
branch: [ main, 6.4.x, 6.3.x ] branch: [ main, 6.5.x, 6.4.x, 6.3.x ]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout

View File

@ -1,22 +0,0 @@
name: Trigger Dependabot Auto Merge Forward
on:
push:
branches:
- '*.x'
permissions: read-all
jobs:
trigger-worflow:
name: Trigger Workflow
runs-on: ubuntu-latest
if: ${{ github.event.commits[0].author.username == 'dependabot[bot]' && github.repository == 'spring-projects/spring-security' }}
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4
- id: trigger
env:
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
run: gh workflow run dependabot-auto-merge-forward.yml -r main

View File

@ -18,7 +18,7 @@ jobs:
matrix: matrix:
branch: [ '5.8.x', '6.2.x', '6.3.x', 'main' ] branch: [ '5.8.x', '6.2.x', '6.3.x', 'main' ]
steps: steps:
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@c2038265125ec6f305a4a041d892ee44c156a754 - uses: spring-io/spring-doc-actions/update-antora-spring-ui@e28269199d1d27975cf7f65e16d6095c555b3cd0
name: Update name: Update
with: with:
docs-branch: ${{ matrix.branch }} docs-branch: ${{ matrix.branch }}
@ -28,7 +28,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
name: Update on docs-build name: Update on docs-build
steps: steps:
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@c2038265125ec6f305a4a041d892ee44c156a754 - uses: spring-io/spring-doc-actions/update-antora-spring-ui@e28269199d1d27975cf7f65e16d6095c555b3cd0
name: Update name: Update
with: with:
docs-branch: 'docs-build' docs-branch: 'docs-build'

View File

@ -1,36 +0,0 @@
name: Update dependabot.yml
on:
workflow_dispatch:
permissions:
contents: read
jobs:
get-supported-branches:
uses: spring-io/spring-security-release-tools/.github/workflows/retrieve-spring-supported-versions.yml@actions-v1
with:
project: spring-security
type: oss
repository_name: spring-projects/spring-security
main:
runs-on: ubuntu-latest
needs: [get-supported-branches]
if: ${{ (github.repository == 'spring-projects/spring-security') && (github.ref == 'refs/heads/main') }}
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- uses: spring-io/spring-security-release-tools/.github/actions/generate-dependabot-yml@actions-v1
name: Update dependabot.yml
with:
gradle-branches: ${{ needs.get-supported-branches.outputs.supported_versions }},main
github-actions-branches: ${{ needs.get-supported-branches.outputs.supported_versions }},main,docs-build
gh-token: ${{ secrets.GITHUB_TOKEN }}
- uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: Update dependabot.yml

View File

@ -79,7 +79,10 @@ See https://github.com/spring-projects/spring-security/tree/main#building-from-s
The wiki pages https://github.com/spring-projects/spring-framework/wiki/Code-Style[Code Style] and https://github.com/spring-projects/spring-framework/wiki/IntelliJ-IDEA-Editor-Settings[IntelliJ IDEA Editor Settings] define the source file coding standards we use along with some IDEA editor settings we customize. The wiki pages https://github.com/spring-projects/spring-framework/wiki/Code-Style[Code Style] and https://github.com/spring-projects/spring-framework/wiki/IntelliJ-IDEA-Editor-Settings[IntelliJ IDEA Editor Settings] define the source file coding standards we use along with some IDEA editor settings we customize.
To format the code as well as check the style, run `./gradlew format check`. Additionally, since Streams are https://github.com/spring-projects/spring-security/issues/7154[much slower] than `for` loops, please use them judiciously.
The team may ask you to change to a `for` loop if the given code is along a hot path.
To format the code as well as check the style, run `./gradlew format && ./gradlew check`.
[[submit-a-pull-request]] [[submit-a-pull-request]]
=== Submit a Pull Request === Submit a Pull Request
@ -104,7 +107,7 @@ If this is for an issue, consider a branch name with the issue number, like `gh-
6. [[update-copyright]] In all files you edited, if the copyright header is of the form 2002-20xx, update the final copyright year to the current year. 6. [[update-copyright]] In all files you edited, if the copyright header is of the form 2002-20xx, update the final copyright year to the current year.
7. [[add-since]] If on `main`, add `@since` JavaDoc attributes to new public APIs that your PR adds 7. [[add-since]] If on `main`, add `@since` JavaDoc attributes to new public APIs that your PR adds
8. [[change-rnc]] If you are updating the XSD, please instead update the RNC file and then run `./gradlew :spring-security-config:rncToXsd`. 8. [[change-rnc]] If you are updating the XSD, please instead update the RNC file and then run `./gradlew :spring-security-config:rncToXsd`.
9. [[format-code]] For each commit, build the code using `./gradlew format check`. 9. [[format-code]] For each commit, build the code using `./gradlew format && ./gradlew check`.
This command ensures the code meets most of <<code-style,the style guide>>; a notable exception is import order. This command ensures the code meets most of <<code-style,the style guide>>; a notable exception is import order.
10. [[commit-atomically]] Choose the granularity of your commits consciously and squash commits that represent 10. [[commit-atomically]] Choose the granularity of your commits consciously and squash commits that represent
multiple edits or corrections of the same logical change. multiple edits or corrections of the same logical change.

View File

@ -100,8 +100,8 @@ public class JdbcAclService implements AclService {
@Override @Override
public List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) { public List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {
Object[] args = { parentIdentity.getIdentifier().toString(), parentIdentity.getType() }; Object[] args = { parentIdentity.getIdentifier().toString(), parentIdentity.getType() };
List<ObjectIdentity> objects = this.jdbcOperations.query(this.findChildrenSql, args, List<ObjectIdentity> objects = this.jdbcOperations.query(this.findChildrenSql,
(rs, rowNum) -> mapObjectIdentityRow(rs)); (rs, rowNum) -> mapObjectIdentityRow(rs), args);
return (!objects.isEmpty()) ? objects : null; return (!objects.isEmpty()) ? objects : null;
} }

View File

@ -190,8 +190,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
* @return the primary key or null if not found * @return the primary key or null if not found
*/ */
protected Long createOrRetrieveClassPrimaryKey(String type, boolean allowCreate, Class idType) { protected Long createOrRetrieveClassPrimaryKey(String type, boolean allowCreate, Class idType) {
List<Long> classIds = this.jdbcOperations.queryForList(this.selectClassPrimaryKey, new Object[] { type }, List<Long> classIds = this.jdbcOperations.queryForList(this.selectClassPrimaryKey, Long.class, type);
Long.class);
if (!classIds.isEmpty()) { if (!classIds.isEmpty()) {
return classIds.get(0); return classIds.get(0);
@ -242,8 +241,8 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
* @return the primary key or null if not found * @return the primary key or null if not found
*/ */
protected Long createOrRetrieveSidPrimaryKey(String sidName, boolean sidIsPrincipal, boolean allowCreate) { protected Long createOrRetrieveSidPrimaryKey(String sidName, boolean sidIsPrincipal, boolean allowCreate) {
List<Long> sidIds = this.jdbcOperations.queryForList(this.selectSidPrimaryKey, List<Long> sidIds = this.jdbcOperations.queryForList(this.selectSidPrimaryKey, Long.class, sidIsPrincipal,
new Object[] { sidIsPrincipal, sidName }, Long.class); sidName);
if (!sidIds.isEmpty()) { if (!sidIds.isEmpty()) {
return sidIds.get(0); return sidIds.get(0);
} }

View File

@ -109,7 +109,7 @@ public class JdbcAclServiceTests {
List<ObjectIdentity> result = new ArrayList<>(); List<ObjectIdentity> result = new ArrayList<>();
result.add(new ObjectIdentityImpl(Object.class, "5577")); result.add(new ObjectIdentityImpl(Object.class, "5577"));
Object[] args = { "1", "org.springframework.security.acls.jdbc.JdbcAclServiceTests$MockLongIdDomainObject" }; Object[] args = { "1", "org.springframework.security.acls.jdbc.JdbcAclServiceTests$MockLongIdDomainObject" };
given(this.jdbcOperations.query(anyString(), eq(args), any(RowMapper.class))).willReturn(result); given(this.jdbcOperations.query(anyString(), any(RowMapper.class), eq(args))).willReturn(result);
ObjectIdentity objectIdentity = new ObjectIdentityImpl(MockLongIdDomainObject.class, 1L); ObjectIdentity objectIdentity = new ObjectIdentityImpl(MockLongIdDomainObject.class, 1L);
List<ObjectIdentity> objectIdentities = this.aclService.findChildren(objectIdentity); List<ObjectIdentity> objectIdentities = this.aclService.findChildren(objectIdentity);
assertThat(objectIdentities).hasSize(1); assertThat(objectIdentities).hasSize(1);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -144,7 +144,7 @@ public class PreAuthorizeAspectTests {
protected void protectedMethod() { protected void protectedMethod() {
} }
@PreAuthorize("hasRole('X')") @PreAuthorize("hasRole('A')")
void publicCallsPrivate() { void publicCallsPrivate() {
privateMethod(); privateMethod();
} }

View File

@ -1,5 +1,6 @@
plugins { plugins {
id "java-gradle-plugin" id "java-gradle-plugin"
id "groovy-gradle-plugin"
id "java" id "java"
id "groovy" id "groovy"
} }
@ -76,6 +77,7 @@ dependencies {
implementation libs.com.github.spullara.mustache.java.compiler implementation libs.com.github.spullara.mustache.java.compiler
implementation libs.io.spring.javaformat.spring.javaformat.gradle.plugin implementation libs.io.spring.javaformat.spring.javaformat.gradle.plugin
implementation libs.io.spring.nohttp.nohttp.gradle implementation libs.io.spring.nohttp.nohttp.gradle
implementation libs.org.jetbrains.kotlin.kotlin.gradle.plugin
implementation (libs.net.sourceforge.htmlunit) { implementation (libs.net.sourceforge.htmlunit) {
exclude group: 'org.eclipse.jetty.websocket', module: 'websocket-client' exclude group: 'org.eclipse.jetty.websocket', module: 'websocket-client'
} }

View File

@ -61,7 +61,7 @@ public class ManagementConfigurationPlugin implements Plugin<Project> {
PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class); PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class);
publishing.getPublications().withType(MavenPublication.class, (mavenPublication -> { publishing.getPublications().withType(MavenPublication.class, (mavenPublication -> {
mavenPublication.versionMapping((versions) -> mavenPublication.versionMapping((versions) ->
versions.allVariants(versionMapping -> versionMapping.fromResolutionResult()) versions.allVariants((versionMapping) -> versionMapping.fromResolutionResult())
); );
})); }));
}); });

View File

@ -80,6 +80,11 @@ class RepositoryConventionPlugin implements Plugin<Project> {
} }
url = 'https://repo.spring.io/release/' url = 'https://repo.spring.io/release/'
} }
forceMavenRepositories.findAll { it.startsWith('https://') || it.startsWith('file://') }.each { mavenUrl ->
maven {
url mavenUrl
}
}
} }
} }

View File

@ -32,10 +32,13 @@ public class SchemaZipPlugin implements Plugin<Project> {
for (def key : schemas.keySet()) { for (def key : schemas.keySet()) {
def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1') def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1')
assert shortName != key assert shortName != key
def schemaResourceName = schemas.get(key)
File xsdFile = module.sourceSets.main.resources.find { File xsdFile = module.sourceSets.main.resources.find {
it.path.endsWith(schemas.get(key)) it.path.endsWith(schemaResourceName)
}
if (xsdFile == null) {
throw new IllegalStateException("Could not find schema file for resource name " + schemaResourceName + " in src/main/resources")
} }
assert xsdFile != null
schemaZip.into (shortName) { schemaZip.into (shortName) {
duplicatesStrategy 'exclude' duplicatesStrategy 'exclude'
from xsdFile.path from xsdFile.path

View File

@ -0,0 +1,17 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id 'kotlin'
}
project.plugins.withId("org.jetbrains.kotlin.jvm", (kotlinProject) -> {
project.tasks.withType(KotlinCompile).configureEach {
kotlinOptions {
languageVersion = '2.2'
apiVersion = '2.2'
freeCompilerArgs = ["-Xjsr305=strict", "-Xsuppress-version-warnings"]
jvmTarget = '17'
}
}
})

View File

@ -81,9 +81,6 @@ public class CheckClasspathForProhibitedDependencies extends DefaultTask {
if (group.startsWith("javax")) { if (group.startsWith("javax")) {
return true; return true;
} }
if (group.equals("commons-logging")) {
return true;
}
if (group.equals("org.slf4j") && id.getName().equals("jcl-over-slf4j")) { if (group.equals("org.slf4j") && id.getName().equals("jcl-over-slf4j")) {
return true; return true;
} }

View File

@ -46,7 +46,7 @@ public class CheckExpectedBranchVersionPlugin implements Plugin<Project> {
task.setDescription("Check if the project version matches the branch version"); task.setDescription("Check if the project version matches the branch version");
task.onlyIf("skipCheckExpectedBranchVersion property is false or not present", CheckExpectedBranchVersionPlugin::skipPropertyFalseOrNotPresent); task.onlyIf("skipCheckExpectedBranchVersion property is false or not present", CheckExpectedBranchVersionPlugin::skipPropertyFalseOrNotPresent);
task.getVersion().convention(project.provider(() -> project.getVersion().toString())); task.getVersion().convention(project.provider(() -> project.getVersion().toString()));
task.getBranchName().convention(project.getProviders().exec(execSpec -> execSpec.setCommandLine("git", "symbolic-ref", "--short", "HEAD")).getStandardOutput().getAsText()); task.getBranchName().convention(project.getProviders().exec((execSpec) -> execSpec.setCommandLine("git", "symbolic-ref", "--short", "HEAD")).getStandardOutput().getAsText());
task.getOutputFile().convention(project.getLayout().getBuildDirectory().file("check-expected-branch-version")); task.getOutputFile().convention(project.getLayout().getBuildDirectory().file("check-expected-branch-version"));
}); });
project.getTasks().named(JavaBasePlugin.CHECK_TASK_NAME, checkTask -> checkTask.dependsOn(checkExpectedBranchVersionTask)); project.getTasks().named(JavaBasePlugin.CHECK_TASK_NAME, checkTask -> checkTask.dependsOn(checkExpectedBranchVersionTask));

View File

@ -4,7 +4,7 @@ import trang.RncToXsd
apply plugin: 'io.spring.convention.spring-module' apply plugin: 'io.spring.convention.spring-module'
apply plugin: 'trang' apply plugin: 'trang'
apply plugin: 'kotlin' apply plugin: 'security-kotlin'
configurations { configurations {
opensaml5 { opensaml5 {
@ -78,12 +78,6 @@ dependencies {
exclude group: 'commons-logging', module: 'commons-logging' exclude group: 'commons-logging', module: 'commons-logging'
exclude group: 'xml-apis', module: 'xml-apis' exclude group: 'xml-apis', module: 'xml-apis'
} }
testImplementation "org.apache.directory.server:apacheds-core"
testImplementation "org.apache.directory.server:apacheds-core-entry"
testImplementation "org.apache.directory.server:apacheds-protocol-shared"
testImplementation "org.apache.directory.server:apacheds-protocol-ldap"
testImplementation "org.apache.directory.server:apacheds-server-jndi"
testImplementation 'org.apache.directory.shared:shared-ldap'
testImplementation "com.unboundid:unboundid-ldapsdk" testImplementation "com.unboundid:unboundid-ldapsdk"
testImplementation 'jakarta.persistence:jakarta.persistence-api' testImplementation 'jakarta.persistence:jakarta.persistence-api'
testImplementation "org.hibernate.orm:hibernate-core" testImplementation "org.hibernate.orm:hibernate-core"
@ -127,6 +121,7 @@ dependencies {
testRuntimeOnly 'org.hsqldb:hsqldb' testRuntimeOnly 'org.hsqldb:hsqldb'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher' testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
} }
def rncToXsd = tasks.named('rncToXsd', RncToXsd) def rncToXsd = tasks.named('rncToXsd', RncToXsd)
@ -158,15 +153,6 @@ tasks.named('sourcesJar', Jar).configure {
} }
} }
tasks.withType(KotlinCompile).configureEach {
kotlinOptions {
languageVersion = "1.7"
apiVersion = "1.7"
freeCompilerArgs = ["-Xjsr305=strict", "-Xsuppress-version-warnings"]
jvmTarget = "17"
}
}
configure(project.tasks.withType(Test)) { configure(project.tasks.withType(Test)) {
doFirst { doFirst {
systemProperties['springSecurityVersion'] = version systemProperties['springSecurityVersion'] = version

View File

@ -44,7 +44,7 @@ import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMap
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource; import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider; import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.security.ldap.server.ApacheDSContainer; import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator; import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
@ -326,11 +326,11 @@ public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
abstract static class BaseLdapServerConfig extends BaseLdapProviderConfig { abstract static class BaseLdapServerConfig extends BaseLdapProviderConfig {
@Bean @Bean
ApacheDSContainer ldapServer() throws Exception { UnboundIdContainer ldapServer() throws Exception {
ApacheDSContainer apacheDSContainer = new ApacheDSContainer("dc=springframework,dc=org", UnboundIdContainer unboundIdContainer = new UnboundIdContainer("dc=springframework,dc=org",
"classpath:/test-server.ldif"); "classpath:/test-server.ldif");
apacheDSContainer.setPort(getPort()); unboundIdContainer.setPort(getPort());
return apacheDSContainer; return unboundIdContainer;
} }
} }

View File

@ -74,8 +74,7 @@ public class HelloRSocketITests {
// @formatter:off // @formatter:off
this.server = RSocketServer.create() this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY) .payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> .interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
registry.forSocketAcceptor(this.interceptor)
) )
.acceptor(this.handler.responder()) .acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0)) .bind(TcpServerTransport.create("localhost", 0))

View File

@ -87,8 +87,7 @@ public class HelloRSocketObservationITests {
// @formatter:off // @formatter:off
this.server = RSocketServer.create() this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY) .payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> .interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
registry.forSocketAcceptor(this.interceptor)
) )
.acceptor(this.handler.responder()) .acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0)) .bind(TcpServerTransport.create("localhost", 0))

View File

@ -74,8 +74,7 @@ public class HelloRSocketWithWebFluxITests {
// @formatter:off // @formatter:off
this.server = RSocketServer.create() this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY) .payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> .interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
registry.forSocketAcceptor(this.interceptor)
) )
.acceptor(this.handler.responder()) .acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0)) .bind(TcpServerTransport.create("localhost", 0))

View File

@ -86,8 +86,7 @@ public class JwtITests {
// @formatter:off // @formatter:off
this.server = RSocketServer.create() this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY) .payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> .interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
registry.forSocketAcceptor(this.interceptor)
) )
.acceptor(this.handler.responder()) .acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0)) .bind(TcpServerTransport.create("localhost", 0))

View File

@ -81,8 +81,7 @@ public class RSocketMessageHandlerConnectionITests {
// @formatter:off // @formatter:off
this.server = RSocketServer.create() this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY) .payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> .interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
registry.forSocketAcceptor(this.interceptor)
) )
.acceptor(this.handler.responder()) .acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0)) .bind(TcpServerTransport.create("localhost", 0))

View File

@ -79,8 +79,7 @@ public class RSocketMessageHandlerITests {
// @formatter:off // @formatter:off
this.server = RSocketServer.create() this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY) .payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> .interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
registry.forSocketAcceptor(this.interceptor)
) )
.acceptor(this.handler.responder()) .acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0)) .bind(TcpServerTransport.create("localhost", 0))

View File

@ -79,8 +79,7 @@ public class SimpleAuthenticationITests {
// @formatter:off // @formatter:off
this.server = RSocketServer.create() this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY) .payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> .interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
registry.forSocketAcceptor(this.interceptor)
) )
.acceptor(this.handler.responder()) .acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0)) .bind(TcpServerTransport.create("localhost", 0))

View File

@ -43,7 +43,7 @@ import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMap
import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource; import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.server.ApacheDSContainer; import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator; import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator; import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper; import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
@ -226,18 +226,18 @@ public class LdapBindAuthenticationManagerFactoryITests {
@EnableWebSecurity @EnableWebSecurity
abstract static class BaseLdapServerConfig implements DisposableBean { abstract static class BaseLdapServerConfig implements DisposableBean {
private ApacheDSContainer container; private UnboundIdContainer container;
@Bean @Bean
ApacheDSContainer ldapServer() throws Exception { UnboundIdContainer ldapServer() {
this.container = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif"); this.container = new UnboundIdContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
this.container.setPort(0); this.container.setPort(0);
return this.container; return this.container;
} }
@Bean @Bean
BaseLdapPathContextSource contextSource(ApacheDSContainer container) { BaseLdapPathContextSource contextSource(UnboundIdContainer container) {
int port = container.getLocalPort(); int port = container.getPort();
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org"); return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org");
} }

View File

@ -31,7 +31,7 @@ import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource; import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.server.ApacheDSContainer; import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
@ -93,18 +93,18 @@ public class LdapPasswordComparisonAuthenticationManagerFactoryITests {
@EnableWebSecurity @EnableWebSecurity
abstract static class BaseLdapServerConfig implements DisposableBean { abstract static class BaseLdapServerConfig implements DisposableBean {
private ApacheDSContainer container; private UnboundIdContainer container;
@Bean @Bean
ApacheDSContainer ldapServer() throws Exception { UnboundIdContainer ldapServer() {
this.container = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif"); this.container = new UnboundIdContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
this.container.setPort(0); this.container.setPort(0);
return this.container; return this.container;
} }
@Bean @Bean
BaseLdapPathContextSource contextSource(ApacheDSContainer container) { BaseLdapPathContextSource contextSource(UnboundIdContainer container) {
int port = container.getLocalPort(); int port = container.getPort();
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org"); return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org");
} }

View File

@ -56,7 +56,7 @@ public class LdapProviderBeanDefinitionParserTests {
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
AuthenticationManager.class); AuthenticationManager.class);
Authentication auth = authenticationManager Authentication auth = authenticationManager
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("ben", "benspassword")); .authenticate(UsernamePasswordAuthenticationToken.unauthenticated("otherben", "otherbenspassword"));
UserDetails ben = (UserDetails) auth.getPrincipal(); UserDetails ben = (UserDetails) auth.getPrincipal();
assertThat(ben.getAuthorities()).hasSize(3); assertThat(ben.getAuthorities()).hasSize(3);
} }
@ -127,6 +127,27 @@ public class LdapProviderBeanDefinitionParserTests {
assertThat(auth).isNotNull(); assertThat(auth).isNotNull();
} }
@Test
public void supportsShaPasswordEncoder() {
this.appCtx = new InMemoryXmlApplicationContext("""
<ldap-server ldif='classpath:test-server.ldif' port='0'/>
<authentication-manager>
<ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>
<password-compare>
<password-encoder ref='pe' />
</password-compare>
</ldap-authentication-provider>
</authentication-manager>
<b:bean id='pe' class='org.springframework.security.crypto.password.LdapShaPasswordEncoder' />
""");
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
AuthenticationManager.class);
Authentication auth = authenticationManager
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("ben", "benspassword"));
assertThat(auth).isNotNull();
}
@Test @Test
public void inetOrgContextMapperIsSupported() { public void inetOrgContextMapperIsSupported() {
this.appCtx = new InMemoryXmlApplicationContext( this.appCtx = new InMemoryXmlApplicationContext(

View File

@ -26,7 +26,7 @@ import org.springframework.ldap.core.LdapTemplate;
import org.springframework.security.config.BeanIds; import org.springframework.security.config.BeanIds;
import org.springframework.security.config.util.InMemoryXmlApplicationContext; import org.springframework.security.config.util.InMemoryXmlApplicationContext;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource; import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.server.ApacheDSContainer; import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -92,9 +92,9 @@ public class LdapServerBeanDefinitionParserTests {
@Test @Test
public void defaultLdifFileIsSuccessful() { public void defaultLdifFileIsSuccessful() {
this.appCtx = new InMemoryXmlApplicationContext("<ldap-server/>"); this.appCtx = new InMemoryXmlApplicationContext("<ldap-server/>");
ApacheDSContainer dsContainer = this.appCtx.getBean(ApacheDSContainer.class); UnboundIdContainer dsContainer = this.appCtx.getBean(UnboundIdContainer.class);
assertThat(ReflectionTestUtils.getField(dsContainer, "ldifResources")).isEqualTo("classpath*:*.ldif"); assertThat(ReflectionTestUtils.getField(dsContainer, "ldif")).isEqualTo("classpath*:*.ldif");
} }
private int getDefaultPort() throws IOException { private int getDefaultPort() throws IOException {

View File

@ -7,7 +7,6 @@
<logger name="org.springframework.security" level="${sec.log.level:-WARN}"/> <logger name="org.springframework.security" level="${sec.log.level:-WARN}"/>
<logger name="org.apache.directory" level="ERROR"/>
<logger name="JdbmTable" level="INFO"/> <logger name="JdbmTable" level="INFO"/>
<logger name="JdbmIndex" level="INFO"/> <logger name="JdbmIndex" level="INFO"/>
<logger name="org.apache.mina" level="WARN"/> <logger name="org.apache.mina" level="WARN"/>

View File

@ -54,8 +54,6 @@ public abstract class BeanIds {
public static final String METHOD_SECURITY_METADATA_SOURCE_ADVISOR = PREFIX + "methodSecurityMetadataSourceAdvisor"; public static final String METHOD_SECURITY_METADATA_SOURCE_ADVISOR = PREFIX + "methodSecurityMetadataSourceAdvisor";
public static final String EMBEDDED_APACHE_DS = PREFIX + "apacheDirectoryServerContainer";
public static final String EMBEDDED_UNBOUNDID = PREFIX + "unboundidServerContainer"; public static final String EMBEDDED_UNBOUNDID = PREFIX + "unboundidServerContainer";
public static final String CONTEXT_SOURCE = PREFIX + "securityContextSource"; public static final String CONTEXT_SOURCE = PREFIX + "securityContextSource";

View File

@ -96,7 +96,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
pc.getReaderContext() pc.getReaderContext()
.fatal("You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or " .fatal("You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or "
+ "spring-security-3.1.xsd schema or spring-security-3.2.xsd schema or spring-security-4.0.xsd schema " + "spring-security-3.1.xsd schema or spring-security-3.2.xsd schema or spring-security-4.0.xsd schema "
+ "with Spring Security 6.5. Please update your schema declarations to the 6.5 schema.", + "with Spring Security 7.0. Please update your schema declarations to the 7.0 schema.",
element); element);
} }
String name = pc.getDelegate().getLocalName(element); String name = pc.getDelegate().getLocalName(element);
@ -221,7 +221,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
private boolean matchesVersionInternal(Element element) { private boolean matchesVersionInternal(Element element) {
String schemaLocation = element.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation"); String schemaLocation = element.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
return schemaLocation.matches("(?m).*spring-security-6\\.5.*.xsd.*") return schemaLocation.matches("(?m).*spring-security-7\\.0.*.xsd.*")
|| schemaLocation.matches("(?m).*spring-security.xsd.*") || schemaLocation.matches("(?m).*spring-security.xsd.*")
|| !schemaLocation.matches("(?m).*spring-security.*"); || !schemaLocation.matches("(?m).*spring-security.*");
} }

View File

@ -50,17 +50,6 @@ public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>>
public void configure(B builder) throws Exception { public void configure(B builder) throws Exception {
} }
/**
* Return the {@link SecurityBuilder} when done using the {@link SecurityConfigurer}.
* This is useful for method chaining.
* @return the {@link SecurityBuilder} for further customizations
* @deprecated For removal in 7.0. Use the lambda based configuration instead.
*/
@Deprecated(since = "6.1", forRemoval = true)
public B and() {
return getBuilder();
}
/** /**
* Gets the {@link SecurityBuilder}. Cannot be null. * Gets the {@link SecurityBuilder}. Cannot be null.
* @return the {@link SecurityBuilder} * @return the {@link SecurityBuilder}

View File

@ -67,9 +67,7 @@ public class AuthenticationManagerBuilder
/** /**
* Creates a new instance * Creates a new instance
* @param objectPostProcessor the * @param objectPostProcessor the {@link ObjectPostProcessor} instance to use.
* {@link org.springframework.security.config.annotation.ObjectPostProcessor} instance
* to use.
*/ */
public AuthenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor) { public AuthenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
super(objectPostProcessor, true); super(objectPostProcessor, true);

View File

@ -25,7 +25,6 @@ import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.ObjectPostProcessor; import org.springframework.security.config.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter; import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder; import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
import org.springframework.security.config.annotation.web.configurers.ChannelSecurityConfigurer;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.NoOpPasswordEncoder;
@ -38,7 +37,6 @@ import org.springframework.security.ldap.authentication.LdapAuthenticator;
import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator; import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch; import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import org.springframework.security.ldap.search.LdapUserSearch; import org.springframework.security.ldap.search.LdapUserSearch;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.security.ldap.server.UnboundIdContainer; import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator; import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper; import org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper;
@ -61,12 +59,8 @@ import org.springframework.util.ClassUtils;
public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuilder<B>> public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuilder<B>>
extends SecurityConfigurerAdapter<AuthenticationManager, B> { extends SecurityConfigurerAdapter<AuthenticationManager, B> {
private static final String APACHEDS_CLASSNAME = "org.apache.directory.server.core.DefaultDirectoryService";
private static final String UNBOUNDID_CLASSNAME = "com.unboundid.ldap.listener.InMemoryDirectoryServer"; private static final String UNBOUNDID_CLASSNAME = "com.unboundid.ldap.listener.InMemoryDirectoryServer";
private static final boolean apacheDsPresent;
private static final boolean unboundIdPresent; private static final boolean unboundIdPresent;
private String groupRoleAttribute = "cn"; private String groupRoleAttribute = "cn";
@ -101,7 +95,6 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
static { static {
ClassLoader classLoader = LdapAuthenticationProviderConfigurer.class.getClassLoader(); ClassLoader classLoader = LdapAuthenticationProviderConfigurer.class.getClassLoader();
apacheDsPresent = ClassUtils.isPresent(APACHEDS_CLASSNAME, classLoader);
unboundIdPresent = ClassUtils.isPresent(UNBOUNDID_CLASSNAME, classLoader); unboundIdPresent = ClassUtils.isPresent(UNBOUNDID_CLASSNAME, classLoader);
} }
@ -133,7 +126,7 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
/** /**
* Adds an {@link ObjectPostProcessor} for this class. * Adds an {@link ObjectPostProcessor} for this class.
* @param objectPostProcessor * @param objectPostProcessor
* @return the {@link ChannelSecurityConfigurer} for further customizations * @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
*/ */
public LdapAuthenticationProviderConfigurer<B> withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) { public LdapAuthenticationProviderConfigurer<B> withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor); addObjectPostProcessor(objectPostProcessor);
@ -393,6 +386,10 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
return this; return this;
} }
public B and() {
return getBuilder();
}
@Override @Override
public void configure(B builder) throws Exception { public void configure(B builder) throws Exception {
LdapAuthenticationProvider provider = postProcess(build()); LdapAuthenticationProvider provider = postProcess(build());
@ -468,8 +465,6 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
*/ */
public final class ContextSourceBuilder { public final class ContextSourceBuilder {
private static final String APACHEDS_CLASSNAME = "org.apache.directory.server.core.DefaultDirectoryService";
private static final String UNBOUNDID_CLASSNAME = "com.unboundid.ldap.listener.InMemoryDirectoryServer"; private static final String UNBOUNDID_CLASSNAME = "com.unboundid.ldap.listener.InMemoryDirectoryServer";
private static final int DEFAULT_PORT = 33389; private static final int DEFAULT_PORT = 33389;
@ -585,14 +580,8 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
return contextSource; return contextSource;
} }
private void startEmbeddedLdapServer() throws Exception { private void startEmbeddedLdapServer() {
if (apacheDsPresent) { if (unboundIdPresent) {
ApacheDSContainer apacheDsContainer = new ApacheDSContainer(this.root, this.ldif);
apacheDsContainer.setPort(getPort());
postProcess(apacheDsContainer);
this.port = apacheDsContainer.getLocalPort();
}
else if (unboundIdPresent) {
UnboundIdContainer unboundIdContainer = new UnboundIdContainer(this.root, this.ldif); UnboundIdContainer unboundIdContainer = new UnboundIdContainer(this.root, this.ldif);
unboundIdContainer.setPort(getPort()); unboundIdContainer.setPort(getPort());
postProcess(unboundIdContainer); postProcess(unboundIdContainer);

View File

@ -41,4 +41,8 @@ public class InMemoryUserDetailsManagerConfigurer<B extends ProviderManagerBuild
super(new InMemoryUserDetailsManager(new ArrayList<>())); super(new InMemoryUserDetailsManager(new ArrayList<>()));
} }
public B and() {
return getBuilder();
}
} }

View File

@ -17,6 +17,7 @@
package org.springframework.security.config.annotation.method.configuration; package org.springframework.security.config.annotation.method.configuration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInterceptor;
@ -31,6 +32,7 @@ import org.springframework.security.aot.hint.SecurityHintsRegistrar;
import org.springframework.security.authorization.AuthorizationProxyFactory; import org.springframework.security.authorization.AuthorizationProxyFactory;
import org.springframework.security.authorization.method.AuthorizationAdvisor; import org.springframework.security.authorization.method.AuthorizationAdvisor;
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory; import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory.TargetVisitor;
import org.springframework.security.authorization.method.AuthorizeReturnObjectMethodInterceptor; import org.springframework.security.authorization.method.AuthorizeReturnObjectMethodInterceptor;
import org.springframework.security.config.Customizer; import org.springframework.security.config.Customizer;
@ -40,21 +42,23 @@ final class AuthorizationProxyConfiguration implements AopInfrastructureBean {
@Bean @Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE) @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static AuthorizationAdvisorProxyFactory authorizationProxyFactory( static AuthorizationAdvisorProxyFactory authorizationProxyFactory(
ObjectProvider<AuthorizationAdvisor> authorizationAdvisors, ObjectProvider<TargetVisitor> targetVisitors,
ObjectProvider<Customizer<AuthorizationAdvisorProxyFactory>> customizers) { ObjectProvider<Customizer<AuthorizationAdvisorProxyFactory>> customizers) {
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory(new ArrayList<>()); List<AuthorizationAdvisor> advisors = new ArrayList<>();
authorizationAdvisors.forEach(advisors::add);
List<TargetVisitor> visitors = new ArrayList<>();
targetVisitors.orderedStream().forEach(visitors::add);
visitors.add(TargetVisitor.defaults());
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory(advisors);
factory.setTargetVisitor(TargetVisitor.of(visitors.toArray(TargetVisitor[]::new)));
customizers.forEach((c) -> c.customize(factory)); customizers.forEach((c) -> c.customize(factory));
return factory; return factory;
} }
@Bean @Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE) @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static MethodInterceptor authorizeReturnObjectMethodInterceptor(ObjectProvider<AuthorizationAdvisor> provider, static MethodInterceptor authorizeReturnObjectMethodInterceptor() {
AuthorizationAdvisorProxyFactory authorizationProxyFactory) { return new AuthorizeReturnObjectMethodInterceptor();
provider.forEach(authorizationProxyFactory::addAdvisor);
AuthorizeReturnObjectMethodInterceptor interceptor = new AuthorizeReturnObjectMethodInterceptor(
authorizationProxyFactory);
authorizationProxyFactory.addAdvisor(interceptor);
return interceptor;
} }
@Bean @Bean

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,13 +16,22 @@
package org.springframework.security.config.annotation.method.configuration; package org.springframework.security.config.annotation.method.configuration;
import java.util.List;
import org.springframework.aop.framework.AopInfrastructureBean; import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role; import org.springframework.context.annotation.Role;
import org.springframework.core.Ordered;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.SliceImpl;
import org.springframework.data.geo.GeoPage;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.GeoResults;
import org.springframework.security.aot.hint.SecurityHintsRegistrar; import org.springframework.security.aot.hint.SecurityHintsRegistrar;
import org.springframework.security.authorization.AuthorizationProxyFactory; import org.springframework.security.authorization.AuthorizationProxyFactory;
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;
import org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar; import org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar;
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ -34,4 +43,45 @@ final class AuthorizationProxyDataConfiguration implements AopInfrastructureBean
return new AuthorizeReturnObjectDataHintsRegistrar(proxyFactory); return new AuthorizeReturnObjectDataHintsRegistrar(proxyFactory);
} }
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
DataTargetVisitor dataTargetVisitor() {
return new DataTargetVisitor();
}
private static final class DataTargetVisitor implements AuthorizationAdvisorProxyFactory.TargetVisitor, Ordered {
private static final int DEFAULT_ORDER = 200;
@Override
public Object visit(AuthorizationAdvisorProxyFactory proxyFactory, Object target) {
if (target instanceof GeoResults<?> geoResults) {
return new GeoResults<>(proxyFactory.proxy(geoResults.getContent()), geoResults.getAverageDistance());
}
if (target instanceof GeoResult<?> geoResult) {
return new GeoResult<>(proxyFactory.proxy(geoResult.getContent()), geoResult.getDistance());
}
if (target instanceof GeoPage<?> geoPage) {
GeoResults<?> results = new GeoResults<>(proxyFactory.proxy(geoPage.getContent()),
geoPage.getAverageDistance());
return new GeoPage<>(results, geoPage.getPageable(), geoPage.getTotalElements());
}
if (target instanceof PageImpl<?> page) {
List<?> content = proxyFactory.proxy(page.getContent());
return new PageImpl<>(content, page.getPageable(), page.getTotalElements());
}
if (target instanceof SliceImpl<?> slice) {
List<?> content = proxyFactory.proxy(slice.getContent());
return new SliceImpl<>(content, slice.getPageable(), slice.hasNext());
}
return null;
}
@Override
public int getOrder() {
return DEFAULT_ORDER;
}
}
} }

View File

@ -0,0 +1,114 @@
/*
* Copyright 2002-2025 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.config.annotation.method.configuration;
import java.util.List;
import java.util.Map;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.core.Ordered;
import org.springframework.http.HttpEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;
import org.springframework.security.web.util.ThrowableAnalyzer;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
@Configuration
class AuthorizationProxyWebConfiguration implements WebMvcConfigurer {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
AuthorizationAdvisorProxyFactory.TargetVisitor webTargetVisitor() {
return new WebTargetVisitor();
}
@Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
for (int i = 0; i < resolvers.size(); i++) {
HandlerExceptionResolver resolver = resolvers.get(i);
if (resolver instanceof DefaultHandlerExceptionResolver) {
resolvers.add(i, new AccessDeniedExceptionResolver());
return;
}
}
resolvers.add(new AccessDeniedExceptionResolver());
}
static class WebTargetVisitor implements AuthorizationAdvisorProxyFactory.TargetVisitor, Ordered {
private static final int DEFAULT_ORDER = 100;
@Override
public Object visit(AuthorizationAdvisorProxyFactory proxyFactory, Object target) {
if (target instanceof ResponseEntity<?> entity) {
return new ResponseEntity<>(proxyFactory.proxy(entity.getBody()), entity.getHeaders(),
entity.getStatusCode());
}
if (target instanceof HttpEntity<?> entity) {
return new HttpEntity<>(proxyFactory.proxy(entity.getBody()), entity.getHeaders());
}
if (target instanceof ModelAndView mav) {
View view = mav.getView();
String viewName = mav.getViewName();
Map<String, Object> model = proxyFactory.proxy(mav.getModel());
ModelAndView proxied = (view != null) ? new ModelAndView(view, model)
: new ModelAndView(viewName, model);
proxied.setStatus(mav.getStatus());
return proxied;
}
return null;
}
@Override
public int getOrder() {
return DEFAULT_ORDER;
}
}
static class AccessDeniedExceptionResolver implements HandlerExceptionResolver {
final ThrowableAnalyzer throwableAnalyzer = new ThrowableAnalyzer();
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);
Throwable accessDeniedException = this.throwableAnalyzer
.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
if (accessDeniedException != null) {
return new ModelAndView((model, req, res) -> {
throw ex;
});
}
return null;
}
}
}

View File

@ -1,72 +0,0 @@
/*
* Copyright 2002-2022 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.config.annotation.method.configuration;
import java.util.function.Supplier;
import io.micrometer.observation.ObservationRegistry;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.AuthorizationResult;
import org.springframework.security.authorization.ObservationAuthorizationManager;
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
import org.springframework.security.authorization.method.MethodInvocationResult;
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedHandler;
import org.springframework.security.core.Authentication;
import org.springframework.util.function.SingletonSupplier;
final class DeferringObservationAuthorizationManager<T>
implements AuthorizationManager<T>, MethodAuthorizationDeniedHandler {
private final Supplier<AuthorizationManager<T>> delegate;
private MethodAuthorizationDeniedHandler handler = new ThrowingMethodAuthorizationDeniedHandler();
DeferringObservationAuthorizationManager(ObjectProvider<ObservationRegistry> provider,
AuthorizationManager<T> delegate) {
this.delegate = SingletonSupplier.of(() -> {
ObservationRegistry registry = provider.getIfAvailable(() -> ObservationRegistry.NOOP);
if (registry.isNoop()) {
return delegate;
}
return new ObservationAuthorizationManager<>(registry, delegate);
});
if (delegate instanceof MethodAuthorizationDeniedHandler h) {
this.handler = h;
}
}
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, T object) {
return this.delegate.get().check(authentication, object);
}
@Override
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
return this.handler.handleDeniedInvocation(methodInvocation, authorizationResult);
}
@Override
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
AuthorizationResult authorizationResult) {
return this.handler.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);
}
}

View File

@ -1,73 +0,0 @@
/*
* Copyright 2002-2023 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.config.annotation.method.configuration;
import java.util.function.Supplier;
import io.micrometer.observation.ObservationRegistry;
import org.aopalliance.intercept.MethodInvocation;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationResult;
import org.springframework.security.authorization.ObservationReactiveAuthorizationManager;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
import org.springframework.security.authorization.method.MethodInvocationResult;
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedHandler;
import org.springframework.security.core.Authentication;
import org.springframework.util.function.SingletonSupplier;
final class DeferringObservationReactiveAuthorizationManager<T>
implements ReactiveAuthorizationManager<T>, MethodAuthorizationDeniedHandler {
private final Supplier<ReactiveAuthorizationManager<T>> delegate;
private MethodAuthorizationDeniedHandler handler = new ThrowingMethodAuthorizationDeniedHandler();
DeferringObservationReactiveAuthorizationManager(ObjectProvider<ObservationRegistry> provider,
ReactiveAuthorizationManager<T> delegate) {
this.delegate = SingletonSupplier.of(() -> {
ObservationRegistry registry = provider.getIfAvailable(() -> ObservationRegistry.NOOP);
if (registry.isNoop()) {
return delegate;
}
return new ObservationReactiveAuthorizationManager<>(registry, delegate);
});
if (delegate instanceof MethodAuthorizationDeniedHandler h) {
this.handler = h;
}
}
@Override
public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, T object) {
return this.delegate.get().check(authentication, object);
}
@Override
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
return this.handler.handleDeniedInvocation(methodInvocation, authorizationResult);
}
@Override
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
AuthorizationResult authorizationResult) {
return this.handler.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -41,6 +41,9 @@ final class MethodSecuritySelector implements ImportSelector {
private static final boolean isDataPresent = ClassUtils private static final boolean isDataPresent = ClassUtils
.isPresent("org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar", null); .isPresent("org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar", null);
private static final boolean isWebPresent = ClassUtils
.isPresent("org.springframework.web.servlet.DispatcherServlet", null);
private static final boolean isObservabilityPresent = ClassUtils private static final boolean isObservabilityPresent = ClassUtils
.isPresent("io.micrometer.observation.ObservationRegistry", null); .isPresent("io.micrometer.observation.ObservationRegistry", null);
@ -67,6 +70,9 @@ final class MethodSecuritySelector implements ImportSelector {
if (isDataPresent) { if (isDataPresent) {
imports.add(AuthorizationProxyDataConfiguration.class.getName()); imports.add(AuthorizationProxyDataConfiguration.class.getName());
} }
if (isWebPresent) {
imports.add(AuthorizationProxyWebConfiguration.class.getName());
}
if (isObservabilityPresent) { if (isObservabilityPresent) {
imports.add(MethodObservationConfiguration.class.getName()); imports.add(MethodObservationConfiguration.class.getName());
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -83,10 +83,11 @@ final class ReactiveAuthorizationManagerMethodSecurityConfiguration
private final AuthorizationManagerAfterReactiveMethodInterceptor postAuthorizeMethodInterceptor; private final AuthorizationManagerAfterReactiveMethodInterceptor postAuthorizeMethodInterceptor;
@Autowired(required = false) ReactiveAuthorizationManagerMethodSecurityConfiguration(
ReactiveAuthorizationManagerMethodSecurityConfiguration(MethodSecurityExpressionHandler expressionHandler, ObjectProvider<MethodSecurityExpressionHandler> expressionHandlers,
ObjectProvider<ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocation>>> preAuthorizePostProcessor, ObjectProvider<ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocation>>> preAuthorizePostProcessor,
ObjectProvider<ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocationResult>>> postAuthorizePostProcessor) { ObjectProvider<ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocationResult>>> postAuthorizePostProcessor) {
MethodSecurityExpressionHandler expressionHandler = expressionHandlers.getIfUnique();
if (expressionHandler != null) { if (expressionHandler != null) {
this.preFilterMethodInterceptor = new PreFilterAuthorizationReactiveMethodInterceptor(expressionHandler); this.preFilterMethodInterceptor = new PreFilterAuthorizationReactiveMethodInterceptor(expressionHandler);
this.preAuthorizeAuthorizationManager = new PreAuthorizeReactiveAuthorizationManager(expressionHandler); this.preAuthorizeAuthorizationManager = new PreAuthorizeReactiveAuthorizationManager(expressionHandler);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -38,6 +38,9 @@ class ReactiveMethodSecuritySelector implements ImportSelector {
private static final boolean isDataPresent = ClassUtils private static final boolean isDataPresent = ClassUtils
.isPresent("org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar", null); .isPresent("org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar", null);
private static final boolean isWebPresent = ClassUtils.isPresent("org.springframework.web.server.ServerWebExchange",
null);
private static final boolean isObservabilityPresent = ClassUtils private static final boolean isObservabilityPresent = ClassUtils
.isPresent("io.micrometer.observation.ObservationRegistry", null); .isPresent("io.micrometer.observation.ObservationRegistry", null);
@ -61,6 +64,9 @@ class ReactiveMethodSecuritySelector implements ImportSelector {
if (isDataPresent) { if (isDataPresent) {
imports.add(AuthorizationProxyDataConfiguration.class.getName()); imports.add(AuthorizationProxyDataConfiguration.class.getName());
} }
if (isWebPresent) {
imports.add(AuthorizationProxyWebConfiguration.class.getName());
}
if (isObservabilityPresent) { if (isObservabilityPresent) {
imports.add(ReactiveMethodObservationConfiguration.class.getName()); imports.add(ReactiveMethodObservationConfiguration.class.getName());
} }

View File

@ -33,12 +33,16 @@ public enum PayloadInterceptorOrder implements Ordered {
/** /**
* Where basic authentication is placed. * Where basic authentication is placed.
* @see RSocketSecurity#basicAuthentication(Customizer) * @see RSocketSecurity#basicAuthentication(Customizer)
* @deprecated please see {@link PayloadInterceptorOrder#AUTHENTICATION}
*/ */
@Deprecated
BASIC_AUTHENTICATION, BASIC_AUTHENTICATION,
/** /**
* Where JWT based authentication is performed. * Where JWT based authentication is performed.
* @see RSocketSecurity#jwt(Customizer) * @see RSocketSecurity#jwt(Customizer)
* @deprecated please see {@link PayloadInterceptorOrder#AUTHENTICATION}
*/ */
@Deprecated
JWT_AUTHENTICATION, JWT_AUTHENTICATION,
/** /**
* A generic placeholder for other types of authentication. * A generic placeholder for other types of authentication.

View File

@ -41,7 +41,6 @@ import org.springframework.http.HttpMethod;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.security.config.ObjectPostProcessor; import org.springframework.security.config.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.ServletRegistrationsSupport.RegistrationMapping; import org.springframework.security.config.annotation.web.ServletRegistrationsSupport.RegistrationMapping;
import org.springframework.security.config.annotation.web.configurers.AbstractConfigAttributeRequestMatcherRegistry;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.AnyRequestMatcher; import org.springframework.security.web.util.matcher.AnyRequestMatcher;
@ -170,7 +169,7 @@ public abstract class AbstractRequestMatcherRegistry<C> {
/** /**
* Associates a list of {@link RequestMatcher} instances with the * Associates a list of {@link RequestMatcher} instances with the
* {@link AbstractConfigAttributeRequestMatcherRegistry} * {@link AbstractRequestMatcherRegistry}
* @param requestMatchers the {@link RequestMatcher} instances * @param requestMatchers the {@link RequestMatcher} instances
* @return the object that is chained after creating the {@link RequestMatcher} * @return the object that is chained after creating the {@link RequestMatcher}
*/ */

View File

@ -35,7 +35,9 @@ import org.springframework.core.ResolvableType;
import org.springframework.security.access.PermissionEvaluator; import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.expression.SecurityExpressionHandler; import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy; import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.SingleResultAuthorizationManager;
import org.springframework.security.config.ObjectPostProcessor; import org.springframework.security.config.ObjectPostProcessor;
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder; import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
import org.springframework.security.config.annotation.SecurityBuilder; import org.springframework.security.config.annotation.SecurityBuilder;
@ -58,6 +60,8 @@ import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler; import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.access.intercept.AuthorizationFilter; import org.springframework.security.web.access.intercept.AuthorizationFilter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager;
import org.springframework.security.web.debug.DebugFilter; import org.springframework.security.web.debug.DebugFilter;
import org.springframework.security.web.firewall.CompositeRequestRejectedHandler; import org.springframework.security.web.firewall.CompositeRequestRejectedHandler;
import org.springframework.security.web.firewall.HttpFirewall; import org.springframework.security.web.firewall.HttpFirewall;
@ -65,6 +69,7 @@ import org.springframework.security.web.firewall.HttpStatusRequestRejectedHandle
import org.springframework.security.web.firewall.ObservationMarkingRequestRejectedHandler; import org.springframework.security.web.firewall.ObservationMarkingRequestRejectedHandler;
import org.springframework.security.web.firewall.RequestRejectedHandler; import org.springframework.security.web.firewall.RequestRejectedHandler;
import org.springframework.security.web.firewall.StrictHttpFirewall; import org.springframework.security.web.firewall.StrictHttpFirewall;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcherEntry; import org.springframework.security.web.util.matcher.RequestMatcherEntry;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -230,8 +235,8 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
/** /**
* Set the {@link WebInvocationPrivilegeEvaluator} to be used. If this is not * Set the {@link WebInvocationPrivilegeEvaluator} to be used. If this is not
* specified, then a {@link RequestMatcherDelegatingWebInvocationPrivilegeEvaluator} * specified, then a {@link AuthorizationManagerWebInvocationPrivilegeEvaluator} will
* will be created based on the list of {@link SecurityFilterChain}. * be created based on the list of {@link SecurityFilterChain}.
* @param privilegeEvaluator the {@link WebInvocationPrivilegeEvaluator} to use * @param privilegeEvaluator the {@link WebInvocationPrivilegeEvaluator} to use
* @return the {@link WebSecurity} for further customizations * @return the {@link WebSecurity} for further customizations
*/ */
@ -300,24 +305,33 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
+ ".addSecurityFilterChainBuilder directly"); + ".addSecurityFilterChainBuilder directly");
int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size(); int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize); List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries = new ArrayList<>(); RequestMatcherDelegatingAuthorizationManager.Builder builder = RequestMatcherDelegatingAuthorizationManager
.builder();
boolean mappings = false;
for (RequestMatcher ignoredRequest : this.ignoredRequests) { for (RequestMatcher ignoredRequest : this.ignoredRequests) {
WebSecurity.this.logger.warn("You are asking Spring Security to ignore " + ignoredRequest WebSecurity.this.logger.warn("You are asking Spring Security to ignore " + ignoredRequest
+ ". This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead."); + ". This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.");
SecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest); SecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest);
securityFilterChains.add(securityFilterChain); securityFilterChains.add(securityFilterChain);
requestMatcherPrivilegeEvaluatorsEntries builder.add(ignoredRequest, SingleResultAuthorizationManager.permitAll());
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain)); mappings = true;
} }
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) { for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build(); SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
securityFilterChains.add(securityFilterChain); securityFilterChains.add(securityFilterChain);
requestMatcherPrivilegeEvaluatorsEntries mappings = addAuthorizationManager(securityFilterChain, builder) || mappings;
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
} }
if (this.privilegeEvaluator == null) { if (this.privilegeEvaluator == null) {
AuthorizationManager<HttpServletRequest> authorizationManager = mappings ? builder.build()
: SingleResultAuthorizationManager.permitAll();
AuthorizationManagerWebInvocationPrivilegeEvaluator privilegeEvaluator = new AuthorizationManagerWebInvocationPrivilegeEvaluator(
authorizationManager);
privilegeEvaluator.setServletContext(this.servletContext);
if (this.privilegeEvaluatorRequestTransformer != null) {
privilegeEvaluator.setRequestTransformer(this.privilegeEvaluatorRequestTransformer);
}
this.privilegeEvaluator = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator( this.privilegeEvaluator = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
requestMatcherPrivilegeEvaluatorsEntries); List.of(new RequestMatcherEntry<>(AnyRequestMatcher.INSTANCE, List.of(privilegeEvaluator))));
} }
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains); FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (this.httpFirewall != null) { if (this.httpFirewall != null) {
@ -350,30 +364,33 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
return result; return result;
} }
private RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> getRequestMatcherPrivilegeEvaluatorsEntry( private boolean addAuthorizationManager(SecurityFilterChain securityFilterChain,
SecurityFilterChain securityFilterChain) { RequestMatcherDelegatingAuthorizationManager.Builder builder) {
List<WebInvocationPrivilegeEvaluator> privilegeEvaluators = new ArrayList<>(); boolean mappings = false;
for (Filter filter : securityFilterChain.getFilters()) { for (Filter filter : securityFilterChain.getFilters()) {
if (filter instanceof FilterSecurityInterceptor) { if (filter instanceof FilterSecurityInterceptor securityInterceptor) {
DefaultWebInvocationPrivilegeEvaluator defaultWebInvocationPrivilegeEvaluator = new DefaultWebInvocationPrivilegeEvaluator( DefaultWebInvocationPrivilegeEvaluator privilegeEvaluator = new DefaultWebInvocationPrivilegeEvaluator(
(FilterSecurityInterceptor) filter); securityInterceptor);
defaultWebInvocationPrivilegeEvaluator.setServletContext(this.servletContext); privilegeEvaluator.setServletContext(this.servletContext);
privilegeEvaluators.add(defaultWebInvocationPrivilegeEvaluator); AuthorizationManager<RequestAuthorizationContext> authorizationManager = (authentication, context) -> {
HttpServletRequest request = context.getRequest();
boolean result = privilegeEvaluator.isAllowed(request.getContextPath(), request.getRequestURI(),
request.getMethod(), authentication.get());
return new AuthorizationDecision(result);
};
builder.add(securityFilterChain::matches, authorizationManager);
mappings = true;
continue; continue;
} }
if (filter instanceof AuthorizationFilter) { if (filter instanceof AuthorizationFilter authorization) {
AuthorizationManager<HttpServletRequest> authorizationManager = ((AuthorizationFilter) filter) AuthorizationManager<HttpServletRequest> authorizationManager = authorization.getAuthorizationManager();
.getAuthorizationManager(); builder.add(securityFilterChain::matches,
AuthorizationManagerWebInvocationPrivilegeEvaluator evaluator = new AuthorizationManagerWebInvocationPrivilegeEvaluator( (authentication, context) -> (AuthorizationDecision) authorizationManager
authorizationManager); .authorize(authentication, context.getRequest()));
evaluator.setServletContext(this.servletContext); mappings = true;
if (this.privilegeEvaluatorRequestTransformer != null) {
evaluator.setRequestTransformer(this.privilegeEvaluatorRequestTransformer);
}
privilegeEvaluators.add(evaluator);
} }
} }
return new RequestMatcherEntry<>(securityFilterChain::matches, privilegeEvaluators); return mappings;
} }
@Override @Override

View File

@ -46,8 +46,9 @@ import org.springframework.security.web.SecurityFilterChain;
* *
* &#064;Bean * &#064;Bean
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { * public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
* http.authorizeHttpRequests().requestMatchers(&quot;/public/**&quot;).permitAll().anyRequest() * http.authorizeHttpRequests((authorize) -&gt; authorize
* .hasRole(&quot;USER&quot;).and() * .requestMatchers(&quot;/public/**&quot;).permitAll()
* .anyRequest().hasRole(&quot;USER&quot;))
* // Possibly more configuration ... * // Possibly more configuration ...
* .formLogin() // enable form based log in * .formLogin() // enable form based log in
* // set permitAll for all URLs associated with Form Login * // set permitAll for all URLs associated with Form Login

View File

@ -78,6 +78,8 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector"; private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
private static final String PATH_PATTERN_REQUEST_TRANSFORMER_BEAN_NAME = "pathPatternRequestTransformer";
private BeanResolver beanResolver; private BeanResolver beanResolver;
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
@ -119,17 +121,6 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
} }
} }
/**
* Used to ensure Spring MVC request matching is cached.
*
* Creates a {@link BeanDefinitionRegistryPostProcessor} that detects if a bean named
* HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME is defined. If so, it moves the
* AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME to another bean name
* and then adds a {@link CompositeFilter} that contains
* {@link HandlerMappingIntrospector#createCacheFilter()} and the original
* FilterChainProxy under the original Bean name.
* @return
*/
@Bean @Bean
static BeanDefinitionRegistryPostProcessor springSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor() { static BeanDefinitionRegistryPostProcessor springSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor() {
return new BeanDefinitionRegistryPostProcessor() { return new BeanDefinitionRegistryPostProcessor() {
@ -144,12 +135,15 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
} }
String hmiRequestTransformerBeanName = HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME + "RequestTransformer"; String hmiRequestTransformerBeanName = HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME + "RequestTransformer";
if (!registry.containsBeanDefinition(hmiRequestTransformerBeanName)) { if (!registry.containsBeanDefinition(PATH_PATTERN_REQUEST_TRANSFORMER_BEAN_NAME)
BeanDefinition hmiRequestTransformer = BeanDefinitionBuilder && !registry.containsBeanDefinition(hmiRequestTransformerBeanName)) {
.rootBeanDefinition(HandlerMappingIntrospectorRequestTransformer.class) if (!registry.containsBeanDefinition(hmiRequestTransformerBeanName)) {
.addConstructorArgReference(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME) BeanDefinition hmiRequestTransformer = BeanDefinitionBuilder
.getBeanDefinition(); .rootBeanDefinition(HandlerMappingIntrospectorRequestTransformer.class)
registry.registerBeanDefinition(hmiRequestTransformerBeanName, hmiRequestTransformer); .addConstructorArgReference(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)
.getBeanDefinition();
registry.registerBeanDefinition(hmiRequestTransformerBeanName, hmiRequestTransformer);
}
} }
BeanDefinition filterChainProxy = registry BeanDefinition filterChainProxy = registry
@ -178,7 +172,11 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
/** /**
* {@link FactoryBean} to defer creation of * {@link FactoryBean} to defer creation of
* {@link HandlerMappingIntrospector#createCacheFilter()} * {@link HandlerMappingIntrospector#createCacheFilter()}
*
* @deprecated see {@link WebSecurityConfiguration} for
* {@link org.springframework.web.util.pattern.PathPattern} replacement
*/ */
@Deprecated
static class HandlerMappingIntrospectorCacheFilterFactoryBean static class HandlerMappingIntrospectorCacheFilterFactoryBean
implements ApplicationContextAware, FactoryBean<Filter> { implements ApplicationContextAware, FactoryBean<Filter> {
@ -207,7 +205,11 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
* Extends {@link FilterChainProxy} to provide as much passivity as possible but * Extends {@link FilterChainProxy} to provide as much passivity as possible but
* delegates to {@link CompositeFilter} for * delegates to {@link CompositeFilter} for
* {@link #doFilter(ServletRequest, ServletResponse, FilterChain)}. * {@link #doFilter(ServletRequest, ServletResponse, FilterChain)}.
*
* @deprecated see {@link WebSecurityConfiguration} for
* {@link org.springframework.web.util.pattern.PathPattern} replacement
*/ */
@Deprecated
static class CompositeFilterChainProxy extends FilterChainProxy { static class CompositeFilterChainProxy extends FilterChainProxy {
/** /**

View File

@ -16,16 +16,29 @@
package org.springframework.security.config.annotation.web.configuration; package org.springframework.security.config.annotation.web.configuration;
import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import jakarta.servlet.Filter; import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.DependsOn;
@ -45,11 +58,18 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor; import org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor;
import org.springframework.security.context.DelegatingApplicationListener; import org.springframework.security.context.DelegatingApplicationListener;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.web.FilterChainProxy; import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.FilterInvocation; import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator; import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
import org.springframework.security.web.debug.DebugFilter;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.RequestRejectedHandler;
import org.springframework.web.filter.CompositeFilter;
import org.springframework.web.filter.ServletRequestPathFilter;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
/** /**
* Uses a {@link WebSecurity} to create the {@link FilterChainProxy} that performs the web * Uses a {@link WebSecurity} to create the {@link FilterChainProxy} that performs the web
@ -186,6 +206,51 @@ public class WebSecurityConfiguration implements ImportAware {
} }
} }
/**
* Used to ensure Spring MVC request matching is cached.
*
* Creates a {@link BeanDefinitionRegistryPostProcessor} that detects if a bean named
* HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME is defined. If so, it moves the
* AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME to another bean name
* and then adds a {@link CompositeFilter} that contains
* {@link HandlerMappingIntrospector#createCacheFilter()} and the original
* FilterChainProxy under the original Bean name.
* @return
*/
@Bean
static BeanDefinitionRegistryPostProcessor springSecurityPathPatternParserBeanDefinitionRegistryPostProcessor() {
return new BeanDefinitionRegistryPostProcessor() {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
BeanDefinition filterChainProxy = registry
.getBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
if (filterChainProxy.getResolvableType().isInstance(CompositeFilterChainProxy.class)) {
return;
}
BeanDefinitionBuilder pppCacheFilterBldr = BeanDefinitionBuilder
.rootBeanDefinition(ServletRequestPathFilter.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
ManagedList<BeanMetadataElement> filters = new ManagedList<>();
filters.add(pppCacheFilterBldr.getBeanDefinition());
filters.add(filterChainProxy);
BeanDefinitionBuilder compositeSpringSecurityFilterChainBldr = BeanDefinitionBuilder
.rootBeanDefinition(CompositeFilterChainProxy.class)
.addConstructorArgValue(filters);
registry.removeBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
registry.registerBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME,
compositeSpringSecurityFilterChainBldr.getBeanDefinition());
}
};
}
/** /**
* A custom version of the Spring provided AnnotationAwareOrderComparator that uses * A custom version of the Spring provided AnnotationAwareOrderComparator that uses
* {@link AnnotationUtils#findAnnotation(Class, Class)} to look on super class * {@link AnnotationUtils#findAnnotation(Class, Class)} to look on super class
@ -219,4 +284,121 @@ public class WebSecurityConfiguration implements ImportAware {
} }
/**
* Extends {@link FilterChainProxy} to provide as much passivity as possible but
* delegates to {@link CompositeFilter} for
* {@link #doFilter(ServletRequest, ServletResponse, FilterChain)}.
*/
static class CompositeFilterChainProxy extends FilterChainProxy {
/**
* Used for {@link #doFilter(ServletRequest, ServletResponse, FilterChain)}
*/
private final Filter doFilterDelegate;
private final FilterChainProxy springSecurityFilterChain;
/**
* Creates a new instance
* @param filters the Filters to delegate to. One of which must be
* FilterChainProxy.
*/
CompositeFilterChainProxy(List<? extends Filter> filters) {
this.doFilterDelegate = createDoFilterDelegate(filters);
this.springSecurityFilterChain = findFilterChainProxy(filters);
}
@Override
public void afterPropertiesSet() {
this.springSecurityFilterChain.afterPropertiesSet();
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
this.doFilterDelegate.doFilter(request, response, chain);
}
@Override
public List<Filter> getFilters(String url) {
return this.springSecurityFilterChain.getFilters(url);
}
@Override
public List<SecurityFilterChain> getFilterChains() {
return this.springSecurityFilterChain.getFilterChains();
}
@Override
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
this.springSecurityFilterChain.setSecurityContextHolderStrategy(securityContextHolderStrategy);
}
@Override
public void setFilterChainValidator(FilterChainValidator filterChainValidator) {
this.springSecurityFilterChain.setFilterChainValidator(filterChainValidator);
}
@Override
public void setFilterChainDecorator(FilterChainDecorator filterChainDecorator) {
this.springSecurityFilterChain.setFilterChainDecorator(filterChainDecorator);
}
@Override
public void setFirewall(HttpFirewall firewall) {
this.springSecurityFilterChain.setFirewall(firewall);
}
@Override
public void setRequestRejectedHandler(RequestRejectedHandler requestRejectedHandler) {
this.springSecurityFilterChain.setRequestRejectedHandler(requestRejectedHandler);
}
/**
* Used through reflection by Spring Security's Test support to lookup the
* FilterChainProxy Filters for a specific HttpServletRequest.
* @param request
* @return
*/
private List<? extends Filter> getFilters(HttpServletRequest request) {
List<SecurityFilterChain> filterChains = this.springSecurityFilterChain.getFilterChains();
for (SecurityFilterChain chain : filterChains) {
if (chain.matches(request)) {
return chain.getFilters();
}
}
return null;
}
/**
* Creates the Filter to delegate to for doFilter
* @param filters the Filters to delegate to.
* @return the Filter for doFilter
*/
private static Filter createDoFilterDelegate(List<? extends Filter> filters) {
CompositeFilter delegate = new CompositeFilter();
delegate.setFilters(filters);
return delegate;
}
/**
* Find the FilterChainProxy in a List of Filter
* @param filters
* @return non-null FilterChainProxy
* @throws IllegalStateException if the FilterChainProxy cannot be found
*/
private static FilterChainProxy findFilterChainProxy(List<? extends Filter> filters) {
for (Filter filter : filters) {
if (filter instanceof FilterChainProxy fcp) {
return fcp;
}
if (filter instanceof DebugFilter debugFilter) {
return debugFilter.getFilterChainProxy();
}
}
throw new IllegalStateException("Couldn't find FilterChainProxy in " + filters);
}
}
} }

View File

@ -21,6 +21,7 @@ import java.util.UUID;
import org.springframework.security.authentication.AnonymousAuthenticationProvider; import org.springframework.security.authentication.AnonymousAuthenticationProvider;
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.SecurityConfigurer; import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@ -57,7 +58,7 @@ public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>>
/** /**
* Creates a new instance * Creates a new instance
* @see HttpSecurity#anonymous() * @see HttpSecurity#anonymous(Customizer)
*/ */
public AnonymousConfigurer() { public AnonymousConfigurer() {
} }

View File

@ -231,17 +231,6 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
return this; return this;
} }
/**
* Return the {@link HttpSecurityBuilder} when done using the
* {@link AuthorizeHttpRequestsConfigurer}. This is useful for method chaining.
* @return the {@link HttpSecurityBuilder} for further customizations
* @deprecated For removal in 7.0. Use the lambda based configuration instead.
*/
@Deprecated(since = "6.1", forRemoval = true)
public H and() {
return AuthorizeHttpRequestsConfigurer.this.and();
}
} }
/** /**

View File

@ -24,10 +24,7 @@ import java.util.List;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig; import org.springframework.security.access.SecurityConfig;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.ObjectPostProcessor; import org.springframework.security.config.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityBuilder;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultRedirectStrategy; import org.springframework.security.web.DefaultRedirectStrategy;
@ -96,7 +93,7 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>>
/** /**
* Creates a new instance * Creates a new instance
* @see HttpSecurity#requiresChannel() * @see HttpSecurity#requiresChannel(Customizer)
*/ */
public ChannelSecurityConfigurer(ApplicationContext context) { public ChannelSecurityConfigurer(ApplicationContext context) {
this.REGISTRY = new ChannelRequestMatcherRegistry(context); this.REGISTRY = new ChannelRequestMatcherRegistry(context);
@ -207,18 +204,6 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>>
return this; return this;
} }
/**
* Return the {@link SecurityBuilder} when done using the
* {@link SecurityConfigurer}. This is useful for method chaining.
* @return the type of {@link HttpSecurityBuilder} that is being configured
* @deprecated For removal in 7.0. Use
* {@link HttpSecurity#requiresChannel(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public H and() {
return ChannelSecurityConfigurer.this.and();
}
} }
/** /**

View File

@ -18,6 +18,7 @@ package org.springframework.security.config.annotation.web.configurers;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -57,7 +58,7 @@ public class CorsConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHt
/** /**
* Creates a new instance * Creates a new instance
* *
* @see HttpSecurity#cors() * @see HttpSecurity#cors(Customizer)
*/ */
public CorsConfigurer() { public CorsConfigurer() {
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2023 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,12 +19,15 @@ package org.springframework.security.config.annotation.web.configurers;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.function.Supplier;
import io.micrometer.observation.ObservationRegistry; import io.micrometer.observation.ObservationRegistry;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry; import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@ -34,13 +37,17 @@ import org.springframework.security.web.access.CompositeAccessDeniedHandler;
import org.springframework.security.web.access.DelegatingAccessDeniedHandler; import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
import org.springframework.security.web.access.ObservationMarkingAccessDeniedHandler; import org.springframework.security.web.access.ObservationMarkingAccessDeniedHandler;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfAuthenticationStrategy; import org.springframework.security.web.csrf.CsrfAuthenticationStrategy;
import org.springframework.security.web.csrf.CsrfFilter; import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.security.web.csrf.CsrfLogoutHandler; import org.springframework.security.web.csrf.CsrfLogoutHandler;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.csrf.CsrfTokenRepository; import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
import org.springframework.security.web.csrf.CsrfTokenRequestHandler; import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
import org.springframework.security.web.csrf.MissingCsrfTokenException; import org.springframework.security.web.csrf.MissingCsrfTokenException;
import org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;
import org.springframework.security.web.session.InvalidSessionAccessDeniedHandler; import org.springframework.security.web.session.InvalidSessionAccessDeniedHandler;
import org.springframework.security.web.session.InvalidSessionStrategy; import org.springframework.security.web.session.InvalidSessionStrategy;
import org.springframework.security.web.util.matcher.AndRequestMatcher; import org.springframework.security.web.util.matcher.AndRequestMatcher;
@ -48,6 +55,7 @@ import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher; import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/** /**
* Adds * Adds
@ -96,7 +104,7 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
/** /**
* Creates a new instance * Creates a new instance
* @see HttpSecurity#csrf() * @see HttpSecurity#csrf(Customizer)
*/ */
public CsrfConfigurer(ApplicationContext context) { public CsrfConfigurer(ApplicationContext context) {
this.context = context; this.context = context;
@ -156,16 +164,16 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
* *
* <pre> * <pre>
* http * http
* .csrf() * .csrf((csrf) -&gt; csrf
* .ignoringRequestMatchers((request) -&gt; "XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) * .ignoringRequestMatchers((request) -&gt; "XMLHttpRequest".equals(request.getHeader("X-Requested-With"))))
* .and()
* ... * ...
* </pre> * </pre>
* *
* @since 5.1 * @since 5.1
*/ */
public CsrfConfigurer<H> ignoringRequestMatchers(RequestMatcher... requestMatchers) { public CsrfConfigurer<H> ignoringRequestMatchers(RequestMatcher... requestMatchers) {
return new IgnoreCsrfProtectionRegistry(this.context).requestMatchers(requestMatchers).and(); new IgnoreCsrfProtectionRegistry(this.context).requestMatchers(requestMatchers);
return this;
} }
/** /**
@ -184,9 +192,8 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
* *
* <pre> * <pre>
* http * http
* .csrf() * .csrf((csrf) -&gt; csrf
* .ignoringRequestMatchers("/sockjs/**") * .ignoringRequestMatchers("/sockjs/**"))
* .and()
* ... * ...
* </pre> * </pre>
* *
@ -194,7 +201,8 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
* @see AbstractRequestMatcherRegistry#requestMatchers(String...) * @see AbstractRequestMatcherRegistry#requestMatchers(String...)
*/ */
public CsrfConfigurer<H> ignoringRequestMatchers(String... patterns) { public CsrfConfigurer<H> ignoringRequestMatchers(String... patterns) {
return new IgnoreCsrfProtectionRegistry(this.context).requestMatchers(patterns).and(); new IgnoreCsrfProtectionRegistry(this.context).requestMatchers(patterns);
return this;
} }
/** /**
@ -214,6 +222,21 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
return this; return this;
} }
/**
* <p>
* Sensible CSRF defaults when used in combination with a single page application.
* Creates a cookie-based token repository and a custom request handler to resolve the
* actual token value instead of the encoded token.
* </p>
* @return the {@link CsrfConfigurer} for further customizations
* @since 7.0
*/
public CsrfConfigurer<H> spa() {
this.csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
this.requestHandler = new SpaCsrfTokenRequestHandler();
return this;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public void configure(H http) { public void configure(H http) {
@ -363,10 +386,6 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
setApplicationContext(context); setApplicationContext(context);
} }
CsrfConfigurer<H> and() {
return CsrfConfigurer.this;
}
@Override @Override
protected IgnoreCsrfProtectionRegistry chainRequestMatchers(List<RequestMatcher> requestMatchers) { protected IgnoreCsrfProtectionRegistry chainRequestMatchers(List<RequestMatcher> requestMatchers) {
CsrfConfigurer.this.ignoredCsrfProtectionMatchers.addAll(requestMatchers); CsrfConfigurer.this.ignoredCsrfProtectionMatchers.addAll(requestMatchers);
@ -375,4 +394,27 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
} }
private static final class SpaCsrfTokenRequestHandler implements CsrfTokenRequestHandler {
private final CsrfTokenRequestAttributeHandler plain = new CsrfTokenRequestAttributeHandler();
private final CsrfTokenRequestAttributeHandler xor = new XorCsrfTokenRequestAttributeHandler();
SpaCsrfTokenRequestHandler() {
this.xor.setCsrfRequestAttributeName(null);
}
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, Supplier<CsrfToken> csrfToken) {
this.xor.handle(request, response, csrfToken);
}
@Override
public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
String headerValue = request.getHeader(csrfToken.getHeaderName());
return (StringUtils.hasText(headerValue) ? this.plain : this.xor).resolveCsrfTokenValue(request, csrfToken);
}
}
} }

View File

@ -33,13 +33,6 @@ import org.springframework.security.web.csrf.CsrfToken;
* Adds a Filter that will generate a login page if one is not specified otherwise when * Adds a Filter that will generate a login page if one is not specified otherwise when
* using {@link EnableWebSecurity}. * using {@link EnableWebSecurity}.
* *
* <p>
* By default an
* {@link org.springframework.security.web.access.channel.InsecureChannelProcessor} and a
* {@link org.springframework.security.web.access.channel.SecureChannelProcessor} will be
* registered.
* </p>
*
* <h2>Security Filters</h2> * <h2>Security Filters</h2>
* *
* The following Filters are conditionally populated * The following Filters are conditionally populated
@ -58,8 +51,6 @@ import org.springframework.security.web.csrf.CsrfToken;
* The following shared objects are used: * The following shared objects are used:
* *
* <ul> * <ul>
* <li>{@link org.springframework.security.web.PortMapper} is used to create the default
* {@link org.springframework.security.web.access.channel.ChannelProcessor} instances</li>
* <li>{@link FormLoginConfigurer} is used to determine if the * <li>{@link FormLoginConfigurer} is used to determine if the
* {@link DefaultLoginPageConfigurer} should be added and how to configure it.</li> * {@link DefaultLoginPageConfigurer} should be added and how to configure it.</li>
* </ul> * </ul>

View File

@ -18,6 +18,7 @@ package org.springframework.security.config.annotation.web.configurers;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.AuthenticationEntryPoint;
@ -76,7 +77,7 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
/** /**
* Creates a new instance * Creates a new instance
* @see HttpSecurity#exceptionHandling() * @see HttpSecurity#exceptionHandling(Customizer)
*/ */
public ExceptionHandlingConfigurer() { public ExceptionHandlingConfigurer() {
} }

View File

@ -29,6 +29,7 @@ import org.springframework.security.access.SecurityConfig;
import org.springframework.security.access.expression.SecurityExpressionHandler; import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy; import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.authentication.AuthenticationTrustResolver; import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.ObjectPostProcessor; import org.springframework.security.config.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@ -77,7 +78,7 @@ import org.springframework.util.StringUtils;
* @author Yanming Zhou * @author Yanming Zhou
* @author Ngoc Nhan * @author Ngoc Nhan
* @since 3.2 * @since 3.2
* @see org.springframework.security.config.annotation.web.builders.HttpSecurity#authorizeRequests() * @see org.springframework.security.config.annotation.web.builders.HttpSecurity#authorizeRequests(Customizer)
* @deprecated Use {@link AuthorizeHttpRequestsConfigurer} instead * @deprecated Use {@link AuthorizeHttpRequestsConfigurer} instead
*/ */
@Deprecated @Deprecated
@ -104,7 +105,7 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
/** /**
* Creates a new instance * Creates a new instance
* @see HttpSecurity#authorizeRequests() * @see HttpSecurity#authorizeRequests(Customizer)
*/ */
public ExpressionUrlAuthorizationConfigurer(ApplicationContext context) { public ExpressionUrlAuthorizationConfigurer(ApplicationContext context) {
GrantedAuthorityDefaults grantedAuthorityDefaults = context.getBeanProvider(GrantedAuthorityDefaults.class) GrantedAuthorityDefaults grantedAuthorityDefaults = context.getBeanProvider(GrantedAuthorityDefaults.class)
@ -250,7 +251,7 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
} }
public H and() { public H and() {
return ExpressionUrlAuthorizationConfigurer.this.and(); return ExpressionUrlAuthorizationConfigurer.this.getBuilder();
} }
} }

View File

@ -17,6 +17,7 @@
package org.springframework.security.config.annotation.web.configurers; package org.springframework.security.config.annotation.web.configurers;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.RequestMatcherFactory; import org.springframework.security.config.annotation.web.RequestMatcherFactory;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@ -74,7 +75,7 @@ public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
/** /**
* Creates a new instance * Creates a new instance
* @see HttpSecurity#formLogin() * @see HttpSecurity#formLogin(Customizer)
*/ */
public FormLoginConfigurer() { public FormLoginConfigurer() {
super(new UsernamePasswordAuthenticationFilter(), null); super(new UsernamePasswordAuthenticationFilter(), null);

View File

@ -111,7 +111,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
/** /**
* Creates a new instance * Creates a new instance
* *
* @see HttpSecurity#headers() * @see HttpSecurity#headers(Customizer)
*/ */
public HeadersConfigurer() { public HeadersConfigurer() {
} }
@ -127,26 +127,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return this; return this;
} }
/**
* Configures the {@link XContentTypeOptionsHeaderWriter} which inserts the
* <a href= "https://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx"
* >X-Content-Type-Options</a>:
*
* <pre>
* X-Content-Type-Options: nosniff
* </pre>
* @return the {@link ContentTypeOptionsConfig} for additional customizations
* @deprecated For removal in 7.0. Use {@link #contentTypeOptions(Customizer)} or
* {@code contentTypeOptions(Customizer.withDefaults())} to stick with defaults. See
* the <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public ContentTypeOptionsConfig contentTypeOptions() {
return this.contentTypeOptions.enable();
}
/** /**
* Configures the {@link XContentTypeOptionsHeaderWriter} which inserts the * Configures the {@link XContentTypeOptionsHeaderWriter} which inserts the
* <a href= "https://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx" * <a href= "https://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx"
@ -164,26 +144,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return HeadersConfigurer.this; return HeadersConfigurer.this;
} }
/**
* <strong>Note this is not comprehensive XSS protection!</strong>
*
* <p>
* Allows customizing the {@link XXssProtectionHeaderWriter} which adds the <a href=
* "https://web.archive.org/web/20160201174302/https://blogs.msdn.com/b/ieinternals/archive/2011/01/31/controlling-the-internet-explorer-xss-filter-with-the-x-xss-protection-http-header.aspx"
* >X-XSS-Protection header</a>
* </p>
* @return the {@link XXssConfig} for additional customizations
* @deprecated For removal in 7.0. Use {@link #xssProtection(Customizer)} or
* {@code xssProtection(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public XXssConfig xssProtection() {
return this.xssProtection.enable();
}
/** /**
* <strong>Note this is not comprehensive XSS protection!</strong> * <strong>Note this is not comprehensive XSS protection!</strong>
* *
@ -201,26 +161,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return HeadersConfigurer.this; return HeadersConfigurer.this;
} }
/**
* Allows customizing the {@link CacheControlHeadersWriter}. Specifically it adds the
* following headers:
* <ul>
* <li>Cache-Control: no-cache, no-store, max-age=0, must-revalidate</li>
* <li>Pragma: no-cache</li>
* <li>Expires: 0</li>
* </ul>
* @return the {@link CacheControlConfig} for additional customizations
* @deprecated For removal in 7.0. Use {@link #cacheControl(Customizer)} or
* {@code cacheControl(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public CacheControlConfig cacheControl() {
return this.cacheControl.enable();
}
/** /**
* Allows customizing the {@link CacheControlHeadersWriter}. Specifically it adds the * Allows customizing the {@link CacheControlHeadersWriter}. Specifically it adds the
* following headers: * following headers:
@ -238,19 +178,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return HeadersConfigurer.this; return HeadersConfigurer.this;
} }
/**
* Allows customizing the {@link HstsHeaderWriter} which provides support for
* <a href="https://tools.ietf.org/html/rfc6797">HTTP Strict Transport Security
* (HSTS)</a>.
* @return the {@link HstsConfig} for additional customizations
* @deprecated For removal in 7.0. Use
* {@link #httpStrictTransportSecurity(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HstsConfig httpStrictTransportSecurity() {
return this.hsts.enable();
}
/** /**
* Allows customizing the {@link HstsHeaderWriter} which provides support for * Allows customizing the {@link HstsHeaderWriter} which provides support for
* <a href="https://tools.ietf.org/html/rfc6797">HTTP Strict Transport Security * <a href="https://tools.ietf.org/html/rfc6797">HTTP Strict Transport Security
@ -264,20 +191,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return HeadersConfigurer.this; return HeadersConfigurer.this;
} }
/**
* Allows customizing the {@link XFrameOptionsHeaderWriter}.
* @return the {@link FrameOptionsConfig} for additional customizations
* @deprecated For removal in 7.0. Use {@link #frameOptions(Customizer)} or
* {@code frameOptions(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public FrameOptionsConfig frameOptions() {
return this.frameOptions.enable();
}
/** /**
* Allows customizing the {@link XFrameOptionsHeaderWriter}. * Allows customizing the {@link XFrameOptionsHeaderWriter}.
* @param frameOptionsCustomizer the {@link Customizer} to provide more options for * @param frameOptionsCustomizer the {@link Customizer} to provide more options for
@ -289,21 +202,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return HeadersConfigurer.this; return HeadersConfigurer.this;
} }
/**
* Allows customizing the {@link HpkpHeaderWriter} which provides support for
* <a href="https://tools.ietf.org/html/rfc7469">HTTP Public Key Pinning (HPKP)</a>.
* @return the {@link HpkpConfig} for additional customizations
*
* @since 4.1
* @deprecated see <a href=
* "https://owasp.org/www-community/controls/Certificate_and_Public_Key_Pinning">Certificate
* and Public Key Pinning</a> for more context
*/
@Deprecated
public HpkpConfig httpPublicKeyPinning() {
return this.hpkp.enable();
}
/** /**
* Allows customizing the {@link HpkpHeaderWriter} which provides support for * Allows customizing the {@link HpkpHeaderWriter} which provides support for
* <a href="https://tools.ietf.org/html/rfc7469">HTTP Public Key Pinning (HPKP)</a>. * <a href="https://tools.ietf.org/html/rfc7469">HTTP Public Key Pinning (HPKP)</a>.
@ -320,39 +218,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return HeadersConfigurer.this; return HeadersConfigurer.this;
} }
/**
* <p>
* Allows configuration for <a href="https://www.w3.org/TR/CSP2/">Content Security
* Policy (CSP) Level 2</a>.
* </p>
*
* <p>
* Calling this method automatically enables (includes) the Content-Security-Policy
* header in the response using the supplied security policy directive(s).
* </p>
*
* <p>
* Configuration is provided to the {@link ContentSecurityPolicyHeaderWriter} which
* supports the writing of the two headers as detailed in the W3C Candidate
* Recommendation:
* </p>
* <ul>
* <li>Content-Security-Policy</li>
* <li>Content-Security-Policy-Report-Only</li>
* </ul>
* @return the {@link ContentSecurityPolicyConfig} for additional configuration
* @throws IllegalArgumentException if policyDirectives is null or empty
* @since 4.1
* @deprecated For removal in 7.0. Use {@link #contentSecurityPolicy(Customizer)}
* instead
* @see ContentSecurityPolicyHeaderWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public ContentSecurityPolicyConfig contentSecurityPolicy(String policyDirectives) {
this.contentSecurityPolicy.writer = new ContentSecurityPolicyHeaderWriter(policyDirectives);
return this.contentSecurityPolicy;
}
/** /**
* <p> * <p>
* Allows configuration for <a href="https://www.w3.org/TR/CSP2/">Content Security * Allows configuration for <a href="https://www.w3.org/TR/CSP2/">Content Security
@ -454,71 +319,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
} }
} }
/**
* <p>
* Allows configuration for <a href="https://www.w3.org/TR/referrer-policy/">Referrer
* Policy</a>.
* </p>
*
* <p>
* Configuration is provided to the {@link ReferrerPolicyHeaderWriter} which support
* the writing of the header as detailed in the W3C Technical Report:
* </p>
* <ul>
* <li>Referrer-Policy</li>
* </ul>
*
* <p>
* Default value is:
* </p>
*
* <pre>
* Referrer-Policy: no-referrer
* </pre>
* @return the {@link ReferrerPolicyConfig} for additional configuration
* @since 4.2
* @deprecated For removal in 7.0. Use {@link #referrerPolicy(Customizer)} or
* {@code referrerPolicy(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
* @see ReferrerPolicyHeaderWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public ReferrerPolicyConfig referrerPolicy() {
this.referrerPolicy.writer = new ReferrerPolicyHeaderWriter();
return this.referrerPolicy;
}
/**
* <p>
* Allows configuration for <a href="https://www.w3.org/TR/referrer-policy/">Referrer
* Policy</a>.
* </p>
*
* <p>
* Configuration is provided to the {@link ReferrerPolicyHeaderWriter} which support
* the writing of the header as detailed in the W3C Technical Report:
* </p>
* <ul>
* <li>Referrer-Policy</li>
* </ul>
* @return the {@link ReferrerPolicyConfig} for additional configuration
* @throws IllegalArgumentException if policy is null or empty
* @since 4.2
* @deprecated For removal in 7.0. Use {@link #referrerPolicy(Customizer)} or
* {@code referrerPolicy(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
* @see ReferrerPolicyHeaderWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public ReferrerPolicyConfig referrerPolicy(ReferrerPolicy policy) {
this.referrerPolicy.writer = new ReferrerPolicyHeaderWriter(policy);
return this.referrerPolicy;
}
/** /**
* <p> * <p>
* Allows configuration for <a href="https://www.w3.org/TR/referrer-policy/">Referrer * Allows configuration for <a href="https://www.w3.org/TR/referrer-policy/">Referrer
@ -568,35 +368,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return this.featurePolicy; return this.featurePolicy;
} }
/**
* <p>
* Allows configuration for
* <a href="https://w3c.github.io/webappsec-permissions-policy/">Permissions
* Policy</a>.
* </p>
*
* <p>
* Configuration is provided to the {@link PermissionsPolicyHeaderWriter} which
* support the writing of the header as detailed in the W3C Technical Report:
* </p>
* <ul>
* <li>Permissions-Policy</li>
* </ul>
* @return the {@link PermissionsPolicyConfig} for additional configuration
* @since 5.5
* @deprecated For removal in 7.0. Use {@link #permissionsPolicyHeader(Customizer)} or
* {@code permissionsPolicy(Customizer.withDefaults())} to stick with defaults. See
* the <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
* @see PermissionsPolicyHeaderWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public PermissionsPolicyConfig permissionsPolicy() {
this.permissionsPolicy.writer = new PermissionsPolicyHeaderWriter();
return this.permissionsPolicy;
}
/** /**
* Allows configuration for * Allows configuration for
* <a href="https://w3c.github.io/webappsec-permissions-policy/"> Permissions * <a href="https://w3c.github.io/webappsec-permissions-policy/"> Permissions
@ -643,26 +414,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return this; return this;
} }
/**
* Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
* Cross-Origin-Opener-Policy</a> header.
* <p>
* Configuration is provided to the {@link CrossOriginOpenerPolicyHeaderWriter} which
* responsible for writing the header.
* </p>
* @return the {@link CrossOriginOpenerPolicyConfig} for additional confniguration
* @since 5.7
* @deprecated For removal in 7.0. Use {@link #crossOriginOpenerPolicy(Customizer)}
* instead
* @see CrossOriginOpenerPolicyHeaderWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public CrossOriginOpenerPolicyConfig crossOriginOpenerPolicy() {
this.crossOriginOpenerPolicy.writer = new CrossOriginOpenerPolicyHeaderWriter();
return this.crossOriginOpenerPolicy;
}
/** /**
* Allows configuration for <a href= * Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy"> * "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
@ -687,26 +438,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return HeadersConfigurer.this; return HeadersConfigurer.this;
} }
/**
* Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
* Cross-Origin-Embedder-Policy</a> header.
* <p>
* Configuration is provided to the {@link CrossOriginEmbedderPolicyHeaderWriter}
* which is responsible for writing the header.
* </p>
* @return the {@link CrossOriginEmbedderPolicyConfig} for additional customizations
* @since 5.7
* @deprecated For removal in 7.0. Use {@link #crossOriginEmbedderPolicy(Customizer)}
* instead
* @see CrossOriginEmbedderPolicyHeaderWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public CrossOriginEmbedderPolicyConfig crossOriginEmbedderPolicy() {
this.crossOriginEmbedderPolicy.writer = new CrossOriginEmbedderPolicyHeaderWriter();
return this.crossOriginEmbedderPolicy;
}
/** /**
* Allows configuration for <a href= * Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy"> * "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
@ -731,26 +462,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return HeadersConfigurer.this; return HeadersConfigurer.this;
} }
/**
* Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
* Cross-Origin-Resource-Policy</a> header.
* <p>
* Configuration is provided to the {@link CrossOriginResourcePolicyHeaderWriter}
* which is responsible for writing the header:
* </p>
* @return the {@link HeadersConfigurer} for additional customizations
* @since 5.7
* @deprecated For removal in 7.0. Use {@link #crossOriginResourcePolicy(Customizer)}
* instead
* @see CrossOriginResourcePolicyHeaderWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public CrossOriginResourcePolicyConfig crossOriginResourcePolicy() {
this.crossOriginResourcePolicy.writer = new CrossOriginResourcePolicyHeaderWriter();
return this.crossOriginResourcePolicy;
}
/** /**
* Allows configuration for <a href= * Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy"> * "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
@ -789,17 +500,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
*/ */
public HeadersConfigurer<H> disable() { public HeadersConfigurer<H> disable() {
this.writer = null; this.writer = null;
return and();
}
/**
* Allows customizing the {@link HeadersConfigurer}
* @return the {@link HeadersConfigurer} for additional customization
* @deprecated For removal in 7.0. Use {@link #contentTypeOptions(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this; return HeadersConfigurer.this;
} }
@ -864,21 +564,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
*/ */
public HeadersConfigurer<H> disable() { public HeadersConfigurer<H> disable() {
this.writer = null; this.writer = null;
return and();
}
/**
* Allows completing configuration of X-XSS-Protection and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use {@link #xssProtection(Customizer)} or
* {@code xssProtection(Customizer.withDefaults())} to stick with defaults. See
* the <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this; return HeadersConfigurer.this;
} }
@ -912,21 +597,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return HeadersConfigurer.this; return HeadersConfigurer.this;
} }
/**
* Allows completing configuration of Cache Control and continuing configuration
* of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use {@link #cacheControl(Customizer)} or
* {@code cacheControl(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
/** /**
* Ensures the Cache Control headers are enabled if they are not already. * Ensures the Cache Control headers are enabled if they are not already.
* @return the {@link CacheControlConfig} for additional customization * @return the {@link CacheControlConfig} for additional customization
@ -1024,18 +694,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return HeadersConfigurer.this; return HeadersConfigurer.this;
} }
/**
* Allows completing configuration of Strict Transport Security and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use
* {@link #httpStrictTransportSecurity(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
/** /**
* Ensures that Strict-Transport-Security is enabled if it is not already * Ensures that Strict-Transport-Security is enabled if it is not already
* @return the {@link HstsConfig} for additional customization * @return the {@link HstsConfig} for additional customization
@ -1063,7 +721,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
*/ */
public HeadersConfigurer<H> deny() { public HeadersConfigurer<H> deny() {
this.writer = new XFrameOptionsHeaderWriter(XFrameOptionsMode.DENY); this.writer = new XFrameOptionsHeaderWriter(XFrameOptionsMode.DENY);
return and(); return HeadersConfigurer.this;
} }
/** /**
@ -1077,7 +735,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
*/ */
public HeadersConfigurer<H> sameOrigin() { public HeadersConfigurer<H> sameOrigin() {
this.writer = new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN); this.writer = new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN);
return and(); return HeadersConfigurer.this;
} }
/** /**
@ -1086,20 +744,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
*/ */
public HeadersConfigurer<H> disable() { public HeadersConfigurer<H> disable() {
this.writer = null; this.writer = null;
return and();
}
/**
* Allows continuing customizing the headers configuration.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use {@link #frameOptions(Customizer)} or
* {@code frameOptions(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this; return HeadersConfigurer.this;
} }
@ -1317,18 +961,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return this; return this;
} }
/**
* Allows completing configuration of Content Security Policy and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use {@link #contentSecurityPolicy(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
} }
public final class ReferrerPolicyConfig { public final class ReferrerPolicyConfig {
@ -1349,18 +981,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return this; return this;
} }
/**
* @deprecated For removal in 7.0. Use {@link #referrerPolicy(Customizer)} or
* {@code referrerPolicy(Customizer.withDefaults())} to stick with defaults. See
* the <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
} }
public final class FeaturePolicyConfig { public final class FeaturePolicyConfig {
@ -1399,18 +1019,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return this; return this;
} }
/**
* Allows completing configuration of Permissions Policy and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use {@link #permissionsPolicy(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
} }
public final class CrossOriginOpenerPolicyConfig { public final class CrossOriginOpenerPolicyConfig {
@ -1432,18 +1040,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return this; return this;
} }
/**
* Allows completing configuration of Cross Origin Opener Policy and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use
* {@link #crossOriginOpenerPolicy(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
} }
public final class CrossOriginEmbedderPolicyConfig { public final class CrossOriginEmbedderPolicyConfig {
@ -1466,18 +1062,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return this; return this;
} }
/**
* Allows completing configuration of Cross-Origin-Embedder-Policy and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use
* {@link #crossOriginEmbedderPolicy(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
} }
public final class CrossOriginResourcePolicyConfig { public final class CrossOriginResourcePolicyConfig {
@ -1500,18 +1084,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return this; return this;
} }
/**
* Allows completing configuration of Cross-Origin-Resource-Policy and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use
* {@link #crossOriginResourcePolicy(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
} }
} }

View File

@ -26,6 +26,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationDetailsSource; import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.AuthenticationEntryPoint;
@ -98,7 +99,7 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>>
/** /**
* Creates a new instance * Creates a new instance
* @see HttpSecurity#httpBasic() * @see HttpSecurity#httpBasic(Customizer)
*/ */
public HttpBasicConfigurer() { public HttpBasicConfigurer() {
realmName(DEFAULT_REALM); realmName(DEFAULT_REALM);

View File

@ -22,6 +22,7 @@ import java.util.Set;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.authority.mapping.SimpleMappableAttributesRetriever; import org.springframework.security.core.authority.mapping.SimpleMappableAttributesRetriever;
@ -77,7 +78,7 @@ public final class JeeConfigurer<H extends HttpSecurityBuilder<H>> extends Abstr
/** /**
* Creates a new instance * Creates a new instance
* @see HttpSecurity#jee() * @see HttpSecurity#jee(Customizer)
*/ */
public JeeConfigurer() { public JeeConfigurer() {
} }

View File

@ -23,6 +23,7 @@ import java.util.List;
import jakarta.servlet.http.HttpSession; import jakarta.servlet.http.HttpSession;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.SecurityConfigurer; import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.RequestMatcherFactory; import org.springframework.security.config.annotation.web.RequestMatcherFactory;
@ -92,7 +93,7 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>>
/** /**
* Creates a new instance * Creates a new instance
* @see HttpSecurity#logout() * @see HttpSecurity#logout(Customizer)
*/ */
public LogoutConfigurer() { public LogoutConfigurer() {
} }
@ -150,7 +151,7 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>>
* @param logoutUrl the URL that will invoke logout. * @param logoutUrl the URL that will invoke logout.
* @return the {@link LogoutConfigurer} for further customization * @return the {@link LogoutConfigurer} for further customization
* @see #logoutRequestMatcher(RequestMatcher) * @see #logoutRequestMatcher(RequestMatcher)
* @see HttpSecurity#csrf() * @see HttpSecurity#csrf(Customizer)
*/ */
public LogoutConfigurer<H> logoutUrl(String logoutUrl) { public LogoutConfigurer<H> logoutUrl(String logoutUrl) {
this.logoutRequestMatcher = null; this.logoutRequestMatcher = null;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -145,6 +145,9 @@ public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>>
RequestMatcher notFavIcon = new NegatedRequestMatcher(getFaviconRequestMatcher()); RequestMatcher notFavIcon = new NegatedRequestMatcher(getFaviconRequestMatcher());
RequestMatcher notXRequestedWith = new NegatedRequestMatcher( RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest")); new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
RequestMatcher notWebSocket = new NegatedRequestMatcher(
new RequestHeaderRequestMatcher("Upgrade", "websocket"));
boolean isCsrfEnabled = http.getConfigurer(CsrfConfigurer.class) != null; boolean isCsrfEnabled = http.getConfigurer(CsrfConfigurer.class) != null;
List<RequestMatcher> matchers = new ArrayList<>(); List<RequestMatcher> matchers = new ArrayList<>();
if (isCsrfEnabled) { if (isCsrfEnabled) {
@ -156,6 +159,7 @@ public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>>
matchers.add(notXRequestedWith); matchers.add(notXRequestedWith);
matchers.add(notMatchingMediaType(http, MediaType.MULTIPART_FORM_DATA)); matchers.add(notMatchingMediaType(http, MediaType.MULTIPART_FORM_DATA));
matchers.add(notMatchingMediaType(http, MediaType.TEXT_EVENT_STREAM)); matchers.add(notMatchingMediaType(http, MediaType.TEXT_EVENT_STREAM));
matchers.add(notWebSocket);
return new AndRequestMatcher(matchers); return new AndRequestMatcher(matchers);
} }

View File

@ -16,6 +16,7 @@
package org.springframework.security.config.annotation.web.configurers; package org.springframework.security.config.annotation.web.configurers;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.config.http.SessionCreationPolicy;
@ -70,7 +71,7 @@ public final class SecurityContextConfigurer<H extends HttpSecurityBuilder<H>>
/** /**
* Creates a new instance * Creates a new instance
* @see HttpSecurity#securityContext() * @see HttpSecurity#securityContext(Customizer)
*/ */
public SecurityContextConfigurer() { public SecurityContextConfigurer() {
} }

View File

@ -23,6 +23,7 @@ import jakarta.servlet.http.HttpServletRequest;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationTrustResolver; import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.core.GrantedAuthorityDefaults; import org.springframework.security.config.core.GrantedAuthorityDefaults;
@ -66,7 +67,7 @@ public final class ServletApiConfigurer<H extends HttpSecurityBuilder<H>>
/** /**
* Creates a new instance * Creates a new instance
* @see HttpSecurity#servletApi() * @see HttpSecurity#servletApi(Customizer)
*/ */
public ServletApiConfigurer() { public ServletApiConfigurer() {
} }

View File

@ -152,7 +152,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
/** /**
* Creates a new instance * Creates a new instance
* @see HttpSecurity#sessionManagement() * @see HttpSecurity#sessionManagement(Customizer)
*/ */
public SessionManagementConfigurer() { public SessionManagementConfigurer() {
} }
@ -775,17 +775,6 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
return this; return this;
} }
/**
* Used to chain back to the {@link SessionManagementConfigurer}
* @return the {@link SessionManagementConfigurer} for further customizations
* @deprecated For removal in 7.0. Use {@link #sessionConcurrency(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public SessionManagementConfigurer<H> and() {
return SessionManagementConfigurer.this;
}
} }
} }

View File

@ -248,7 +248,7 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
} }
public H and() { public H and() {
return UrlAuthorizationConfigurer.this.and(); return UrlAuthorizationConfigurer.this.getBuilder();
} }
} }

View File

@ -46,6 +46,7 @@ import org.springframework.security.web.webauthn.registration.DefaultWebAuthnReg
import org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsFilter; import org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsFilter;
import org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsRepository; import org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsRepository;
import org.springframework.security.web.webauthn.registration.WebAuthnRegistrationFilter; import org.springframework.security.web.webauthn.registration.WebAuthnRegistrationFilter;
import org.springframework.util.Assert;
/** /**
* Configures WebAuthn for Spring Security applications * Configures WebAuthn for Spring Security applications
@ -75,6 +76,7 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
* @return the {@link WebAuthnConfigurer} for further customization * @return the {@link WebAuthnConfigurer} for further customization
*/ */
public WebAuthnConfigurer<H> rpId(String rpId) { public WebAuthnConfigurer<H> rpId(String rpId) {
Assert.hasText(rpId, "rpId be null or empty");
this.rpId = rpId; this.rpId = rpId;
return this; return this;
} }
@ -85,6 +87,7 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
* @return the {@link WebAuthnConfigurer} for further customization * @return the {@link WebAuthnConfigurer} for further customization
*/ */
public WebAuthnConfigurer<H> rpName(String rpName) { public WebAuthnConfigurer<H> rpName(String rpName) {
Assert.hasText(rpName, "rpName can't be null or empty");
this.rpName = rpName; this.rpName = rpName;
return this; return this;
} }
@ -106,6 +109,7 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
* @see #allowedOrigins(String...) * @see #allowedOrigins(String...)
*/ */
public WebAuthnConfigurer<H> allowedOrigins(Set<String> allowedOrigins) { public WebAuthnConfigurer<H> allowedOrigins(Set<String> allowedOrigins) {
Assert.notNull(allowedOrigins, "allowedOrigins can't be null");
this.allowedOrigins = allowedOrigins; this.allowedOrigins = allowedOrigins;
return this; return this;
} }
@ -129,6 +133,7 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
* @return the {@link WebAuthnConfigurer} for further customization * @return the {@link WebAuthnConfigurer} for further customization
*/ */
public WebAuthnConfigurer<H> messageConverter(HttpMessageConverter<Object> converter) { public WebAuthnConfigurer<H> messageConverter(HttpMessageConverter<Object> converter) {
Assert.notNull(converter, "converter can't be null");
this.converter = converter; this.converter = converter;
return this; return this;
} }
@ -140,15 +145,15 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
*/ */
public WebAuthnConfigurer<H> creationOptionsRepository( public WebAuthnConfigurer<H> creationOptionsRepository(
PublicKeyCredentialCreationOptionsRepository creationOptionsRepository) { PublicKeyCredentialCreationOptionsRepository creationOptionsRepository) {
Assert.notNull(creationOptionsRepository, "creationOptionsRepository can't be null");
this.creationOptionsRepository = creationOptionsRepository; this.creationOptionsRepository = creationOptionsRepository;
return this; return this;
} }
@Override @Override
public void configure(H http) throws Exception { public void configure(H http) throws Exception {
UserDetailsService userDetailsService = getSharedOrBean(http, UserDetailsService.class).orElseGet(() -> { UserDetailsService userDetailsService = getSharedOrBean(http, UserDetailsService.class)
throw new IllegalStateException("Missing UserDetailsService Bean"); .orElseThrow(() -> new IllegalStateException("Missing UserDetailsService Bean"));
});
PublicKeyCredentialUserEntityRepository userEntities = getSharedOrBean(http, PublicKeyCredentialUserEntityRepository userEntities = getSharedOrBean(http,
PublicKeyCredentialUserEntityRepository.class) PublicKeyCredentialUserEntityRepository.class)
.orElse(userEntityRepository()); .orElse(userEntityRepository());
@ -238,12 +243,9 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
PublicKeyCredentialUserEntityRepository userEntities, UserCredentialRepository userCredentials) { PublicKeyCredentialUserEntityRepository userEntities, UserCredentialRepository userCredentials) {
Optional<WebAuthnRelyingPartyOperations> webauthnOperationsBean = getBeanOrNull( Optional<WebAuthnRelyingPartyOperations> webauthnOperationsBean = getBeanOrNull(
WebAuthnRelyingPartyOperations.class); WebAuthnRelyingPartyOperations.class);
if (webauthnOperationsBean.isPresent()) { return webauthnOperationsBean.orElseGet(() -> new Webauthn4JRelyingPartyOperations(userEntities,
return webauthnOperationsBean.get(); userCredentials, PublicKeyCredentialRpEntity.builder().id(this.rpId).name(this.rpName).build(),
} this.allowedOrigins));
Webauthn4JRelyingPartyOperations result = new Webauthn4JRelyingPartyOperations(userEntities, userCredentials,
PublicKeyCredentialRpEntity.builder().id(this.rpId).name(this.rpName).build(), this.allowedOrigins);
return result;
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,6 +21,7 @@ import jakarta.servlet.http.HttpServletRequest;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.security.authentication.AuthenticationDetailsSource; import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
@ -90,7 +91,7 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>>
/** /**
* Creates a new instance * Creates a new instance
* *
* @see HttpSecurity#x509() * @see HttpSecurity#x509(Customizer)
*/ */
public X509Configurer() { public X509Configurer() {
} }
@ -161,7 +162,10 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>>
* @param subjectPrincipalRegex the regex to extract the user principal from the * @param subjectPrincipalRegex the regex to extract the user principal from the
* certificate (i.e. "CN=(.*?)(?:,|$)"). * certificate (i.e. "CN=(.*?)(?:,|$)").
* @return the {@link X509Configurer} for further customizations * @return the {@link X509Configurer} for further customizations
* @deprecated Please use {{@link #x509PrincipalExtractor(X509PrincipalExtractor)}
* instead
*/ */
@Deprecated
public X509Configurer<H> subjectPrincipalRegex(String subjectPrincipalRegex) { public X509Configurer<H> subjectPrincipalRegex(String subjectPrincipalRegex) {
SubjectDnX509PrincipalExtractor principalExtractor = new SubjectDnX509PrincipalExtractor(); SubjectDnX509PrincipalExtractor principalExtractor = new SubjectDnX509PrincipalExtractor();
principalExtractor.setSubjectDnRegex(subjectPrincipalRegex); principalExtractor.setSubjectDnRegex(subjectPrincipalRegex);

View File

@ -141,18 +141,6 @@ public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>>
return this; return this;
} }
/**
* Returns the {@link AuthorizationCodeGrantConfigurer} for configuring the OAuth 2.0
* Authorization Code Grant.
* @return the {@link AuthorizationCodeGrantConfigurer}
* @deprecated For removal in 7.0. Use {@link #authorizationCodeGrant(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public AuthorizationCodeGrantConfigurer authorizationCodeGrant() {
return this.authorizationCodeGrantConfigurer;
}
/** /**
* Configures the OAuth 2.0 Authorization Code Grant. * Configures the OAuth 2.0 Authorization Code Grant.
* @param authorizationCodeGrantCustomizer the {@link Customizer} to provide more * @param authorizationCodeGrantCustomizer the {@link Customizer} to provide more
@ -242,17 +230,6 @@ public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>>
return this; return this;
} }
/**
* Returns the {@link OAuth2ClientConfigurer} for further configuration.
* @return the {@link OAuth2ClientConfigurer}
* @deprecated For removal in 7.0. Use {@link #authorizationCodeGrant(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2ClientConfigurer<B> and() {
return OAuth2ClientConfigurer.this;
}
private void init(B builder) { private void init(B builder) {
OAuth2AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider = new OAuth2AuthorizationCodeAuthenticationProvider( OAuth2AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider = new OAuth2AuthorizationCodeAuthenticationProvider(
getAccessTokenResponseClient()); getAccessTokenResponseClient());

View File

@ -155,7 +155,7 @@ import org.springframework.util.ReflectionUtils;
* @author Kazuki Shimizu * @author Kazuki Shimizu
* @author Ngoc Nhan * @author Ngoc Nhan
* @since 5.0 * @since 5.0
* @see HttpSecurity#oauth2Login() * @see HttpSecurity#oauth2Login(Customizer)
* @see OAuth2AuthorizationRequestRedirectFilter * @see OAuth2AuthorizationRequestRedirectFilter
* @see OAuth2LoginAuthenticationFilter * @see OAuth2LoginAuthenticationFilter
* @see ClientRegistrationRepository * @see ClientRegistrationRepository
@ -246,18 +246,6 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
return this; return this;
} }
/**
* Returns the {@link AuthorizationEndpointConfig} for configuring the Authorization
* Server's Authorization Endpoint.
* @return the {@link AuthorizationEndpointConfig}
* @deprecated For removal in 7.0. Use {@link #authorizationEndpoint(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public AuthorizationEndpointConfig authorizationEndpoint() {
return this.authorizationEndpointConfig;
}
/** /**
* Configures the Authorization Server's Authorization Endpoint. * Configures the Authorization Server's Authorization Endpoint.
* @param authorizationEndpointCustomizer the {@link Customizer} to provide more * @param authorizationEndpointCustomizer the {@link Customizer} to provide more
@ -270,21 +258,6 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
return this; return this;
} }
/**
* Returns the {@link TokenEndpointConfig} for configuring the Authorization Server's
* Token Endpoint.
* @return the {@link TokenEndpointConfig}
* @deprecated For removal in 7.0. Use {@link #tokenEndpoint(Customizer)} or
* {@code tokenEndpoint(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public TokenEndpointConfig tokenEndpoint() {
return this.tokenEndpointConfig;
}
/** /**
* Configures the Authorization Server's Token Endpoint. * Configures the Authorization Server's Token Endpoint.
* @param tokenEndpointCustomizer the {@link Customizer} to provide more options for * @param tokenEndpointCustomizer the {@link Customizer} to provide more options for
@ -297,18 +270,6 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
return this; return this;
} }
/**
* Returns the {@link RedirectionEndpointConfig} for configuring the Client's
* Redirection Endpoint.
* @return the {@link RedirectionEndpointConfig}
* @deprecated For removal in 7.0. Use {@link #redirectionEndpoint(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public RedirectionEndpointConfig redirectionEndpoint() {
return this.redirectionEndpointConfig;
}
/** /**
* Configures the Client's Redirection Endpoint. * Configures the Client's Redirection Endpoint.
* @param redirectionEndpointCustomizer the {@link Customizer} to provide more options * @param redirectionEndpointCustomizer the {@link Customizer} to provide more options
@ -321,21 +282,6 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
return this; return this;
} }
/**
* Returns the {@link UserInfoEndpointConfig} for configuring the Authorization
* Server's UserInfo Endpoint.
* @return the {@link UserInfoEndpointConfig}
* @deprecated For removal in 7.0. Use {@link #userInfoEndpoint(Customizer)} or
* {@code userInfoEndpoint(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public UserInfoEndpointConfig userInfoEndpoint() {
return this.userInfoEndpointConfig;
}
/** /**
* Configures the Authorization Server's UserInfo Endpoint. * Configures the Authorization Server's UserInfo Endpoint.
* @param userInfoEndpointCustomizer the {@link Customizer} to provide more options * @param userInfoEndpointCustomizer the {@link Customizer} to provide more options
@ -404,11 +350,9 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
oidcAuthorizationCodeAuthenticationProvider.setAuthoritiesMapper(userAuthoritiesMapper); oidcAuthorizationCodeAuthenticationProvider.setAuthoritiesMapper(userAuthoritiesMapper);
oidcAuthorizedClientRefreshedEventListener.setAuthoritiesMapper(userAuthoritiesMapper); oidcAuthorizedClientRefreshedEventListener.setAuthoritiesMapper(userAuthoritiesMapper);
} }
oidcAuthorizationCodeAuthenticationProvider = this.postProcess(oidcAuthorizationCodeAuthenticationProvider); http.authenticationProvider(this.postProcess(oidcAuthorizationCodeAuthenticationProvider));
http.authenticationProvider(oidcAuthorizationCodeAuthenticationProvider);
oidcAuthorizedClientRefreshedEventListener = this.postProcess(oidcAuthorizedClientRefreshedEventListener); registerDelegateApplicationListener(this.postProcess(oidcAuthorizedClientRefreshedEventListener));
registerDelegateApplicationListener(oidcAuthorizedClientRefreshedEventListener);
configureOidcUserRefreshedEventListener(http); configureOidcUserRefreshedEventListener(http);
} }
else { else {
@ -724,17 +668,6 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
return this; return this;
} }
/**
* Returns the {@link OAuth2LoginConfigurer} for further configuration.
* @return the {@link OAuth2LoginConfigurer}
* @deprecated For removal in 7.0. Use {@link #authorizationEndpoint(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2LoginConfigurer<B> and() {
return OAuth2LoginConfigurer.this;
}
} }
/** /**
@ -761,20 +694,6 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
return this; return this;
} }
/**
* Returns the {@link OAuth2LoginConfigurer} for further configuration.
* @return the {@link OAuth2LoginConfigurer}
* @deprecated For removal in 7.0. Use {@link #tokenEndpoint(Customizer)} or
* {@code tokenEndpoint(Customizer.withDefaults())} to stick with defaults. See
* the <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2LoginConfigurer<B> and() {
return OAuth2LoginConfigurer.this;
}
} }
/** /**
@ -799,17 +718,6 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
return this; return this;
} }
/**
* Returns the {@link OAuth2LoginConfigurer} for further configuration.
* @return the {@link OAuth2LoginConfigurer}
* @deprecated For removal in 7.0. Use {@link #redirectionEndpoint(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2LoginConfigurer<B> and() {
return OAuth2LoginConfigurer.this;
}
} }
/** /**
@ -864,17 +772,6 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
return this; return this;
} }
/**
* Returns the {@link OAuth2LoginConfigurer} for further configuration.
* @return the {@link OAuth2LoginConfigurer}
* @deprecated For removal in 7.0. Use {@link #userInfoEndpoint(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2LoginConfigurer<B> and() {
return OAuth2LoginConfigurer.this;
}
} }
private static class OidcAuthenticationRequestChecker implements AuthenticationProvider { private static class OidcAuthenticationRequestChecker implements AuthenticationProvider {

View File

@ -18,11 +18,6 @@ package org.springframework.security.config.annotation.web.configurers.oauth2.cl
import java.util.function.Function; import java.util.function.Function;
import com.nimbusds.jose.JOSEObjectType;
import com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier;
import com.nimbusds.jose.proc.JOSEObjectTypeVerifier;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
@ -38,6 +33,7 @@ import org.springframework.security.oauth2.jwt.BadJwtException;
import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtDecoderFactory; import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
import org.springframework.security.oauth2.jwt.JwtTypeValidator;
import org.springframework.security.oauth2.jwt.JwtValidators; import org.springframework.security.oauth2.jwt.JwtValidators;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -67,8 +63,10 @@ final class OidcBackChannelLogoutAuthenticationProvider implements Authenticatio
* Construct an {@link OidcBackChannelLogoutAuthenticationProvider} * Construct an {@link OidcBackChannelLogoutAuthenticationProvider}
*/ */
OidcBackChannelLogoutAuthenticationProvider() { OidcBackChannelLogoutAuthenticationProvider() {
JwtTypeValidator type = new JwtTypeValidator("JWT", "logout+jwt");
type.setAllowEmpty(true);
Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidator = (clientRegistration) -> JwtValidators Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidator = (clientRegistration) -> JwtValidators
.createDefaultWithValidators(new OidcBackChannelLogoutTokenValidator(clientRegistration)); .createDefaultWithValidators(type, new OidcBackChannelLogoutTokenValidator(clientRegistration));
this.logoutTokenDecoderFactory = (clientRegistration) -> { this.logoutTokenDecoderFactory = (clientRegistration) -> {
String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri(); String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
if (!StringUtils.hasText(jwkSetUri)) { if (!StringUtils.hasText(jwkSetUri)) {
@ -79,11 +77,7 @@ final class OidcBackChannelLogoutAuthenticationProvider implements Authenticatio
null); null);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
} }
JOSEObjectTypeVerifier<SecurityContext> typeVerifier = new DefaultJOSEObjectTypeVerifier<>(null, NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build();
JOSEObjectType.JWT, new JOSEObjectType("logout+jwt"));
NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri)
.jwtProcessorCustomizer((processor) -> processor.setJWSTypeVerifier(typeVerifier))
.build();
decoder.setJwtValidator(jwtValidator.apply(clientRegistration)); decoder.setJwtValidator(jwtValidator.apply(clientRegistration));
decoder.setClaimSetConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverter()); decoder.setClaimSetConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverter());
return decoder; return decoder;

View File

@ -127,7 +127,7 @@ public final class OidcBackChannelLogoutHandler implements LogoutHandler {
String computeLogoutEndpoint(HttpServletRequest request, OidcBackChannelLogoutAuthentication token) { String computeLogoutEndpoint(HttpServletRequest request, OidcBackChannelLogoutAuthentication token) {
// @formatter:off // @formatter:off
UriComponents uriComponents = UriComponentsBuilder UriComponents uriComponents = UriComponentsBuilder
.fromHttpUrl(UrlUtils.buildFullRequestUrl(request)) .fromUriString(UrlUtils.buildFullRequestUrl(request))
.replacePath(request.getContextPath()) .replacePath(request.getContextPath())
.replaceQuery(null) .replaceQuery(null)
.fragment(null) .fragment(null)

View File

@ -111,11 +111,6 @@ public final class OidcLogoutConfigurer<B extends HttpSecurityBuilder<B>>
return this; return this;
} }
@Deprecated(forRemoval = true, since = "6.2")
public B and() {
return getBuilder();
}
@Override @Override
public void configure(B builder) throws Exception { public void configure(B builder) throws Exception {
if (this.backChannel != null) { if (this.backChannel != null) {

View File

@ -17,39 +17,54 @@
package org.springframework.security.config.annotation.web.configurers.oauth2.server.resource; package org.springframework.security.config.annotation.web.configurers.oauth2.server.resource;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationManagerResolver;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes; import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
import org.springframework.security.oauth2.server.resource.authentication.DPoPAuthenticationProvider; import org.springframework.security.oauth2.server.resource.authentication.DPoPAuthenticationProvider;
import org.springframework.security.oauth2.server.resource.authentication.DPoPAuthenticationToken; import org.springframework.security.oauth2.server.resource.authentication.DPoPAuthenticationToken;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AuthenticationConverter; import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler; import org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationFilter; import org.springframework.security.web.authentication.AuthenticationFilter;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository; import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/** /**
* An {@link AbstractHttpConfigurer} for OAuth 2.0 Demonstrating Proof of Possession
* (DPoP) support.
*
* @author Joe Grandja * @author Joe Grandja
* @since 6.5 * @since 6.5
* @see DPoPAuthenticationProvider * @see DPoPAuthenticationProvider
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc9449">RFC 9449
* OAuth 2.0 Demonstrating Proof of Possession (DPoP)</a>
*/ */
final class DPoPAuthenticationConfigurer<B extends HttpSecurityBuilder<B>> final class DPoPAuthenticationConfigurer<B extends HttpSecurityBuilder<B>>
extends AbstractHttpConfigurer<DPoPAuthenticationConfigurer<B>, B> { extends AbstractHttpConfigurer<DPoPAuthenticationConfigurer<B>, B> {
@ -65,7 +80,7 @@ final class DPoPAuthenticationConfigurer<B extends HttpSecurityBuilder<B>>
@Override @Override
public void configure(B http) { public void configure(B http) {
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class); AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
http.authenticationProvider(new DPoPAuthenticationProvider(authenticationManager)); http.authenticationProvider(new DPoPAuthenticationProvider(getTokenAuthenticationManager(http)));
AuthenticationFilter authenticationFilter = new AuthenticationFilter(authenticationManager, AuthenticationFilter authenticationFilter = new AuthenticationFilter(authenticationManager,
getAuthenticationConverter()); getAuthenticationConverter());
authenticationFilter.setRequestMatcher(getRequestMatcher()); authenticationFilter.setRequestMatcher(getRequestMatcher());
@ -76,6 +91,23 @@ final class DPoPAuthenticationConfigurer<B extends HttpSecurityBuilder<B>>
http.addFilter(authenticationFilter); http.addFilter(authenticationFilter);
} }
private AuthenticationManager getTokenAuthenticationManager(B http) {
OAuth2ResourceServerConfigurer<B> resourceServerConfigurer = http
.getConfigurer(OAuth2ResourceServerConfigurer.class);
final AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver = resourceServerConfigurer
.getAuthenticationManagerResolver();
if (authenticationManagerResolver == null) {
return resourceServerConfigurer.getAuthenticationManager(http);
}
return (authentication) -> {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
AuthenticationManager authenticationManager = authenticationManagerResolver
.resolve(servletRequestAttributes.getRequest());
return authenticationManager.authenticate(authentication);
};
}
private RequestMatcher getRequestMatcher() { private RequestMatcher getRequestMatcher() {
if (this.requestMatcher == null) { if (this.requestMatcher == null) {
this.requestMatcher = new DPoPRequestMatcher(); this.requestMatcher = new DPoPRequestMatcher();
@ -102,7 +134,7 @@ final class DPoPAuthenticationConfigurer<B extends HttpSecurityBuilder<B>>
private AuthenticationFailureHandler getAuthenticationFailureHandler() { private AuthenticationFailureHandler getAuthenticationFailureHandler() {
if (this.authenticationFailureHandler == null) { if (this.authenticationFailureHandler == null) {
this.authenticationFailureHandler = new AuthenticationEntryPointFailureHandler( this.authenticationFailureHandler = new AuthenticationEntryPointFailureHandler(
new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)); new DPoPAuthenticationEntryPoint());
} }
return this.authenticationFailureHandler; return this.authenticationFailureHandler;
} }
@ -161,4 +193,47 @@ final class DPoPAuthenticationConfigurer<B extends HttpSecurityBuilder<B>>
} }
private static final class DPoPAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authenticationException) {
Map<String, String> parameters = new LinkedHashMap<>();
if (authenticationException instanceof OAuth2AuthenticationException oauth2AuthenticationException) {
OAuth2Error error = oauth2AuthenticationException.getError();
parameters.put(OAuth2ParameterNames.ERROR, error.getErrorCode());
if (StringUtils.hasText(error.getDescription())) {
parameters.put(OAuth2ParameterNames.ERROR_DESCRIPTION, error.getDescription());
}
if (StringUtils.hasText(error.getUri())) {
parameters.put(OAuth2ParameterNames.ERROR_URI, error.getUri());
}
}
parameters.put("algs",
JwsAlgorithms.RS256 + " " + JwsAlgorithms.RS384 + " " + JwsAlgorithms.RS512 + " "
+ JwsAlgorithms.PS256 + " " + JwsAlgorithms.PS384 + " " + JwsAlgorithms.PS512 + " "
+ JwsAlgorithms.ES256 + " " + JwsAlgorithms.ES384 + " " + JwsAlgorithms.ES512);
String wwwAuthenticate = toWWWAuthenticateHeader(parameters);
response.addHeader(HttpHeaders.WWW_AUTHENTICATE, wwwAuthenticate);
response.setStatus(HttpStatus.UNAUTHORIZED.value());
}
private static String toWWWAuthenticateHeader(Map<String, String> parameters) {
StringBuilder wwwAuthenticate = new StringBuilder();
wwwAuthenticate.append(OAuth2AccessToken.TokenType.DPOP.getValue());
if (!parameters.isEmpty()) {
wwwAuthenticate.append(" ");
int i = 0;
for (Map.Entry<String, String> entry : parameters.entrySet()) {
wwwAuthenticate.append(entry.getKey()).append("=\"").append(entry.getValue()).append("\"");
if (i++ != parameters.size() - 1) {
wwwAuthenticate.append(", ");
}
}
}
return wwwAuthenticate.toString();
}
}
} }

View File

@ -37,6 +37,7 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHt
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer; import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer; import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.JwtDecoder;
@ -49,13 +50,14 @@ import org.springframework.security.oauth2.server.resource.introspection.OpaqueT
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector; import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint; import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver; import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler; import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter; import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.AccessDeniedHandlerImpl; import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.security.web.access.DelegatingAccessDeniedHandler; import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.csrf.CsrfException; import org.springframework.security.web.csrf.CsrfException;
import org.springframework.security.web.util.matcher.AndRequestMatcher; import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher; import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
@ -156,7 +158,7 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
private AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver; private AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
private BearerTokenResolver bearerTokenResolver; private AuthenticationConverter authenticationConverter;
private JwtConfigurer jwtConfigurer; private JwtConfigurer jwtConfigurer;
@ -196,22 +198,20 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
public OAuth2ResourceServerConfigurer<H> bearerTokenResolver(BearerTokenResolver bearerTokenResolver) { public OAuth2ResourceServerConfigurer<H> bearerTokenResolver(BearerTokenResolver bearerTokenResolver) {
Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null"); Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null");
this.bearerTokenResolver = bearerTokenResolver; this.authenticationConverter = new BearerTokenResolverHoldingAuthenticationConverter(bearerTokenResolver);
return this; return this;
} }
/** /**
* @deprecated For removal in 7.0. Use {@link #jwt(Customizer)} or * Sets the {@link AuthenticationConverter} to use.
* {@code jwt(Customizer.withDefaults())} to stick with defaults. See the <a href= * @param authenticationConverter the authentication converter
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a> * @return the {@link OAuth2ResourceServerConfigurer} for further configuration
* for more details. * @since 7.0
*/ */
@Deprecated(since = "6.1", forRemoval = true) public OAuth2ResourceServerConfigurer<H> authenticationConverter(AuthenticationConverter authenticationConverter) {
public JwtConfigurer jwt() { Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
if (this.jwtConfigurer == null) { this.authenticationConverter = authenticationConverter;
this.jwtConfigurer = new JwtConfigurer(this.context); return this;
}
return this.jwtConfigurer;
} }
/** /**
@ -228,21 +228,6 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
return this; return this;
} }
/**
* @deprecated For removal in 7.0. Use {@link #opaqueToken(Customizer)} or
* {@code opaqueToken(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public OpaqueTokenConfigurer opaqueToken() {
if (this.opaqueTokenConfigurer == null) {
this.opaqueTokenConfigurer = new OpaqueTokenConfigurer(this.context);
}
return this.opaqueTokenConfigurer;
}
/** /**
* Enables opaque bearer token support. * Enables opaque bearer token support.
* @param opaqueTokenCustomizer the {@link Customizer} to provide more options for the * @param opaqueTokenCustomizer the {@link Customizer} to provide more options for the
@ -271,16 +256,15 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
@Override @Override
public void configure(H http) { public void configure(H http) {
BearerTokenResolver bearerTokenResolver = getBearerTokenResolver();
this.requestMatcher.setBearerTokenResolver(bearerTokenResolver);
AuthenticationManagerResolver resolver = this.authenticationManagerResolver; AuthenticationManagerResolver resolver = this.authenticationManagerResolver;
if (resolver == null) { if (resolver == null) {
AuthenticationManager authenticationManager = getAuthenticationManager(http); AuthenticationManager authenticationManager = getAuthenticationManager(http);
resolver = (request) -> authenticationManager; resolver = (request) -> authenticationManager;
} }
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver); AuthenticationConverter converter = getAuthenticationConverter();
filter.setBearerTokenResolver(bearerTokenResolver); this.requestMatcher.setAuthenticationConverter(converter);
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver, converter);
filter.setAuthenticationEntryPoint(this.authenticationEntryPoint); filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
filter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy()); filter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
filter = postProcess(filter); filter = postProcess(filter);
@ -363,16 +347,33 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
return http.getSharedObject(AuthenticationManager.class); return http.getSharedObject(AuthenticationManager.class);
} }
BearerTokenResolver getBearerTokenResolver() { AuthenticationManagerResolver<HttpServletRequest> getAuthenticationManagerResolver() {
if (this.bearerTokenResolver == null) { return this.authenticationManagerResolver;
if (this.context.getBeanNamesForType(BearerTokenResolver.class).length > 0) { }
this.bearerTokenResolver = this.context.getBean(BearerTokenResolver.class);
} AuthenticationConverter getAuthenticationConverter() {
else { if (this.authenticationConverter != null) {
this.bearerTokenResolver = new DefaultBearerTokenResolver(); return this.authenticationConverter;
}
} }
return this.bearerTokenResolver; if (this.context.getBeanNamesForType(AuthenticationConverter.class).length > 0) {
this.authenticationConverter = this.context.getBean(AuthenticationConverter.class);
}
else if (this.context.getBeanNamesForType(BearerTokenResolver.class).length > 0) {
BearerTokenResolver bearerTokenResolver = this.context.getBean(BearerTokenResolver.class);
this.authenticationConverter = new BearerTokenResolverHoldingAuthenticationConverter(bearerTokenResolver);
}
else {
this.authenticationConverter = new BearerTokenAuthenticationConverter();
}
return this.authenticationConverter;
}
BearerTokenResolver getBearerTokenResolver() {
AuthenticationConverter authenticationConverter = getAuthenticationConverter();
if (authenticationConverter instanceof OAuth2ResourceServerConfigurer.BearerTokenResolverHoldingAuthenticationConverter bearer) {
return bearer.bearerTokenResolver;
}
return null;
} }
public class JwtConfigurer { public class JwtConfigurer {
@ -411,17 +412,6 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
return this; return this;
} }
/**
* @deprecated For removal in 7.0. Use {@link #jwt(Customizer)} or
* {@code jwt(Customizer.withDefaults())} to stick with defaults. See the <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2ResourceServerConfigurer<H> and() {
return OAuth2ResourceServerConfigurer.this;
}
Converter<Jwt, ? extends AbstractAuthenticationToken> getJwtAuthenticationConverter() { Converter<Jwt, ? extends AbstractAuthenticationToken> getJwtAuthenticationConverter() {
if (this.jwtAuthenticationConverter == null) { if (this.jwtAuthenticationConverter == null) {
if (this.context.getBeanNamesForType(JwtAuthenticationConverter.class).length > 0) { if (this.context.getBeanNamesForType(JwtAuthenticationConverter.class).length > 0) {
@ -560,21 +550,41 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
private static final class BearerTokenRequestMatcher implements RequestMatcher { private static final class BearerTokenRequestMatcher implements RequestMatcher {
private BearerTokenResolver bearerTokenResolver; private AuthenticationConverter authenticationConverter;
@Override @Override
public boolean matches(HttpServletRequest request) { public boolean matches(HttpServletRequest request) {
try { try {
return this.bearerTokenResolver.resolve(request) != null; return this.authenticationConverter.convert(request) != null;
} }
catch (OAuth2AuthenticationException ex) { catch (OAuth2AuthenticationException ex) {
return false; return false;
} }
} }
void setBearerTokenResolver(BearerTokenResolver tokenResolver) { void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {
Assert.notNull(tokenResolver, "resolver cannot be null"); Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
this.bearerTokenResolver = tokenResolver; this.authenticationConverter = authenticationConverter;
}
}
private static final class BearerTokenResolverHoldingAuthenticationConverter implements AuthenticationConverter {
private final BearerTokenResolver bearerTokenResolver;
private final AuthenticationConverter authenticationConverter;
BearerTokenResolverHoldingAuthenticationConverter(BearerTokenResolver bearerTokenResolver) {
this.bearerTokenResolver = bearerTokenResolver;
BearerTokenAuthenticationConverter authenticationConverter = new BearerTokenAuthenticationConverter();
authenticationConverter.setBearerTokenResolver(bearerTokenResolver);
this.authenticationConverter = authenticationConverter;
}
@Override
public Authentication convert(HttpServletRequest request) {
return this.authenticationConverter.convert(request);
} }
} }

View File

@ -27,6 +27,7 @@ import org.opensaml.core.Version;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.RequestMatcherFactory; import org.springframework.security.config.annotation.web.RequestMatcherFactory;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@ -111,7 +112,7 @@ import org.springframework.util.StringUtils;
* </ul> * </ul>
* *
* @since 5.2 * @since 5.2
* @see HttpSecurity#saml2Login() * @see HttpSecurity#saml2Login(Customizer)
* @see Saml2WebSsoAuthenticationFilter * @see Saml2WebSsoAuthenticationFilter
* @see Saml2WebSsoAuthenticationRequestFilter * @see Saml2WebSsoAuthenticationRequestFilter
* @see RelyingPartyRegistrationRepository * @see RelyingPartyRegistrationRepository

View File

@ -34,6 +34,8 @@ import org.springframework.security.config.annotation.web.configurers.LogoutConf
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
import org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertionAccessor;
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml4LogoutRequestValidator; import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml4LogoutRequestValidator;
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml4LogoutResponseValidator; import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml4LogoutResponseValidator;
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml5LogoutRequestValidator; import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml5LogoutRequestValidator;
@ -133,7 +135,7 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
/** /**
* Creates a new instance * Creates a new instance
* @see HttpSecurity#logout() * @see HttpSecurity#logout(Customizer)
*/ */
public Saml2LogoutConfigurer(ApplicationContext context) { public Saml2LogoutConfigurer(ApplicationContext context) {
this.context = context; this.context = context;
@ -156,7 +158,7 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
* @param logoutUrl the URL that will invoke logout * @param logoutUrl the URL that will invoke logout
* @return the {@link LogoutConfigurer} for further customizations * @return the {@link LogoutConfigurer} for further customizations
* @see LogoutConfigurer#logoutUrl(String) * @see LogoutConfigurer#logoutUrl(String)
* @see HttpSecurity#csrf() * @see HttpSecurity#csrf(Customizer)
*/ */
public Saml2LogoutConfigurer<H> logoutUrl(String logoutUrl) { public Saml2LogoutConfigurer<H> logoutUrl(String logoutUrl) {
this.logoutUrl = logoutUrl; this.logoutUrl = logoutUrl;
@ -175,20 +177,6 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
return this; return this;
} }
/**
* Get configurer for SAML 2.0 Logout Request components
* @return the {@link LogoutRequestConfigurer} for further customizations
* @deprecated For removal in 7.0. Use {@link #logoutRequest(Customizer)} or
* {@code logoutRequest(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public LogoutRequestConfigurer logoutRequest() {
return this.logoutRequestConfigurer;
}
/** /**
* Configures SAML 2.0 Logout Request components * Configures SAML 2.0 Logout Request components
* @param logoutRequestConfigurerCustomizer the {@link Customizer} to provide more * @param logoutRequestConfigurerCustomizer the {@link Customizer} to provide more
@ -201,20 +189,6 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
return this; return this;
} }
/**
* Get configurer for SAML 2.0 Logout Response components
* @return the {@link LogoutResponseConfigurer} for further customizations
* @deprecated For removal in 7.0. Use {@link #logoutResponse(Customizer)} or
* {@code logoutResponse(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public LogoutResponseConfigurer logoutResponse() {
return this.logoutResponseConfigurer;
}
/** /**
* Configures SAML 2.0 Logout Response components * Configures SAML 2.0 Logout Response components
* @param logoutResponseConfigurerCustomizer the {@link Customizer} to provide more * @param logoutResponseConfigurerCustomizer the {@link Customizer} to provide more
@ -404,18 +378,6 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
return this; return this;
} }
/**
* @deprecated For removal in 7.0. Use {@link #logoutRequest(Customizer)} or
* {@code logoutRequest(Customizer.withDefaults())} to stick with defaults. See
* the <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public Saml2LogoutConfigurer<H> and() {
return Saml2LogoutConfigurer.this;
}
private Saml2LogoutRequestValidator logoutRequestValidator() { private Saml2LogoutRequestValidator logoutRequestValidator() {
if (this.logoutRequestValidator != null) { if (this.logoutRequestValidator != null) {
return this.logoutRequestValidator; return this.logoutRequestValidator;
@ -486,18 +448,6 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
return this; return this;
} }
/**
* @deprecated For removal in 7.0. Use {@link #logoutResponse(Customizer)} or
* {@code logoutResponse(Customizer.withDefaults())} to stick with defaults. See
* the <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public Saml2LogoutConfigurer<H> and() {
return Saml2LogoutConfigurer.this;
}
private Saml2LogoutResponseValidator logoutResponseValidator() { private Saml2LogoutResponseValidator logoutResponseValidator() {
if (this.logoutResponseValidator != null) { if (this.logoutResponseValidator != null) {
return this.logoutResponseValidator; return this.logoutResponseValidator;
@ -534,7 +484,13 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
if (authentication == null) { if (authentication == null) {
return false; return false;
} }
return authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal; if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal) {
return true;
}
if (authentication.getCredentials() instanceof Saml2ResponseAssertionAccessor) {
return true;
}
return authentication instanceof Saml2Authentication;
} }
} }

View File

@ -43,6 +43,8 @@ import org.springframework.security.web.reactive.result.method.annotation.Curren
import org.springframework.web.reactive.config.WebFluxConfigurer; import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer; import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer;
import static org.springframework.security.config.Customizer.withDefaults;
/** /**
* @author Rob Winch * @author Rob Winch
* @author Dan Zheng * @author Dan Zheng
@ -156,8 +158,8 @@ class ServerHttpSecurityConfiguration {
ContextAwareServerHttpSecurity http = new ContextAwareServerHttpSecurity(); ContextAwareServerHttpSecurity http = new ContextAwareServerHttpSecurity();
// @formatter:off // @formatter:off
return http.authenticationManager(authenticationManager()) return http.authenticationManager(authenticationManager())
.headers().and() .headers(withDefaults())
.logout().and(); .logout(withDefaults());
// @formatter:on // @formatter:on
} }

View File

@ -40,6 +40,8 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.web.reactive.result.view.AbstractView; import org.springframework.web.reactive.result.view.AbstractView;
import static org.springframework.security.config.Customizer.withDefaults;
/** /**
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
@ -121,13 +123,13 @@ class WebFluxSecurityConfiguration {
* @return * @return
*/ */
private SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { private SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange().anyExchange().authenticated(); http.authorizeExchange((authorize) -> authorize.anyExchange().authenticated());
if (isOAuth2Present && OAuth2ClasspathGuard.shouldConfigure(this.context)) { if (isOAuth2Present && OAuth2ClasspathGuard.shouldConfigure(this.context)) {
OAuth2ClasspathGuard.configure(this.context, http); OAuth2ClasspathGuard.configure(this.context, http);
} }
else { else {
http.httpBasic(); http.httpBasic(withDefaults());
http.formLogin(); http.formLogin(withDefaults());
} }
SecurityWebFilterChain result = http.build(); SecurityWebFilterChain result = http.build();
return result; return result;
@ -136,8 +138,8 @@ class WebFluxSecurityConfiguration {
private static class OAuth2ClasspathGuard { private static class OAuth2ClasspathGuard {
static void configure(ApplicationContext context, ServerHttpSecurity http) { static void configure(ApplicationContext context, ServerHttpSecurity http) {
http.oauth2Login(); http.oauth2Login(withDefaults());
http.oauth2Client(); http.oauth2Client(withDefaults());
} }
static boolean shouldConfigure(ApplicationContext context) { static boolean shouldConfigure(ApplicationContext context) {

View File

@ -1,286 +0,0 @@
/*
* Copyright 2002-2022 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.config.annotation.web.socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.messaging.Message;
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
import org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.config.ObjectPostProcessor;
import org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration;
import org.springframework.security.config.annotation.web.messaging.MessageSecurityMetadataSourceRegistry;
import org.springframework.security.messaging.access.expression.DefaultMessageSecurityExpressionHandler;
import org.springframework.security.messaging.access.expression.MessageExpressionVoter;
import org.springframework.security.messaging.access.intercept.ChannelSecurityInterceptor;
import org.springframework.security.messaging.access.intercept.MessageSecurityMetadataSource;
import org.springframework.security.messaging.context.AuthenticationPrincipalArgumentResolver;
import org.springframework.security.messaging.context.SecurityContextChannelInterceptor;
import org.springframework.security.messaging.web.csrf.CsrfChannelInterceptor;
import org.springframework.security.messaging.web.socket.server.CsrfTokenHandshakeInterceptor;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert;
import org.springframework.util.PathMatcher;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.server.HandshakeInterceptor;
import org.springframework.web.socket.server.support.WebSocketHttpRequestHandler;
import org.springframework.web.socket.sockjs.SockJsService;
import org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler;
import org.springframework.web.socket.sockjs.transport.TransportHandlingSockJsService;
/**
* Allows configuring WebSocket Authorization.
*
* <p>
* For example:
* </p>
*
* <pre>
* &#064;Configuration
* public class WebSocketSecurityConfig extends
* AbstractSecurityWebSocketMessageBrokerConfigurer {
*
* &#064;Override
* protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
* messages.simpDestMatchers(&quot;/user/queue/errors&quot;).permitAll()
* .simpDestMatchers(&quot;/admin/**&quot;).hasRole(&quot;ADMIN&quot;).anyMessage()
* .authenticated();
* }
* }
* </pre>
*
* @author Rob Winch
* @since 4.0
* @see WebSocketMessageBrokerSecurityConfiguration
* @deprecated Use {@link EnableWebSocketSecurity} instead
*/
@Order(Ordered.HIGHEST_PRECEDENCE + 100)
@Import(ObjectPostProcessorConfiguration.class)
@Deprecated
public abstract class AbstractSecurityWebSocketMessageBrokerConfigurer
implements WebSocketMessageBrokerConfigurer, SmartInitializingSingleton {
private final WebSocketMessageSecurityMetadataSourceRegistry inboundRegistry = new WebSocketMessageSecurityMetadataSourceRegistry();
private SecurityExpressionHandler<Message<Object>> defaultExpressionHandler = new DefaultMessageSecurityExpressionHandler<>();
private SecurityExpressionHandler<Message<Object>> expressionHandler;
private ApplicationContext context;
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new AuthenticationPrincipalArgumentResolver());
}
@Override
public final void configureClientInboundChannel(ChannelRegistration registration) {
ChannelSecurityInterceptor inboundChannelSecurity = this.context.getBean(ChannelSecurityInterceptor.class);
registration.interceptors(this.context.getBean(SecurityContextChannelInterceptor.class));
if (!sameOriginDisabled()) {
registration.interceptors(this.context.getBean(CsrfChannelInterceptor.class));
}
if (this.inboundRegistry.containsMapping()) {
registration.interceptors(inboundChannelSecurity);
}
customizeClientInboundChannel(registration);
}
private PathMatcher getDefaultPathMatcher() {
try {
return this.context.getBean(SimpAnnotationMethodMessageHandler.class).getPathMatcher();
}
catch (NoSuchBeanDefinitionException ex) {
return new AntPathMatcher();
}
}
/**
* <p>
* Determines if a CSRF token is required for connecting. This protects against remote
* sites from connecting to the application and being able to read/write data over the
* connection. The default is false (the token is required).
* </p>
* <p>
* Subclasses can override this method to disable CSRF protection
* </p>
* @return false if a CSRF token is required for connecting, else true
*/
protected boolean sameOriginDisabled() {
return false;
}
/**
* Allows subclasses to customize the configuration of the {@link ChannelRegistration}
* .
* @param registration the {@link ChannelRegistration} to customize
*/
protected void customizeClientInboundChannel(ChannelRegistration registration) {
}
@Bean
public CsrfChannelInterceptor csrfChannelInterceptor() {
return new CsrfChannelInterceptor();
}
@Bean
public ChannelSecurityInterceptor inboundChannelSecurity(
MessageSecurityMetadataSource messageSecurityMetadataSource) {
ChannelSecurityInterceptor channelSecurityInterceptor = new ChannelSecurityInterceptor(
messageSecurityMetadataSource);
MessageExpressionVoter<Object> voter = new MessageExpressionVoter<>();
voter.setExpressionHandler(getMessageExpressionHandler());
List<AccessDecisionVoter<?>> voters = new ArrayList<>();
voters.add(voter);
AffirmativeBased manager = new AffirmativeBased(voters);
channelSecurityInterceptor.setAccessDecisionManager(manager);
return channelSecurityInterceptor;
}
@Bean
public SecurityContextChannelInterceptor securityContextChannelInterceptor() {
return new SecurityContextChannelInterceptor();
}
@Bean
public MessageSecurityMetadataSource inboundMessageSecurityMetadataSource() {
this.inboundRegistry.expressionHandler(getMessageExpressionHandler());
configureInbound(this.inboundRegistry);
return this.inboundRegistry.createMetadataSource();
}
/**
* @param messages
*/
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
}
@Autowired
public void setApplicationContext(ApplicationContext context) {
this.context = context;
}
@Deprecated
public void setMessageExpessionHandler(List<SecurityExpressionHandler<Message<Object>>> expressionHandlers) {
setMessageExpressionHandler(expressionHandlers);
}
@Autowired(required = false)
public void setMessageExpressionHandler(List<SecurityExpressionHandler<Message<Object>>> expressionHandlers) {
if (expressionHandlers.size() == 1) {
this.expressionHandler = expressionHandlers.get(0);
}
}
@Autowired(required = false)
public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
this.defaultExpressionHandler = objectPostProcessor.postProcess(this.defaultExpressionHandler);
}
private SecurityExpressionHandler<Message<Object>> getMessageExpressionHandler() {
if (this.expressionHandler == null) {
return this.defaultExpressionHandler;
}
return this.expressionHandler;
}
@Override
public void afterSingletonsInstantiated() {
if (sameOriginDisabled()) {
return;
}
String beanName = "stompWebSocketHandlerMapping";
SimpleUrlHandlerMapping mapping = this.context.getBean(beanName, SimpleUrlHandlerMapping.class);
Map<String, Object> mappings = mapping.getHandlerMap();
for (Object object : mappings.values()) {
if (object instanceof SockJsHttpRequestHandler) {
setHandshakeInterceptors((SockJsHttpRequestHandler) object);
}
else if (object instanceof WebSocketHttpRequestHandler) {
setHandshakeInterceptors((WebSocketHttpRequestHandler) object);
}
else {
throw new IllegalStateException("Bean " + beanName + " is expected to contain mappings to either a "
+ "SockJsHttpRequestHandler or a WebSocketHttpRequestHandler but got " + object);
}
}
if (this.inboundRegistry.containsMapping() && !this.inboundRegistry.isSimpDestPathMatcherConfigured()) {
PathMatcher pathMatcher = getDefaultPathMatcher();
this.inboundRegistry.simpDestPathMatcher(pathMatcher);
}
}
private void setHandshakeInterceptors(SockJsHttpRequestHandler handler) {
SockJsService sockJsService = handler.getSockJsService();
Assert.state(sockJsService instanceof TransportHandlingSockJsService,
() -> "sockJsService must be instance of TransportHandlingSockJsService got " + sockJsService);
TransportHandlingSockJsService transportHandlingSockJsService = (TransportHandlingSockJsService) sockJsService;
List<HandshakeInterceptor> handshakeInterceptors = transportHandlingSockJsService.getHandshakeInterceptors();
List<HandshakeInterceptor> interceptorsToSet = new ArrayList<>(handshakeInterceptors.size() + 1);
interceptorsToSet.add(new CsrfTokenHandshakeInterceptor());
interceptorsToSet.addAll(handshakeInterceptors);
transportHandlingSockJsService.setHandshakeInterceptors(interceptorsToSet);
}
private void setHandshakeInterceptors(WebSocketHttpRequestHandler handler) {
List<HandshakeInterceptor> handshakeInterceptors = handler.getHandshakeInterceptors();
List<HandshakeInterceptor> interceptorsToSet = new ArrayList<>(handshakeInterceptors.size() + 1);
interceptorsToSet.add(new CsrfTokenHandshakeInterceptor());
interceptorsToSet.addAll(handshakeInterceptors);
handler.setHandshakeInterceptors(interceptorsToSet);
}
private static class WebSocketMessageSecurityMetadataSourceRegistry extends MessageSecurityMetadataSourceRegistry {
@Override
public MessageSecurityMetadataSource createMetadataSource() {
return super.createMetadataSource();
}
@Override
protected boolean containsMapping() {
return super.containsMapping();
}
@Override
protected boolean isSimpDestPathMatcherConfigured() {
return super.isSimpDestPathMatcherConfigured();
}
}
}

View File

@ -31,6 +31,9 @@ final class MessageMatcherAuthorizationManagerConfiguration {
MessageMatcherDelegatingAuthorizationManager.Builder messageAuthorizationManagerBuilder( MessageMatcherDelegatingAuthorizationManager.Builder messageAuthorizationManagerBuilder(
ApplicationContext context) { ApplicationContext context) {
MessageMatcherFactory.setApplicationContext(context); MessageMatcherFactory.setApplicationContext(context);
if (MessageMatcherFactory.usesPathPatterns()) {
return MessageMatcherDelegatingAuthorizationManager.builder();
}
return MessageMatcherDelegatingAuthorizationManager.builder() return MessageMatcherDelegatingAuthorizationManager.builder()
.simpDestPathMatcher( .simpDestPathMatcher(
() -> (context.getBeanNamesForType(SimpAnnotationMethodMessageHandler.class).length > 0) () -> (context.getBeanNamesForType(SimpAnnotationMethodMessageHandler.class).length > 0)

View File

@ -0,0 +1,43 @@
/*
* Copyright 2002-2023 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.config.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;
/**
* Runtime hints for
* {@link org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration}
*
* @author Marcus da Coregio
*/
class WebSecurityConfigurationRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.reflection()
.registerType(TypeReference
.of("org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy"),
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
hints.reflection()
.registerType(TypeReference.of("org.springframework.web.filter.ServletRequestPathFilter"),
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
}
}

View File

@ -521,11 +521,23 @@ final class AuthenticationConfigBuilder {
filterBuilder.addPropertyValue("authenticationManager", authManager); filterBuilder.addPropertyValue("authenticationManager", authManager);
filterBuilder.addPropertyValue("securityContextHolderStrategy", filterBuilder.addPropertyValue("securityContextHolderStrategy",
authenticationFilterSecurityContextHolderStrategyRef); authenticationFilterSecurityContextHolderStrategyRef);
String regex = x509Elt.getAttribute("subject-principal-regex"); String principalExtractorRef = x509Elt.getAttribute("principal-extractor-ref");
if (StringUtils.hasText(regex)) { String subjectPrincipalRegex = x509Elt.getAttribute("subject-principal-regex");
boolean hasPrincipalExtractorRef = StringUtils.hasText(principalExtractorRef);
boolean hasSubjectPrincipalRegex = StringUtils.hasText(subjectPrincipalRegex);
if (hasPrincipalExtractorRef && hasSubjectPrincipalRegex) {
this.pc.getReaderContext()
.error("The attribute 'principal-extractor-ref' cannot be used together with the 'subject-principal-regex' attribute within <"
+ Elements.X509 + ">", this.pc.extractSource(x509Elt));
}
if (hasPrincipalExtractorRef) {
RuntimeBeanReference principalExtractor = new RuntimeBeanReference(principalExtractorRef);
filterBuilder.addPropertyValue("principalExtractor", principalExtractor);
}
if (hasSubjectPrincipalRegex) {
BeanDefinitionBuilder extractor = BeanDefinitionBuilder BeanDefinitionBuilder extractor = BeanDefinitionBuilder
.rootBeanDefinition(SubjectDnX509PrincipalExtractor.class); .rootBeanDefinition(SubjectDnX509PrincipalExtractor.class);
extractor.addPropertyValue("subjectDnRegex", regex); extractor.addPropertyValue("subjectDnRegex", subjectPrincipalRegex);
filterBuilder.addPropertyValue("principalExtractor", extractor.getBeanDefinition()); filterBuilder.addPropertyValue("principalExtractor", extractor.getBeanDefinition());
} }
injectAuthenticationDetailsSource(x509Elt, filterBuilder); injectAuthenticationDetailsSource(x509Elt, filterBuilder);

View File

@ -51,6 +51,7 @@ import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator; import org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator; import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.HandlerMappingIntrospectorRequestTransformer; import org.springframework.security.web.access.HandlerMappingIntrospectorRequestTransformer;
import org.springframework.security.web.access.PathPatternRequestTransformer;
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl; import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;
import org.springframework.security.web.access.channel.ChannelProcessingFilter; import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.web.access.channel.InsecureChannelProcessor; import org.springframework.security.web.access.channel.InsecureChannelProcessor;
@ -974,10 +975,17 @@ class HttpConfigurationBuilder {
@Override @Override
public AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer getObject() public AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer getObject()
throws Exception { throws Exception {
AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer requestTransformer = this.applicationContext
.getBeanProvider(
AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer.class)
.getIfUnique();
if (requestTransformer != null) {
return requestTransformer;
}
HandlerMappingIntrospector hmi = this.applicationContext.getBeanProvider(HandlerMappingIntrospector.class) HandlerMappingIntrospector hmi = this.applicationContext.getBeanProvider(HandlerMappingIntrospector.class)
.getIfAvailable(); .getIfAvailable();
return (hmi != null) ? new HandlerMappingIntrospectorRequestTransformer(hmi) return (hmi != null) ? new HandlerMappingIntrospectorRequestTransformer(hmi)
: AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer.IDENTITY; : new PathPatternRequestTransformer();
} }
@Override @Override

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,6 +23,7 @@ import jakarta.servlet.http.HttpServletRequest;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference; import org.springframework.beans.factory.config.BeanReference;
@ -40,12 +41,13 @@ import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
import org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenAuthenticationProvider; import org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenAuthenticationProvider;
import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector; import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint; import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver; import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler; import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter; import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -64,6 +66,8 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
static final String BEARER_TOKEN_RESOLVER_REF = "bearer-token-resolver-ref"; static final String BEARER_TOKEN_RESOLVER_REF = "bearer-token-resolver-ref";
static final String AUTHENTICATION_CONVERTER_REF = "authentication-converter-ref";
static final String ENTRY_POINT_REF = "entry-point-ref"; static final String ENTRY_POINT_REF = "entry-point-ref";
static final String BEARER_TOKEN_RESOLVER = "bearerTokenResolver"; static final String BEARER_TOKEN_RESOLVER = "bearerTokenResolver";
@ -124,11 +128,16 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
pc.getReaderContext().registerWithGeneratedName(opaqueTokenAuthenticationProvider))); pc.getReaderContext().registerWithGeneratedName(opaqueTokenAuthenticationProvider)));
} }
BeanMetadataElement bearerTokenResolver = getBearerTokenResolver(oauth2ResourceServer); BeanMetadataElement bearerTokenResolver = getBearerTokenResolver(oauth2ResourceServer);
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder BeanMetadataElement authenticationConverter = getAuthenticationConverter(oauth2ResourceServer);
.rootBeanDefinition(BearerTokenRequestMatcher.class); if (bearerTokenResolver != null && authenticationConverter != null) {
requestMatcherBuilder.addConstructorArgValue(bearerTokenResolver); throw new BeanDefinitionStoreException(
BeanDefinition requestMatcher = requestMatcherBuilder.getBeanDefinition(); "You cannot use bearer-token-ref and authentication-converter-ref in the same oauth2-resource-server element");
}
if (bearerTokenResolver == null && authenticationConverter == null) {
authenticationConverter = new RootBeanDefinition(BearerTokenAuthenticationConverter.class);
}
BeanMetadataElement authenticationEntryPoint = getEntryPoint(oauth2ResourceServer); BeanMetadataElement authenticationEntryPoint = getEntryPoint(oauth2ResourceServer);
BeanDefinition requestMatcher = buildRequestMatcher(bearerTokenResolver, authenticationConverter);
this.entryPoints.put(requestMatcher, authenticationEntryPoint); this.entryPoints.put(requestMatcher, authenticationEntryPoint);
this.deniedHandlers.put(requestMatcher, this.accessDeniedHandler); this.deniedHandlers.put(requestMatcher, this.accessDeniedHandler);
this.ignoreCsrfRequestMatchers.add(requestMatcher); this.ignoreCsrfRequestMatchers.add(requestMatcher);
@ -136,13 +145,35 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
.rootBeanDefinition(BearerTokenAuthenticationFilter.class); .rootBeanDefinition(BearerTokenAuthenticationFilter.class);
BeanMetadataElement authenticationManagerResolver = getAuthenticationManagerResolver(oauth2ResourceServer); BeanMetadataElement authenticationManagerResolver = getAuthenticationManagerResolver(oauth2ResourceServer);
filterBuilder.addConstructorArgValue(authenticationManagerResolver); filterBuilder.addConstructorArgValue(authenticationManagerResolver);
filterBuilder.addPropertyValue(BEARER_TOKEN_RESOLVER, bearerTokenResolver);
filterBuilder.addPropertyValue(AUTHENTICATION_ENTRY_POINT, authenticationEntryPoint); filterBuilder.addPropertyValue(AUTHENTICATION_ENTRY_POINT, authenticationEntryPoint);
filterBuilder.addPropertyValue("securityContextHolderStrategy", filterBuilder.addPropertyValue("securityContextHolderStrategy",
this.authenticationFilterSecurityContextHolderStrategy); this.authenticationFilterSecurityContextHolderStrategy);
if (authenticationConverter != null) {
filterBuilder.addConstructorArgValue(authenticationConverter);
}
if (bearerTokenResolver != null) {
filterBuilder.addPropertyValue(BEARER_TOKEN_RESOLVER, bearerTokenResolver);
}
return filterBuilder.getBeanDefinition(); return filterBuilder.getBeanDefinition();
} }
private BeanDefinition buildRequestMatcher(BeanMetadataElement bearerTokenResolver,
BeanMetadataElement authenticationConverter) {
if (bearerTokenResolver != null) {
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
.rootBeanDefinition(BearerTokenRequestMatcher.class);
requestMatcherBuilder.addConstructorArgValue(bearerTokenResolver);
return requestMatcherBuilder.getBeanDefinition();
}
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
.rootBeanDefinition(BearerTokenAuthenticationRequestMatcher.class);
if (authenticationConverter != null) {
requestMatcherBuilder.addConstructorArgValue(authenticationConverter);
}
return requestMatcherBuilder.getBeanDefinition();
}
void validateConfiguration(Element oauth2ResourceServer, Element jwt, Element opaqueToken, ParserContext pc) { void validateConfiguration(Element oauth2ResourceServer, Element jwt, Element opaqueToken, ParserContext pc) {
if (!oauth2ResourceServer.hasAttribute(AUTHENTICATION_MANAGER_RESOLVER_REF)) { if (!oauth2ResourceServer.hasAttribute(AUTHENTICATION_MANAGER_RESOLVER_REF)) {
if (jwt == null && opaqueToken == null) { if (jwt == null && opaqueToken == null) {
@ -178,11 +209,19 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
BeanMetadataElement getBearerTokenResolver(Element element) { BeanMetadataElement getBearerTokenResolver(Element element) {
String bearerTokenResolverRef = element.getAttribute(BEARER_TOKEN_RESOLVER_REF); String bearerTokenResolverRef = element.getAttribute(BEARER_TOKEN_RESOLVER_REF);
if (!StringUtils.hasLength(bearerTokenResolverRef)) { if (!StringUtils.hasLength(bearerTokenResolverRef)) {
return new RootBeanDefinition(DefaultBearerTokenResolver.class); return null;
} }
return new RuntimeBeanReference(bearerTokenResolverRef); return new RuntimeBeanReference(bearerTokenResolverRef);
} }
BeanMetadataElement getAuthenticationConverter(Element element) {
String authenticationConverterRef = element.getAttribute(AUTHENTICATION_CONVERTER_REF);
if (!StringUtils.hasLength(authenticationConverterRef)) {
return null;
}
return new RuntimeBeanReference(authenticationConverterRef);
}
BeanMetadataElement getEntryPoint(Element element) { BeanMetadataElement getEntryPoint(Element element) {
String entryPointRef = element.getAttribute(ENTRY_POINT_REF); String entryPointRef = element.getAttribute(ENTRY_POINT_REF);
if (!StringUtils.hasLength(entryPointRef)) { if (!StringUtils.hasLength(entryPointRef)) {
@ -300,7 +339,7 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
String clientId = element.getAttribute(CLIENT_ID); String clientId = element.getAttribute(CLIENT_ID);
String clientSecret = element.getAttribute(CLIENT_SECRET); String clientSecret = element.getAttribute(CLIENT_SECRET);
BeanDefinitionBuilder introspectorBuilder = BeanDefinitionBuilder BeanDefinitionBuilder introspectorBuilder = BeanDefinitionBuilder
.rootBeanDefinition(NimbusOpaqueTokenIntrospector.class); .rootBeanDefinition(SpringOpaqueTokenIntrospector.class);
introspectorBuilder.addConstructorArgValue(introspectionUri); introspectorBuilder.addConstructorArgValue(introspectionUri);
introspectorBuilder.addConstructorArgValue(clientId); introspectorBuilder.addConstructorArgValue(clientId);
introspectorBuilder.addConstructorArgValue(clientSecret); introspectorBuilder.addConstructorArgValue(clientSecret);
@ -366,4 +405,29 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
} }
static final class BearerTokenAuthenticationRequestMatcher implements RequestMatcher {
private final AuthenticationConverter authenticationConverter;
BearerTokenAuthenticationRequestMatcher() {
this.authenticationConverter = new BearerTokenAuthenticationConverter();
}
BearerTokenAuthenticationRequestMatcher(AuthenticationConverter authenticationConverter) {
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
this.authenticationConverter = authenticationConverter;
}
@Override
public boolean matches(HttpServletRequest request) {
try {
return this.authenticationConverter.convert(request) != null;
}
catch (OAuth2AuthenticationException ex) {
return false;
}
}
}
} }

View File

@ -32,6 +32,8 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
import org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertionAccessor;
import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver; import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter; import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter;
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseFilter; import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseFilter;
@ -239,7 +241,13 @@ final class Saml2LogoutBeanDefinitionParser implements BeanDefinitionParser {
if (authentication == null) { if (authentication == null) {
return false; return false;
} }
return authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal; if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal) {
return true;
}
if (authentication.getCredentials() instanceof Saml2ResponseAssertionAccessor) {
return true;
}
return authentication instanceof Saml2Authentication;
} }
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) { public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {

View File

@ -32,7 +32,6 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.security.config.BeanIds; import org.springframework.security.config.BeanIds;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource; import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.security.ldap.server.UnboundIdContainer; import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -47,7 +46,7 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
private static final String CONTEXT_SOURCE_CLASS = "org.springframework.security.ldap.DefaultSpringSecurityContextSource"; private static final String CONTEXT_SOURCE_CLASS = "org.springframework.security.ldap.DefaultSpringSecurityContextSource";
/** /**
* Defines the Url of the ldap server to use. If not specified, an embedded apache DS * Defines the Url of the ldap server to use. If not specified, an embedded UnboundID
* instance will be created * instance will be created
*/ */
private static final String ATT_URL = "url"; private static final String ATT_URL = "url";
@ -78,22 +77,15 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
private static final int DEFAULT_PORT = 33389; private static final int DEFAULT_PORT = 33389;
private static final String APACHEDS_CLASSNAME = "org.apache.directory.server.core.DefaultDirectoryService";
private static final String UNBOUNID_CLASSNAME = "com.unboundid.ldap.listener.InMemoryDirectoryServer"; private static final String UNBOUNID_CLASSNAME = "com.unboundid.ldap.listener.InMemoryDirectoryServer";
private static final String APACHEDS_CONTAINER_CLASSNAME = "org.springframework.security.ldap.server.ApacheDSContainer";
private static final String UNBOUNDID_CONTAINER_CLASSNAME = "org.springframework.security.ldap.server.UnboundIdContainer"; private static final String UNBOUNDID_CONTAINER_CLASSNAME = "org.springframework.security.ldap.server.UnboundIdContainer";
private static final boolean unboundIdPresent; private static final boolean unboundIdPresent;
private static final boolean apacheDsPresent;
static { static {
ClassLoader classLoader = LdapServerBeanDefinitionParser.class.getClassLoader(); ClassLoader classLoader = LdapServerBeanDefinitionParser.class.getClassLoader();
unboundIdPresent = ClassUtils.isPresent(UNBOUNID_CLASSNAME, classLoader); unboundIdPresent = ClassUtils.isPresent(UNBOUNID_CLASSNAME, classLoader);
apacheDsPresent = ClassUtils.isPresent(APACHEDS_CLASSNAME, classLoader);
} }
@Override @Override
@ -128,10 +120,9 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
/** /**
* Will be called if no url attribute is supplied. * Will be called if no url attribute is supplied.
* *
* Registers beans to create an embedded apache directory server. * Registers beans to create an embedded UnboundID Server.
* @return the BeanDefinition for the ContextSource for the embedded server. * @return the BeanDefinition for the ContextSource for the embedded server.
* *
* @see ApacheDSContainer
* @see UnboundIdContainer * @see UnboundIdContainer
*/ */
private RootBeanDefinition createEmbeddedServer(Element element, ParserContext parserContext) { private RootBeanDefinition createEmbeddedServer(Element element, ParserContext parserContext) {
@ -162,8 +153,7 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
} }
ldapContainer.getConstructorArgumentValues().addGenericArgumentValue(ldifs); ldapContainer.getConstructorArgumentValues().addGenericArgumentValue(ldifs);
ldapContainer.getPropertyValues().addPropertyValue("port", getPort(element)); ldapContainer.getPropertyValues().addPropertyValue("port", getPort(element));
if (parserContext.getRegistry().containsBeanDefinition(BeanIds.EMBEDDED_APACHE_DS) if (parserContext.getRegistry().containsBeanDefinition(BeanIds.EMBEDDED_UNBOUNDID)) {
|| parserContext.getRegistry().containsBeanDefinition(BeanIds.EMBEDDED_UNBOUNDID)) {
parserContext.getReaderContext() parserContext.getReaderContext()
.error("Only one embedded server bean is allowed per application context", element); .error("Only one embedded server bean is allowed per application context", element);
} }
@ -175,9 +165,6 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
} }
private RootBeanDefinition getRootBeanDefinition(String mode) { private RootBeanDefinition getRootBeanDefinition(String mode) {
if (isApacheDsEnabled(mode)) {
return new RootBeanDefinition(APACHEDS_CONTAINER_CLASSNAME, null, null);
}
if (isUnboundidEnabled(mode)) { if (isUnboundidEnabled(mode)) {
return new RootBeanDefinition(UNBOUNDID_CONTAINER_CLASSNAME, null, null); return new RootBeanDefinition(UNBOUNDID_CONTAINER_CLASSNAME, null, null);
} }
@ -185,19 +172,12 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
} }
private String resolveBeanId(String mode) { private String resolveBeanId(String mode) {
if (isApacheDsEnabled(mode)) {
return BeanIds.EMBEDDED_APACHE_DS;
}
if (isUnboundidEnabled(mode)) { if (isUnboundidEnabled(mode)) {
return BeanIds.EMBEDDED_UNBOUNDID; return BeanIds.EMBEDDED_UNBOUNDID;
} }
return null; return null;
} }
private boolean isApacheDsEnabled(String mode) {
return "apacheds".equals(mode) || apacheDsPresent;
}
private boolean isUnboundidEnabled(String mode) { private boolean isUnboundidEnabled(String mode) {
return "unboundid".equals(mode) || unboundIdPresent; return "unboundid".equals(mode) || unboundIdPresent;
} }
@ -233,10 +213,6 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
} }
private int getPort() { private int getPort() {
if (apacheDsPresent) {
ApacheDSContainer apacheDSContainer = this.applicationContext.getBean(ApacheDSContainer.class);
return apacheDSContainer.getLocalPort();
}
if (unboundIdPresent) { if (unboundIdPresent) {
UnboundIdContainer unboundIdContainer = this.applicationContext.getBean(UnboundIdContainer.class); UnboundIdContainer unboundIdContainer = this.applicationContext.getBean(UnboundIdContainer.class);
return unboundIdContainer.getPort(); return unboundIdContainer.getPort();

View File

@ -24,6 +24,7 @@ import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.vote.AffirmativeBased; import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.access.vote.AuthenticatedVoter; import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter; import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.config.BeanIds; import org.springframework.security.config.BeanIds;
/** /**
@ -32,7 +33,9 @@ import org.springframework.security.config.BeanIds;
* @author Luke Taylor * @author Luke Taylor
* @author Ben Alex * @author Ben Alex
* @author Rob Winch * @author Rob Winch
* @deprecated Please use {@link AuthorizationManager} instead
*/ */
@Deprecated
abstract class MethodConfigUtils { abstract class MethodConfigUtils {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2020 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -125,7 +125,7 @@ public final class ClientRegistrationsBeanDefinitionParser implements BeanDefini
getOptionalIfNotEmpty(parserContext, clientRegistrationElt.getAttribute(ATT_CLIENT_SECRET)) getOptionalIfNotEmpty(parserContext, clientRegistrationElt.getAttribute(ATT_CLIENT_SECRET))
.ifPresent(builder::clientSecret); .ifPresent(builder::clientSecret);
getOptionalIfNotEmpty(parserContext, clientRegistrationElt.getAttribute(ATT_CLIENT_AUTHENTICATION_METHOD)) getOptionalIfNotEmpty(parserContext, clientRegistrationElt.getAttribute(ATT_CLIENT_AUTHENTICATION_METHOD))
.map(ClientAuthenticationMethod::new) .map(ClientAuthenticationMethod::valueOf)
.ifPresent(builder::clientAuthenticationMethod); .ifPresent(builder::clientAuthenticationMethod);
getOptionalIfNotEmpty(parserContext, clientRegistrationElt.getAttribute(ATT_AUTHORIZATION_GRANT_TYPE)) getOptionalIfNotEmpty(parserContext, clientRegistrationElt.getAttribute(ATT_AUTHORIZATION_GRANT_TYPE))
.map(AuthorizationGrantType::new) .map(AuthorizationGrantType::new)

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