mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-28 23:02:15 +00:00
Compare commits
No commits in common. "main" and "6.4.0-RC1" have entirely different histories.
2
.github/dco.yml
vendored
2
.github/dco.yml
vendored
@ -1,2 +0,0 @@
|
|||||||
require:
|
|
||||||
members: false
|
|
41
.github/dependabot.template.yml
vendored
Normal file
41
.github/dependabot.template.yml
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
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: "sjohnr/*"
|
||||||
|
- dependency-name: "spring-io/*"
|
||||||
|
- dependency-name: "spring-security-release-tools/*"
|
57
.github/dependabot.yml
vendored
57
.github/dependabot.yml
vendored
@ -5,7 +5,7 @@ registries:
|
|||||||
url: https://repo.spring.io/milestone
|
url: https://repo.spring.io/milestone
|
||||||
updates:
|
updates:
|
||||||
- package-ecosystem: gradle
|
- package-ecosystem: gradle
|
||||||
target-branch: 6.5.x
|
target-branch: 5.8.x
|
||||||
directory: /
|
directory: /
|
||||||
schedule:
|
schedule:
|
||||||
interval: daily
|
interval: daily
|
||||||
@ -19,7 +19,6 @@ updates:
|
|||||||
- dependency-name: com.nimbusds:nimbus-jose-jwt
|
- dependency-name: com.nimbusds:nimbus-jose-jwt
|
||||||
- dependency-name: org.python:jython
|
- dependency-name: org.python:jython
|
||||||
- dependency-name: org.apache.directory.server:*
|
- dependency-name: org.apache.directory.server:*
|
||||||
- dependency-name: org.apache.directory.shared:*
|
|
||||||
- dependency-name: org.junit:junit-bom
|
- dependency-name: org.junit:junit-bom
|
||||||
update-types:
|
update-types:
|
||||||
- version-update:semver-major
|
- version-update:semver-major
|
||||||
@ -31,7 +30,7 @@ updates:
|
|||||||
- version-update:semver-major
|
- version-update:semver-major
|
||||||
- version-update:semver-minor
|
- version-update:semver-minor
|
||||||
- package-ecosystem: gradle
|
- package-ecosystem: gradle
|
||||||
target-branch: 6.4.x
|
target-branch: 6.2.x
|
||||||
directory: /
|
directory: /
|
||||||
schedule:
|
schedule:
|
||||||
interval: daily
|
interval: daily
|
||||||
@ -45,7 +44,6 @@ updates:
|
|||||||
- dependency-name: com.nimbusds:nimbus-jose-jwt
|
- dependency-name: com.nimbusds:nimbus-jose-jwt
|
||||||
- dependency-name: org.python:jython
|
- dependency-name: org.python:jython
|
||||||
- dependency-name: org.apache.directory.server:*
|
- dependency-name: org.apache.directory.server:*
|
||||||
- dependency-name: org.apache.directory.shared:*
|
|
||||||
- dependency-name: org.junit:junit-bom
|
- dependency-name: org.junit:junit-bom
|
||||||
update-types:
|
update-types:
|
||||||
- version-update:semver-major
|
- version-update:semver-major
|
||||||
@ -71,7 +69,6 @@ updates:
|
|||||||
- dependency-name: com.nimbusds:nimbus-jose-jwt
|
- dependency-name: com.nimbusds:nimbus-jose-jwt
|
||||||
- dependency-name: org.python:jython
|
- dependency-name: org.python:jython
|
||||||
- dependency-name: org.apache.directory.server:*
|
- dependency-name: org.apache.directory.server:*
|
||||||
- dependency-name: org.apache.directory.shared:*
|
|
||||||
- dependency-name: org.junit:junit-bom
|
- dependency-name: org.junit:junit-bom
|
||||||
update-types:
|
update-types:
|
||||||
- version-update:semver-major
|
- version-update:semver-major
|
||||||
@ -97,7 +94,6 @@ updates:
|
|||||||
- dependency-name: com.nimbusds:nimbus-jose-jwt
|
- dependency-name: com.nimbusds:nimbus-jose-jwt
|
||||||
- dependency-name: org.python:jython
|
- dependency-name: org.python:jython
|
||||||
- dependency-name: org.apache.directory.server:*
|
- dependency-name: org.apache.directory.server:*
|
||||||
- dependency-name: org.apache.directory.shared:*
|
|
||||||
- dependency-name: org.junit:junit-bom
|
- dependency-name: org.junit:junit-bom
|
||||||
update-types:
|
update-types:
|
||||||
- version-update:semver-major
|
- version-update:semver-major
|
||||||
@ -111,8 +107,27 @@ 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: 5.8.x
|
||||||
|
directory: /
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
labels:
|
||||||
|
- 'type: task'
|
||||||
|
- 'in: build'
|
||||||
|
ignore:
|
||||||
|
- dependency-name: sjohnr/*
|
||||||
|
- package-ecosystem: github-actions
|
||||||
|
target-branch: 6.2.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: /
|
||||||
@ -123,6 +138,16 @@ 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'
|
||||||
|
ignore:
|
||||||
|
- dependency-name: sjohnr/*
|
||||||
- package-ecosystem: github-actions
|
- package-ecosystem: github-actions
|
||||||
target-branch: docs-build
|
target-branch: docs-build
|
||||||
directory: /
|
directory: /
|
||||||
@ -131,6 +156,8 @@ updates:
|
|||||||
labels:
|
labels:
|
||||||
- 'type: task'
|
- 'type: task'
|
||||||
- 'in: build'
|
- 'in: build'
|
||||||
|
ignore:
|
||||||
|
- dependency-name: sjohnr/*
|
||||||
|
|
||||||
- package-ecosystem: npm
|
- package-ecosystem: npm
|
||||||
target-branch: docs-build
|
target-branch: docs-build
|
||||||
@ -157,3 +184,19 @@ updates:
|
|||||||
labels:
|
labels:
|
||||||
- 'type: task'
|
- 'type: task'
|
||||||
- 'in: build'
|
- 'in: build'
|
||||||
|
- package-ecosystem: npm
|
||||||
|
target-branch: 6.2.x
|
||||||
|
directory: /docs
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
labels:
|
||||||
|
- 'type: task'
|
||||||
|
- 'in: build'
|
||||||
|
- package-ecosystem: npm
|
||||||
|
target-branch: 5.8.x
|
||||||
|
directory: /docs
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
labels:
|
||||||
|
- 'type: task'
|
||||||
|
- 'in: build'
|
||||||
|
17
.github/workflows/codeql.yml
vendored
17
.github/workflows/codeql.yml
vendored
@ -1,17 +0,0 @@
|
|||||||
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
|
|
@ -9,7 +9,7 @@ on:
|
|||||||
workflow_dispatch: # Manual trigger
|
workflow_dispatch: # Manual trigger
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@ -39,25 +39,64 @@ jobs:
|
|||||||
toolchain: 17
|
toolchain: 17
|
||||||
with:
|
with:
|
||||||
java-version: ${{ matrix.java-version }}
|
java-version: ${{ matrix.java-version }}
|
||||||
test-args: --refresh-dependencies -PforceMavenRepositories=snapshot,https://oss.sonatype.org/content/repositories/snapshots -PisOverrideVersionCatalog -PtestToolchain=${{ matrix.toolchain }} -PspringFrameworkVersion=7.+ -PreactorVersion=2025.+ -PspringDataVersion=2025.+ --stacktrace
|
test-args: --refresh-dependencies -PforceMavenRepositories=snapshot -PisOverrideVersionCatalog -PtestToolchain=${{ matrix.toolchain }} -PspringFrameworkVersion=6.2.+ -PreactorVersion=2023.0.+ -PspringDataVersion=2024.0.+ --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
|
||||||
|
check-tangles:
|
||||||
|
name: Check for Package Tangles
|
||||||
|
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 for package tangles
|
||||||
|
env:
|
||||||
|
STRUCTURE101_LICENSEID: ${{ secrets.STRUCTURE101_LICENSEID }}
|
||||||
|
run: |
|
||||||
|
./gradlew check s101 -Ps101.licenseId="$STRUCTURE101_LICENSEID" --stacktrace
|
||||||
deploy-artifacts:
|
deploy-artifacts:
|
||||||
name: Deploy Artifacts
|
name: Deploy Artifacts
|
||||||
needs: [ build, test]
|
needs: [ build, test, check-samples, check-tangles ]
|
||||||
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 ]
|
needs: [ build, test, check-samples, check-tangles ]
|
||||||
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 ]
|
needs: [ build, test, check-samples, check-tangles ]
|
||||||
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 }}
|
||||||
@ -77,7 +116,7 @@ jobs:
|
|||||||
send-notification:
|
send-notification:
|
||||||
name: Send Notification
|
name: Send Notification
|
||||||
needs: [ perform-release ]
|
needs: [ perform-release ]
|
||||||
if: ${{ !success() }}
|
if: ${{ failure() || cancelled() }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Send Notification
|
- name: Send Notification
|
||||||
|
57
.github/workflows/dependabot-auto-merge-forward.yml
vendored
Normal file
57
.github/workflows/dependabot-auto-merge-forward.yml
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
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: 5.8.x,${{ 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'
|
@ -4,8 +4,7 @@ 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
|
||||||
|
45
.github/workflows/mark-duplicate-dependabot-prs.yml
vendored
Normal file
45
.github/workflows/mark-duplicate-dependabot-prs.yml
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
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
|
63
.github/workflows/merge-dependabot-pr.yml
vendored
Normal file
63
.github/workflows/merge-dependabot-pr.yml
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
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 }}
|
5
.github/workflows/pr-build-workflow.yml
vendored
5
.github/workflows/pr-build-workflow.yml
vendored
@ -2,6 +2,9 @@ name: PR Build
|
|||||||
|
|
||||||
on: pull_request
|
on: pull_request
|
||||||
|
|
||||||
|
env:
|
||||||
|
DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
@ -18,7 +21,7 @@ jobs:
|
|||||||
java-version: '17'
|
java-version: '17'
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
run: ./gradlew clean build -PskipCheckExpectedBranchVersion --continue --scan
|
run: ./gradlew clean build -PskipCheckExpectedBranchVersion --continue
|
||||||
generate-docs:
|
generate-docs:
|
||||||
name: Generate Docs
|
name: Generate Docs
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
2
.github/workflows/release-scheduler.yml
vendored
2
.github/workflows/release-scheduler.yml
vendored
@ -11,7 +11,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
# List of active maintenance branches.
|
# List of active maintenance branches.
|
||||||
branch: [ main, 6.5.x, 6.4.x, 6.3.x ]
|
branch: [ main, 6.3.x, 6.2.x, 5.8.x ]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
22
.github/workflows/trigger-dependabot-auto-merge-forward.yml
vendored
Normal file
22
.github/workflows/trigger-dependabot-auto-merge-forward.yml
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
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
|
@ -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@e28269199d1d27975cf7f65e16d6095c555b3cd0
|
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@852920ba3fb1f28b35a2f13201133bc00ef33677
|
||||||
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@e28269199d1d27975cf7f65e16d6095c555b3cd0
|
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@852920ba3fb1f28b35a2f13201133bc00ef33677
|
||||||
name: Update
|
name: Update
|
||||||
with:
|
with:
|
||||||
docs-branch: 'docs-build'
|
docs-branch: 'docs-build'
|
||||||
|
36
.github/workflows/update-dependabot.yml
vendored
Normal file
36
.github/workflows/update-dependabot.yml
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
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
|
@ -79,10 +79,7 @@ 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.
|
||||||
|
|
||||||
Additionally, since Streams are https://github.com/spring-projects/spring-security/issues/7154[much slower] than `for` loops, please use them judiciously.
|
To format the code as well as check the style, run `./gradlew format check`.
|
||||||
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
|
||||||
@ -92,30 +89,41 @@ We are excited for your pull request! :heart:
|
|||||||
Please do your best to follow these steps.
|
Please do your best to follow these steps.
|
||||||
Don't worry if you don't get them all correct the first time, we will help you.
|
Don't worry if you don't get them all correct the first time, we will help you.
|
||||||
|
|
||||||
1. [[sign-cla]] All commits must include a __Signed-off-by__ trailer at the end of each commit message to indicate that the contributor agrees to the Developer Certificate of Origin.
|
[[sign-cla]]
|
||||||
For additional details, please refer to the blog post https://spring.io/blog/2025/01/06/hello-dco-goodbye-cla-simplifying-contributions-to-spring[Hello DCO, Goodbye CLA: Simplifying Contributions to Spring].
|
1. If you have not previously done so, please sign the https://cla.spring.io/sign/spring[Contributor License Agreement].
|
||||||
2. [[create-an-issue-list]] Must you https://github.com/spring-projects/spring-security/issues/new/choose[create an issue] first? No, but it is recommended for features and larger bug fixes. It's easier discuss with the team first to determine the right fix or enhancement.
|
You will be reminded automatically when you submit the PR.
|
||||||
|
[[create-an-issue]]
|
||||||
|
1. Must you https://github.com/spring-projects/spring-security/issues/new/choose[create an issue] first? No, but it is recommended for features and larger bug fixes. It's easier discuss with the team first to determine the right fix or enhancement.
|
||||||
For typos and straightforward bug fixes, starting with a pull request is encouraged.
|
For typos and straightforward bug fixes, starting with a pull request is encouraged.
|
||||||
Please include a description for context and motivation.
|
Please include a description for context and motivation.
|
||||||
Note that the team may close your pull request if it's not a fit for the project.
|
Note that the team may close your pull request if it's not a fit for the project.
|
||||||
3. [[choose-a-branch]] Always check out the branch indicated in the milestone and submit pull requests against it (for example, for milestone `5.8.3` use the `5.8.x` branch).
|
[[choose-a-branch]]
|
||||||
|
1. Always check out the branch indicated in the milestone and submit pull requests against it (for example, for milestone `5.8.3` use the `5.8.x` branch).
|
||||||
If there is no milestone, choose `main`.
|
If there is no milestone, choose `main`.
|
||||||
Once merged, the fix will be forwarded-ported to applicable branches including `main`.
|
Once merged, the fix will be forwarded-ported to applicable branches including `main`.
|
||||||
4. [[create-a-local-branch]] Create a local branch
|
[[create-a-local-branch]]
|
||||||
|
1. Create a local branch
|
||||||
If this is for an issue, consider a branch name with the issue number, like `gh-22276`.
|
If this is for an issue, consider a branch name with the issue number, like `gh-22276`.
|
||||||
5. [[write-tests]] Add documentation and JUnit Tests for your changes.
|
[[write-tests]]
|
||||||
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.
|
1. Add documentation and JUnit Tests for your changes.
|
||||||
7. [[add-since]] If on `main`, add `@since` JavaDoc attributes to new public APIs that your PR adds
|
[[update-copyright]]
|
||||||
8. [[change-rnc]] If you are updating the XSD, please instead update the RNC file and then run `./gradlew :spring-security-config:rncToXsd`.
|
1. In all files you edited, if the copyright header is of the form 2002-20xx, update the final copyright year to the current year.
|
||||||
9. [[format-code]] For each commit, build the code using `./gradlew format && ./gradlew check`.
|
[[add-since]]
|
||||||
|
1. If on `main`, add `@since` JavaDoc attributes to new public APIs that your PR adds
|
||||||
|
[[change-rnc]]
|
||||||
|
1. If you are updating the XSD, please instead update the RNC file and then run `./gradlew :spring-security-config:rncToXsd`.
|
||||||
|
[[format-code]]
|
||||||
|
1. For each commit, build the code using `./gradlew format 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
|
[[commit-atomically]]
|
||||||
|
1. 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.
|
||||||
See https://git-scm.com/book/en/Git-Tools-Rewriting-History[Rewriting History section of Pro Git] for an overview of streamlining the commit history.
|
See https://git-scm.com/book/en/Git-Tools-Rewriting-History[Rewriting History section of Pro Git] for an overview of streamlining the commit history.
|
||||||
11. [[format-commit-messages]] Format commit messages using 55 characters for the subject line, 72 characters per line
|
[[format-commit-messages]]
|
||||||
|
1. Format commit messages using 55 characters for the subject line, 72 characters per line
|
||||||
for the description, followed by the issue fixed, for example, `Closes gh-22276`.
|
for the description, followed by the issue fixed, for example, `Closes gh-22276`.
|
||||||
See the https://git-scm.com/book/en/Distributed-Git-Contributing-to-a-Project#Commit-Guidelines[Commit Guidelines section of Pro Git] for best practices around commit messages, and use `git log` to see some examples.
|
See the https://git-scm.com/book/en/Distributed-Git-Contributing-to-a-Project#Commit-Guidelines[Commit Guidelines section of Pro Git] for best practices around commit messages, and use `git log` to see some examples.
|
||||||
Favor imperative tense over present tense (use "Fix" instead of "Fixes"); avoid past tense (use "Fix" instead of "Fixed").
|
Present tense is preferred.
|
||||||
+
|
+
|
||||||
[indent=0]
|
[indent=0]
|
||||||
----
|
----
|
||||||
|
@ -21,8 +21,6 @@ See https://docs.spring.io/spring-security/reference/getting-spring-security.htm
|
|||||||
Be sure to read the https://docs.spring.io/spring-security/reference/[Spring Security Reference].
|
Be sure to read the https://docs.spring.io/spring-security/reference/[Spring Security Reference].
|
||||||
Extensive JavaDoc for the Spring Security code is also available in the https://docs.spring.io/spring-security/site/docs/current/api/[Spring Security API Documentation].
|
Extensive JavaDoc for the Spring Security code is also available in the https://docs.spring.io/spring-security/site/docs/current/api/[Spring Security API Documentation].
|
||||||
|
|
||||||
You may also want to check out https://docs.spring.io/spring-security/reference/whats-new.html[what's new in the latest release].
|
|
||||||
|
|
||||||
== Quick Start
|
== Quick Start
|
||||||
See https://docs.spring.io/spring-security/reference/servlet/getting-started.html[Hello Spring Security] to get started with a "Hello, World" application.
|
See https://docs.spring.io/spring-security/reference/servlet/getting-started.html[Hello Spring Security] to get started with a "Hello, World" application.
|
||||||
|
|
||||||
|
@ -20,5 +20,4 @@ dependencies {
|
|||||||
testImplementation "org.springframework:spring-test"
|
testImplementation "org.springframework:spring-test"
|
||||||
|
|
||||||
testRuntimeOnly 'org.hsqldb:hsqldb'
|
testRuntimeOnly 'org.hsqldb:hsqldb'
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
|
||||||
}
|
}
|
||||||
|
@ -96,11 +96,7 @@ import org.springframework.util.StringUtils;
|
|||||||
* All comparisons and prefixes are case sensitive.
|
* All comparisons and prefixes are case sensitive.
|
||||||
*
|
*
|
||||||
* @author Ben Alex
|
* @author Ben Alex
|
||||||
* @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security
|
|
||||||
* annotations may also prove useful, for example
|
|
||||||
* {@code @PreAuthorize("hasPermission(#id, ObjectsReturnType.class, read)")}
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
|
||||||
public class AclEntryVoter extends AbstractAclVoter {
|
public class AclEntryVoter extends AbstractAclVoter {
|
||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(AclEntryVoter.class);
|
private static final Log logger = LogFactory.getLog(AclEntryVoter.class);
|
||||||
|
@ -20,7 +20,6 @@ import java.util.List;
|
|||||||
|
|
||||||
import org.springframework.security.access.AfterInvocationProvider;
|
import org.springframework.security.access.AfterInvocationProvider;
|
||||||
import org.springframework.security.access.ConfigAttribute;
|
import org.springframework.security.access.ConfigAttribute;
|
||||||
import org.springframework.security.acls.AclPermissionEvaluator;
|
|
||||||
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
|
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
|
||||||
import org.springframework.security.acls.domain.SidRetrievalStrategyImpl;
|
import org.springframework.security.acls.domain.SidRetrievalStrategyImpl;
|
||||||
import org.springframework.security.acls.model.Acl;
|
import org.springframework.security.acls.model.Acl;
|
||||||
@ -40,11 +39,7 @@ import org.springframework.util.ObjectUtils;
|
|||||||
* services.
|
* services.
|
||||||
*
|
*
|
||||||
* @author Ben Alex
|
* @author Ben Alex
|
||||||
* @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security
|
|
||||||
* annotations may also prove useful, for example
|
|
||||||
* {@code @PostAuthorize("hasPermission(filterObject, read)")}
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
|
||||||
public abstract class AbstractAclProvider implements AfterInvocationProvider {
|
public abstract class AbstractAclProvider implements AfterInvocationProvider {
|
||||||
|
|
||||||
protected final AclService aclService;
|
protected final AclService aclService;
|
||||||
|
@ -26,7 +26,6 @@ import org.springframework.core.log.LogMessage;
|
|||||||
import org.springframework.security.access.AccessDeniedException;
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
import org.springframework.security.access.AuthorizationServiceException;
|
import org.springframework.security.access.AuthorizationServiceException;
|
||||||
import org.springframework.security.access.ConfigAttribute;
|
import org.springframework.security.access.ConfigAttribute;
|
||||||
import org.springframework.security.acls.AclPermissionEvaluator;
|
|
||||||
import org.springframework.security.acls.model.AclService;
|
import org.springframework.security.acls.model.AclService;
|
||||||
import org.springframework.security.acls.model.Permission;
|
import org.springframework.security.acls.model.Permission;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
@ -63,11 +62,7 @@ import org.springframework.security.core.Authentication;
|
|||||||
*
|
*
|
||||||
* @author Ben Alex
|
* @author Ben Alex
|
||||||
* @author Paulo Neves
|
* @author Paulo Neves
|
||||||
* @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security
|
|
||||||
* annotations may also prove useful, for example
|
|
||||||
* {@code @PostFilter("hasPermission(filterObject, read)")}
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
|
||||||
public class AclEntryAfterInvocationCollectionFilteringProvider extends AbstractAclProvider {
|
public class AclEntryAfterInvocationCollectionFilteringProvider extends AbstractAclProvider {
|
||||||
|
|
||||||
protected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationCollectionFilteringProvider.class);
|
protected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationCollectionFilteringProvider.class);
|
||||||
|
@ -27,7 +27,6 @@ import org.springframework.context.MessageSourceAware;
|
|||||||
import org.springframework.context.support.MessageSourceAccessor;
|
import org.springframework.context.support.MessageSourceAccessor;
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
import org.springframework.security.access.ConfigAttribute;
|
import org.springframework.security.access.ConfigAttribute;
|
||||||
import org.springframework.security.acls.AclPermissionEvaluator;
|
|
||||||
import org.springframework.security.acls.model.AclService;
|
import org.springframework.security.acls.model.AclService;
|
||||||
import org.springframework.security.acls.model.Permission;
|
import org.springframework.security.acls.model.Permission;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
@ -60,12 +59,7 @@ import org.springframework.security.core.SpringSecurityMessageSource;
|
|||||||
* granted and <code>null</code> will be returned.
|
* granted and <code>null</code> will be returned.
|
||||||
* <p>
|
* <p>
|
||||||
* All comparisons and prefixes are case sensitive.
|
* All comparisons and prefixes are case sensitive.
|
||||||
*
|
|
||||||
* @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security
|
|
||||||
* annotations may also prove useful, for example
|
|
||||||
* {@code @PostAuthorize("hasPermission(filterObject, read)")}
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
|
||||||
public class AclEntryAfterInvocationProvider extends AbstractAclProvider implements MessageSourceAware {
|
public class AclEntryAfterInvocationProvider extends AbstractAclProvider implements MessageSourceAware {
|
||||||
|
|
||||||
protected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationProvider.class);
|
protected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationProvider.class);
|
||||||
|
@ -32,9 +32,7 @@ import org.springframework.core.log.LogMessage;
|
|||||||
*
|
*
|
||||||
* @author Ben Alex
|
* @author Ben Alex
|
||||||
* @author Paulo Neves
|
* @author Paulo Neves
|
||||||
* @deprecated please see {@code PostFilter}
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
|
||||||
class ArrayFilterer<T> implements Filterer<T> {
|
class ArrayFilterer<T> implements Filterer<T> {
|
||||||
|
|
||||||
protected static final Log logger = LogFactory.getLog(ArrayFilterer.class);
|
protected static final Log logger = LogFactory.getLog(ArrayFilterer.class);
|
||||||
|
@ -31,9 +31,7 @@ import org.springframework.core.log.LogMessage;
|
|||||||
*
|
*
|
||||||
* @author Ben Alex
|
* @author Ben Alex
|
||||||
* @author Paulo Neves
|
* @author Paulo Neves
|
||||||
* @deprecated please see {@code PostFilter}
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
|
||||||
class CollectionFilterer<T> implements Filterer<T> {
|
class CollectionFilterer<T> implements Filterer<T> {
|
||||||
|
|
||||||
protected static final Log logger = LogFactory.getLog(CollectionFilterer.class);
|
protected static final Log logger = LogFactory.getLog(CollectionFilterer.class);
|
||||||
|
@ -23,9 +23,7 @@ import java.util.Iterator;
|
|||||||
*
|
*
|
||||||
* @author Ben Alex
|
* @author Ben Alex
|
||||||
* @author Paulo Neves
|
* @author Paulo Neves
|
||||||
* @deprecated please use {@code PreFilter} and {@code @PostFilter} instead
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
|
||||||
interface Filterer<T> extends Iterable<T> {
|
interface Filterer<T> extends Iterable<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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,
|
List<ObjectIdentity> objects = this.jdbcOperations.query(this.findChildrenSql, args,
|
||||||
(rs, rowNum) -> mapObjectIdentityRow(rs), args);
|
(rs, rowNum) -> mapObjectIdentityRow(rs));
|
||||||
return (!objects.isEmpty()) ? objects : null;
|
return (!objects.isEmpty()) ? objects : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +190,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 createOrRetrieveClassPrimaryKey(String type, boolean allowCreate, Class idType) {
|
protected Long createOrRetrieveClassPrimaryKey(String type, boolean allowCreate, Class idType) {
|
||||||
List<Long> classIds = this.jdbcOperations.queryForList(this.selectClassPrimaryKey, Long.class, type);
|
List<Long> classIds = this.jdbcOperations.queryForList(this.selectClassPrimaryKey, new Object[] { type },
|
||||||
|
Long.class);
|
||||||
|
|
||||||
if (!classIds.isEmpty()) {
|
if (!classIds.isEmpty()) {
|
||||||
return classIds.get(0);
|
return classIds.get(0);
|
||||||
@ -241,8 +242,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, Long.class, sidIsPrincipal,
|
List<Long> sidIds = this.jdbcOperations.queryForList(this.selectSidPrimaryKey,
|
||||||
sidName);
|
new Object[] { sidIsPrincipal, sidName }, Long.class);
|
||||||
if (!sidIds.isEmpty()) {
|
if (!sidIds.isEmpty()) {
|
||||||
return sidIds.get(0);
|
return sidIds.get(0);
|
||||||
}
|
}
|
||||||
|
@ -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(), any(RowMapper.class), eq(args))).willReturn(result);
|
given(this.jdbcOperations.query(anyString(), eq(args), any(RowMapper.class))).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);
|
||||||
|
@ -27,8 +27,6 @@ dependencies {
|
|||||||
testImplementation "org.mockito:mockito-junit-jupiter"
|
testImplementation "org.mockito:mockito-junit-jupiter"
|
||||||
testImplementation "org.springframework:spring-test"
|
testImplementation "org.springframework:spring-test"
|
||||||
testAspect sourceSets.main.output
|
testAspect sourceSets.main.output
|
||||||
|
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compileAspectj.ajcOptions.outxmlfile = "META-INF/aop.xml"
|
compileAspectj.ajcOptions.outxmlfile = "META-INF/aop.xml"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2025 the original author or authors.
|
* Copyright 2002-2024 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('A')")
|
@PreAuthorize("hasRole('X')")
|
||||||
void publicCallsPrivate() {
|
void publicCallsPrivate() {
|
||||||
privateMethod();
|
privateMethod();
|
||||||
}
|
}
|
||||||
|
12
build.gradle
12
build.gradle
@ -20,6 +20,7 @@ plugins {
|
|||||||
|
|
||||||
apply plugin: 'io.spring.nohttp'
|
apply plugin: 'io.spring.nohttp'
|
||||||
apply plugin: 'locks'
|
apply plugin: 'locks'
|
||||||
|
apply plugin: 's101'
|
||||||
apply plugin: 'io.spring.convention.root'
|
apply plugin: 'io.spring.convention.root'
|
||||||
apply plugin: 'org.jetbrains.kotlin.jvm'
|
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||||
apply plugin: 'org.springframework.security.versions.verify-dependencies-versions'
|
apply plugin: 'org.springframework.security.versions.verify-dependencies-versions'
|
||||||
@ -109,10 +110,6 @@ nohttp {
|
|||||||
source.builtBy(project(':spring-security-config').tasks.withType(RncToXsd))
|
source.builtBy(project(':spring-security-config').tasks.withType(RncToXsd))
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named('checkstyleNohttp') {
|
|
||||||
maxHeapSize = '1g'
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.register('cloneRepository', IncludeRepoTask) {
|
tasks.register('cloneRepository', IncludeRepoTask) {
|
||||||
repository = project.getProperties().get("repositoryName")
|
repository = project.getProperties().get("repositoryName")
|
||||||
ref = project.getProperties().get("ref")
|
ref = project.getProperties().get("ref")
|
||||||
@ -120,11 +117,16 @@ tasks.register('cloneRepository', IncludeRepoTask) {
|
|||||||
outputDirectory = project.hasProperty("cloneOutputDirectory") ? project.file("$cloneOutputDirectory") : defaultDirectory
|
outputDirectory = project.hasProperty("cloneOutputDirectory") ? project.file("$cloneOutputDirectory") : defaultDirectory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s101 {
|
||||||
|
repository = 'https://structure101.com/binaries/latest'
|
||||||
|
configurationDirectory = project.file("etc/s101")
|
||||||
|
}
|
||||||
|
|
||||||
wrapperUpgrade {
|
wrapperUpgrade {
|
||||||
gradle {
|
gradle {
|
||||||
'spring-security' {
|
'spring-security' {
|
||||||
repo = 'spring-projects/spring-security'
|
repo = 'spring-projects/spring-security'
|
||||||
baseBranch = '6.3.x' // runs only on 6.3.x and the update is merged forward to main
|
baseBranch = '6.2.x' // runs only on 6.2.x and the update is merged forward to main
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id "java-gradle-plugin"
|
id "java-gradle-plugin"
|
||||||
id "groovy-gradle-plugin"
|
|
||||||
id "java"
|
id "java"
|
||||||
id "groovy"
|
id "groovy"
|
||||||
}
|
}
|
||||||
@ -77,7 +76,6 @@ 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'
|
||||||
}
|
}
|
||||||
@ -97,8 +95,6 @@ dependencies {
|
|||||||
testImplementation 'org.mockito:mockito-core'
|
testImplementation 'org.mockito:mockito-core'
|
||||||
testImplementation 'org.mockito:mockito-junit-jupiter'
|
testImplementation 'org.mockito:mockito-junit-jupiter'
|
||||||
testImplementation libs.com.squareup.okhttp3.mockwebserver
|
testImplementation libs.com.squareup.okhttp3.mockwebserver
|
||||||
|
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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())
|
||||||
);
|
);
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
@ -71,4 +71,4 @@ public class ManagementConfigurationPlugin implements Plugin<Project> {
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -80,11 +80,6 @@ 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,13 +32,10 @@ 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(schemaResourceName)
|
it.path.endsWith(schemas.get(key))
|
||||||
}
|
|
||||||
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
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
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'
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
@ -81,6 +81,9 @@ 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;
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
|
@ -50,7 +50,7 @@ public class S101Plugin implements Plugin<Project> {
|
|||||||
|
|
||||||
private void configure(JavaExec exec) {
|
private void configure(JavaExec exec) {
|
||||||
exec.setDescription("Runs Structure101 headless analysis, installing and configuring if necessary");
|
exec.setDescription("Runs Structure101 headless analysis, installing and configuring if necessary");
|
||||||
exec.dependsOn("assemble");
|
exec.dependsOn("check");
|
||||||
Project project = exec.getProject();
|
Project project = exec.getProject();
|
||||||
S101PluginExtension extension = project.getExtensions().getByType(S101PluginExtension.class);
|
S101PluginExtension extension = project.getExtensions().getByType(S101PluginExtension.class);
|
||||||
exec
|
exec
|
||||||
|
@ -6,7 +6,5 @@ dependencies {
|
|||||||
implementation 'org.springframework:spring-core'
|
implementation 'org.springframework:spring-core'
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-api"
|
testImplementation "org.junit.jupiter:junit-jupiter-api"
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-engine"
|
testImplementation "org.junit.jupiter:junit-jupiter-engine"
|
||||||
|
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,4 @@ dependencies {
|
|||||||
optional 'ch.qos.logback:logback-classic'
|
optional 'ch.qos.logback:logback-classic'
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-api"
|
testImplementation "org.junit.jupiter:junit-jupiter-api"
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-engine"
|
testImplementation "org.junit.jupiter:junit-jupiter-engine"
|
||||||
|
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,4 @@ dependencies {
|
|||||||
testImplementation "org.mockito:mockito-junit-jupiter"
|
testImplementation "org.mockito:mockito-junit-jupiter"
|
||||||
testImplementation "org.springframework:spring-test"
|
testImplementation "org.springframework:spring-test"
|
||||||
testImplementation 'org.skyscreamer:jsonassert'
|
testImplementation 'org.skyscreamer:jsonassert'
|
||||||
|
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
|
||||||
}
|
}
|
||||||
|
@ -115,8 +115,15 @@ public class CasAuthenticationToken extends AbstractAuthenticationToken implemen
|
|||||||
if (!super.equals(obj)) {
|
if (!super.equals(obj)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (obj instanceof CasAuthenticationToken test) {
|
if (obj instanceof CasAuthenticationToken) {
|
||||||
return this.assertion.equals(test.getAssertion()) && this.getKeyHash() == test.getKeyHash();
|
CasAuthenticationToken test = (CasAuthenticationToken) obj;
|
||||||
|
if (!this.assertion.equals(test.getAssertion())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.getKeyHash() != test.getKeyHash()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,6 @@ import org.springframework.security.jackson2.SecurityJackson2Modules;
|
|||||||
* @since 4.2
|
* @since 4.2
|
||||||
* @see org.springframework.security.jackson2.SecurityJackson2Modules
|
* @see org.springframework.security.jackson2.SecurityJackson2Modules
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
|
||||||
public class CasJackson2Module extends SimpleModule {
|
public class CasJackson2Module extends SimpleModule {
|
||||||
|
|
||||||
public CasJackson2Module() {
|
public CasJackson2Module() {
|
||||||
|
@ -18,7 +18,6 @@ package org.springframework.security.cas.userdetails;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import org.apereo.cas.client.validation.Assertion;
|
import org.apereo.cas.client.validation.Assertion;
|
||||||
|
|
||||||
@ -74,8 +73,7 @@ public final class GrantedAuthorityFromAssertionAttributesUserDetailsService
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SimpleGrantedAuthority createSimpleGrantedAuthority(Object o) {
|
private SimpleGrantedAuthority createSimpleGrantedAuthority(Object o) {
|
||||||
return new SimpleGrantedAuthority(
|
return new SimpleGrantedAuthority(this.convertToUpperCase ? o.toString().toUpperCase() : o.toString());
|
||||||
this.convertToUpperCase ? o.toString().toUpperCase(Locale.ROOT) : o.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,7 +51,6 @@ import org.springframework.security.web.context.SecurityContextRepository;
|
|||||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
|
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
|
||||||
import org.springframework.security.web.savedrequest.RequestCache;
|
import org.springframework.security.web.savedrequest.RequestCache;
|
||||||
import org.springframework.security.web.savedrequest.SavedRequest;
|
import org.springframework.security.web.savedrequest.SavedRequest;
|
||||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
|
||||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
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;
|
||||||
@ -216,8 +215,6 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
|||||||
|
|
||||||
public CasAuthenticationFilter() {
|
public CasAuthenticationFilter() {
|
||||||
super("/login/cas");
|
super("/login/cas");
|
||||||
RequestMatcher processUri = PathPatternRequestMatcher.withDefaults().matcher("/login/cas");
|
|
||||||
setRequiresAuthenticationRequestMatcher(processUri);
|
|
||||||
setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler());
|
setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler());
|
||||||
setSecurityContextRepository(this.securityContextRepository);
|
setSecurityContextRepository(this.securityContextRepository);
|
||||||
}
|
}
|
||||||
@ -322,18 +319,6 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
|||||||
super.setAuthenticationFailureHandler(new CasAuthenticationFailureHandler(failureHandler));
|
super.setAuthenticationFailureHandler(new CasAuthenticationFailureHandler(failureHandler));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Use this {@code RequestMatcher} to match proxy receptor requests. Without setting
|
|
||||||
* this matcher, {@link CasAuthenticationFilter} will not capture any proxy receptor
|
|
||||||
* requets.
|
|
||||||
* @param proxyReceptorMatcher the {@link RequestMatcher} to use
|
|
||||||
* @since 6.5
|
|
||||||
*/
|
|
||||||
public final void setProxyReceptorMatcher(RequestMatcher proxyReceptorMatcher) {
|
|
||||||
Assert.notNull(proxyReceptorMatcher, "proxyReceptorMatcher cannot be null");
|
|
||||||
this.proxyReceptorMatcher = proxyReceptorMatcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void setProxyReceptorUrl(final String proxyReceptorUrl) {
|
public final void setProxyReceptorUrl(final String proxyReceptorUrl) {
|
||||||
this.proxyReceptorMatcher = new AntPathRequestMatcher("/**" + proxyReceptorUrl);
|
this.proxyReceptorMatcher = new AntPathRequestMatcher("/**" + proxyReceptorUrl);
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,6 @@ import org.springframework.security.core.context.SecurityContextImpl;
|
|||||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||||
import org.springframework.security.web.context.SecurityContextRepository;
|
import org.springframework.security.web.context.SecurityContextRepository;
|
||||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
|
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
|
||||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
|
||||||
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;
|
||||||
@ -79,7 +78,7 @@ public class CasAuthenticationFilterTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNormalOperation() throws Exception {
|
public void testNormalOperation() throws Exception {
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/login/cas");
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
request.setServletPath("/login/cas");
|
request.setServletPath("/login/cas");
|
||||||
request.addParameter("ticket", "ST-0-ER94xMJmn6pha35CQRoZ");
|
request.addParameter("ticket", "ST-0-ER94xMJmn6pha35CQRoZ");
|
||||||
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
||||||
@ -104,7 +103,7 @@ public class CasAuthenticationFilterTests {
|
|||||||
String url = "/login/cas";
|
String url = "/login/cas";
|
||||||
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
||||||
filter.setFilterProcessesUrl(url);
|
filter.setFilterProcessesUrl(url);
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("POST", url);
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
request.setServletPath(url);
|
request.setServletPath(url);
|
||||||
assertThat(filter.requiresAuthentication(request, response)).isTrue();
|
assertThat(filter.requiresAuthentication(request, response)).isTrue();
|
||||||
@ -133,11 +132,10 @@ public class CasAuthenticationFilterTests {
|
|||||||
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
||||||
filter.setFilterProcessesUrl(url);
|
filter.setFilterProcessesUrl(url);
|
||||||
filter.setServiceProperties(properties);
|
filter.setServiceProperties(properties);
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("POST", url);
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
request.setServletPath(url);
|
request.setServletPath(url);
|
||||||
assertThat(filter.requiresAuthentication(request, response)).isTrue();
|
assertThat(filter.requiresAuthentication(request, response)).isTrue();
|
||||||
request = new MockHttpServletRequest("POST", "/other");
|
|
||||||
request.setServletPath("/other");
|
request.setServletPath("/other");
|
||||||
assertThat(filter.requiresAuthentication(request, response)).isFalse();
|
assertThat(filter.requiresAuthentication(request, response)).isFalse();
|
||||||
request.setParameter(properties.getArtifactParameter(), "value");
|
request.setParameter(properties.getArtifactParameter(), "value");
|
||||||
@ -172,7 +170,7 @@ public class CasAuthenticationFilterTests {
|
|||||||
given(manager.authenticate(any(Authentication.class))).willReturn(authentication);
|
given(manager.authenticate(any(Authentication.class))).willReturn(authentication);
|
||||||
ServiceProperties serviceProperties = new ServiceProperties();
|
ServiceProperties serviceProperties = new ServiceProperties();
|
||||||
serviceProperties.setAuthenticateAllArtifacts(true);
|
serviceProperties.setAuthenticateAllArtifacts(true);
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/authenticate");
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
request.setParameter("ticket", "ST-1-123");
|
request.setParameter("ticket", "ST-1-123");
|
||||||
request.setServletPath("/authenticate");
|
request.setServletPath("/authenticate");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
@ -268,20 +266,4 @@ public class CasAuthenticationFilterTests {
|
|||||||
verify(securityContextRepository).setContext(any(SecurityContext.class));
|
verify(securityContextRepository).setContext(any(SecurityContext.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void requiresAuthenticationWhenProxyRequestMatcherThenMatches() {
|
|
||||||
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/pgtCallback");
|
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
|
||||||
request.setServletPath("/pgtCallback");
|
|
||||||
assertThat(filter.requiresAuthentication(request, response)).isFalse();
|
|
||||||
filter.setProxyReceptorMatcher(PathPatternRequestMatcher.withDefaults().matcher(request.getServletPath()));
|
|
||||||
assertThat(filter.requiresAuthentication(request, response)).isFalse();
|
|
||||||
filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));
|
|
||||||
assertThat(filter.requiresAuthentication(request, response)).isTrue();
|
|
||||||
request.setRequestURI("/other");
|
|
||||||
request.setServletPath("/other");
|
|
||||||
assertThat(filter.requiresAuthentication(request, response)).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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: 'security-kotlin'
|
apply plugin: 'kotlin'
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
opensaml5 {
|
opensaml5 {
|
||||||
@ -78,6 +78,12 @@ 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"
|
||||||
@ -116,12 +122,8 @@ dependencies {
|
|||||||
exclude group: "org.slf4j", module: "jcl-over-slf4j"
|
exclude group: "org.slf4j", module: "jcl-over-slf4j"
|
||||||
}
|
}
|
||||||
testImplementation libs.org.instancio.instancio.junit
|
testImplementation libs.org.instancio.instancio.junit
|
||||||
testImplementation libs.org.eclipse.jetty.jetty.server
|
|
||||||
testImplementation libs.org.eclipse.jetty.jetty.servlet
|
|
||||||
|
|
||||||
testRuntimeOnly 'org.hsqldb:hsqldb'
|
testRuntimeOnly 'org.hsqldb:hsqldb'
|
||||||
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)
|
||||||
@ -153,20 +155,18 @@ tasks.named('sourcesJar', Jar).configure {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
configure(project.tasks.withType(Test)) {
|
tasks.withType(KotlinCompile).configureEach {
|
||||||
doFirst {
|
kotlinOptions {
|
||||||
systemProperties['springSecurityVersion'] = version
|
languageVersion = "1.7"
|
||||||
|
apiVersion = "1.7"
|
||||||
|
freeCompilerArgs = ["-Xjsr305=strict", "-Xsuppress-version-warnings"]
|
||||||
|
jvmTarget = "17"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
configure(project.tasks.withType(Test)) {
|
||||||
onOutput { descriptor, event ->
|
doFirst {
|
||||||
if (!project.hasProperty('serialization')) {
|
systemProperties['springSecurityVersion'] = version
|
||||||
return
|
|
||||||
}
|
|
||||||
if (descriptor.name=='listClassesMissingSerialVersion()') {
|
|
||||||
logger.lifecycle(event.message)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.UnboundIdContainer;
|
import org.springframework.security.ldap.server.ApacheDSContainer;
|
||||||
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
|
||||||
UnboundIdContainer ldapServer() throws Exception {
|
ApacheDSContainer ldapServer() throws Exception {
|
||||||
UnboundIdContainer unboundIdContainer = new UnboundIdContainer("dc=springframework,dc=org",
|
ApacheDSContainer apacheDSContainer = new ApacheDSContainer("dc=springframework,dc=org",
|
||||||
"classpath:/test-server.ldif");
|
"classpath:/test-server.ldif");
|
||||||
unboundIdContainer.setPort(getPort());
|
apacheDSContainer.setPort(getPort());
|
||||||
return unboundIdContainer;
|
return apacheDSContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,357 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2002-2024 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.configurers;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import org.assertj.core.api.AbstractAssert;
|
|
||||||
import org.assertj.core.api.AbstractStringAssert;
|
|
||||||
import org.eclipse.jetty.server.Server;
|
|
||||||
import org.eclipse.jetty.server.ServerConnector;
|
|
||||||
import org.eclipse.jetty.servlet.FilterHolder;
|
|
||||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
|
||||||
import org.junit.jupiter.api.AfterAll;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.openqa.selenium.By;
|
|
||||||
import org.openqa.selenium.WebDriverException;
|
|
||||||
import org.openqa.selenium.WebElement;
|
|
||||||
import org.openqa.selenium.chrome.ChromeDriverService;
|
|
||||||
import org.openqa.selenium.chrome.ChromeOptions;
|
|
||||||
import org.openqa.selenium.chromium.HasCdp;
|
|
||||||
import org.openqa.selenium.devtools.HasDevTools;
|
|
||||||
import org.openqa.selenium.remote.Augmenter;
|
|
||||||
import org.openqa.selenium.remote.RemoteWebDriver;
|
|
||||||
import org.openqa.selenium.support.ui.FluentWait;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.core.env.Environment;
|
|
||||||
import org.springframework.mock.env.MockPropertySource;
|
|
||||||
import org.springframework.security.config.Customizer;
|
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
|
||||||
import org.springframework.security.core.userdetails.User;
|
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
|
||||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
|
||||||
import org.springframework.security.web.FilterChainProxy;
|
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
|
||||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
|
||||||
import org.springframework.web.filter.DelegatingFilterProxy;
|
|
||||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Webdriver-based tests for the WebAuthnConfigurer. This uses a full browser because
|
|
||||||
* these features require Javascript and browser APIs to be available.
|
|
||||||
*
|
|
||||||
* @author Daniel Garnier-Moiroux
|
|
||||||
*/
|
|
||||||
@org.junit.jupiter.api.Disabled
|
|
||||||
class WebAuthnWebDriverTests {
|
|
||||||
|
|
||||||
private String baseUrl;
|
|
||||||
|
|
||||||
private static ChromeDriverService driverService;
|
|
||||||
|
|
||||||
private Server server;
|
|
||||||
|
|
||||||
private RemoteWebDriver driver;
|
|
||||||
|
|
||||||
private static final String USERNAME = "user";
|
|
||||||
|
|
||||||
private static final String PASSWORD = "password";
|
|
||||||
|
|
||||||
@BeforeAll
|
|
||||||
static void startChromeDriverService() throws Exception {
|
|
||||||
driverService = new ChromeDriverService.Builder().usingAnyFreePort().build();
|
|
||||||
driverService.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterAll
|
|
||||||
static void stopChromeDriverService() {
|
|
||||||
driverService.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void startServer() throws Exception {
|
|
||||||
// Create the server on port 8080
|
|
||||||
this.server = new Server(0);
|
|
||||||
|
|
||||||
// Set up the ServletContextHandler
|
|
||||||
ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
|
|
||||||
contextHandler.setContextPath("/");
|
|
||||||
this.server.setHandler(contextHandler);
|
|
||||||
this.server.start();
|
|
||||||
int serverPort = ((ServerConnector) this.server.getConnectors()[0]).getLocalPort();
|
|
||||||
this.baseUrl = "http://localhost:" + serverPort;
|
|
||||||
|
|
||||||
// Set up Spring application context
|
|
||||||
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
|
|
||||||
applicationContext.register(WebAuthnConfiguration.class);
|
|
||||||
applicationContext.setServletContext(contextHandler.getServletContext());
|
|
||||||
|
|
||||||
// Add the server port
|
|
||||||
MockPropertySource propertySource = new MockPropertySource().withProperty("server.port", serverPort);
|
|
||||||
applicationContext.getEnvironment().getPropertySources().addFirst(propertySource);
|
|
||||||
|
|
||||||
// Register the filter chain
|
|
||||||
DelegatingFilterProxy filterProxy = new DelegatingFilterProxy("securityFilterChain", applicationContext);
|
|
||||||
FilterHolder filterHolder = new FilterHolder(filterProxy);
|
|
||||||
contextHandler.addFilter(filterHolder, "/*", null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterEach
|
|
||||||
void stopServer() throws Exception {
|
|
||||||
this.server.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void setupDriver() {
|
|
||||||
ChromeOptions options = new ChromeOptions();
|
|
||||||
options.addArguments("--headless=new");
|
|
||||||
RemoteWebDriver baseDriver = new RemoteWebDriver(driverService.getUrl(), options);
|
|
||||||
// Enable dev tools
|
|
||||||
this.driver = (RemoteWebDriver) new Augmenter().augment(baseDriver);
|
|
||||||
this.driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterEach
|
|
||||||
void cleanupDriver() {
|
|
||||||
this.driver.quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void loginWhenNoValidAuthenticatorCredentialsThenRejects() {
|
|
||||||
createVirtualAuthenticator(true);
|
|
||||||
this.driver.get(this.baseUrl);
|
|
||||||
this.driver.findElement(signinWithPasskeyButton()).click();
|
|
||||||
await(() -> assertThat(this.driver.getCurrentUrl()).endsWith("/login?error"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void registerWhenNoLabelThenRejects() {
|
|
||||||
login();
|
|
||||||
|
|
||||||
this.driver.get(this.baseUrl + "/webauthn/register");
|
|
||||||
|
|
||||||
this.driver.findElement(registerPasskeyButton()).click();
|
|
||||||
assertHasAlertStartingWith("error", "Error: Passkey Label is required");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void registerWhenAuthenticatorNoUserVerificationThenRejects() {
|
|
||||||
createVirtualAuthenticator(false);
|
|
||||||
login();
|
|
||||||
this.driver.get(this.baseUrl + "/webauthn/register");
|
|
||||||
this.driver.findElement(passkeyLabel()).sendKeys("Virtual authenticator");
|
|
||||||
this.driver.findElement(registerPasskeyButton()).click();
|
|
||||||
|
|
||||||
await(() -> assertHasAlertStartingWith("error",
|
|
||||||
"Registration failed. Call to navigator.credentials.create failed:"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test in 4 steps to verify the end-to-end flow of registering an authenticator and
|
|
||||||
* using it to register.
|
|
||||||
* <ul>
|
|
||||||
* <li>Step 1: Log in with username / password</li>
|
|
||||||
* <li>Step 2: Register a credential from the virtual authenticator</li>
|
|
||||||
* <li>Step 3: Log out</li>
|
|
||||||
* <li>Step 4: Log in with the authenticator</li>
|
|
||||||
* </ul>
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
void loginWhenAuthenticatorRegisteredThenSuccess() {
|
|
||||||
// Setup
|
|
||||||
createVirtualAuthenticator(true);
|
|
||||||
|
|
||||||
// Step 1: log in with username / password
|
|
||||||
login();
|
|
||||||
|
|
||||||
// Step 2: register a credential from the virtual authenticator
|
|
||||||
this.driver.get(this.baseUrl + "/webauthn/register");
|
|
||||||
this.driver.findElement(passkeyLabel()).sendKeys("Virtual authenticator");
|
|
||||||
this.driver.findElement(registerPasskeyButton()).click();
|
|
||||||
|
|
||||||
// Ensure the page location has changed before performing further assertions.
|
|
||||||
// This is required because the location change is asynchronously performed in
|
|
||||||
// javascript, and performing assertions based on this.driver.findElement(...)
|
|
||||||
// may result in a StaleElementReferenceException.
|
|
||||||
await(() -> assertThat(this.driver.getCurrentUrl()).endsWith("/webauthn/register?success"));
|
|
||||||
await(() -> assertHasAlertStartingWith("success", "Success!"));
|
|
||||||
|
|
||||||
List<WebElement> passkeyRows = this.driver.findElements(passkeyTableRows());
|
|
||||||
assertThat(passkeyRows).hasSize(1)
|
|
||||||
.first()
|
|
||||||
.extracting((row) -> row.findElement(firstCell()))
|
|
||||||
.extracting(WebElement::getText)
|
|
||||||
.isEqualTo("Virtual authenticator");
|
|
||||||
|
|
||||||
// Step 3: log out
|
|
||||||
logout();
|
|
||||||
|
|
||||||
// Step 4: log in with the virtual authenticator
|
|
||||||
this.driver.get(this.baseUrl + "/webauthn/register");
|
|
||||||
this.driver.findElement(signinWithPasskeyButton()).click();
|
|
||||||
await(() -> assertThat(this.driver.getCurrentUrl()).endsWith("/webauthn/register?continue"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a virtual authenticator.
|
|
||||||
* <p>
|
|
||||||
* Note that Selenium docs for {@link HasCdp} strongly encourage to use
|
|
||||||
* {@link HasDevTools} instead. However, devtools require more dependencies and
|
|
||||||
* boilerplate, notably to sync the Devtools-CDP version with the current browser
|
|
||||||
* version, whereas CDP runs out of the box.
|
|
||||||
* <p>
|
|
||||||
* @param userIsVerified whether the authenticator simulates user verification.
|
|
||||||
* Setting it to false will make the ceremonies fail.
|
|
||||||
* @see <a href=
|
|
||||||
* "https://chromedevtools.github.io/devtools-protocol/tot/WebAuthn/">https://chromedevtools.github.io/devtools-protocol/tot/WebAuthn/</a>
|
|
||||||
*/
|
|
||||||
private void createVirtualAuthenticator(boolean userIsVerified) {
|
|
||||||
HasCdp cdpDriver = (HasCdp) this.driver;
|
|
||||||
cdpDriver.executeCdpCommand("WebAuthn.enable", Map.of("enableUI", false));
|
|
||||||
// this.driver.addVirtualAuthenticator(createVirtualAuthenticatorOptions());
|
|
||||||
//@formatter:off
|
|
||||||
cdpDriver.executeCdpCommand("WebAuthn.addVirtualAuthenticator",
|
|
||||||
Map.of(
|
|
||||||
"options",
|
|
||||||
Map.of(
|
|
||||||
"protocol", "ctap2",
|
|
||||||
"transport", "usb",
|
|
||||||
"hasUserVerification", true,
|
|
||||||
"hasResidentKey", true,
|
|
||||||
"isUserVerified", userIsVerified,
|
|
||||||
"automaticPresenceSimulation", true
|
|
||||||
)
|
|
||||||
));
|
|
||||||
//@formatter:on
|
|
||||||
}
|
|
||||||
|
|
||||||
private void login() {
|
|
||||||
this.driver.get(this.baseUrl);
|
|
||||||
this.driver.findElement(usernameField()).sendKeys(USERNAME);
|
|
||||||
this.driver.findElement(passwordField()).sendKeys(PASSWORD);
|
|
||||||
this.driver.findElement(signinWithUsernamePasswordButton()).click();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void logout() {
|
|
||||||
this.driver.get(this.baseUrl + "/logout");
|
|
||||||
this.driver.findElement(logoutButton()).click();
|
|
||||||
await(() -> assertThat(this.driver.getCurrentUrl()).endsWith("/login?logout"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private AbstractStringAssert<?> assertHasAlertStartingWith(String alertType, String alertMessage) {
|
|
||||||
WebElement alert = this.driver.findElement(new By.ById(alertType));
|
|
||||||
assertThat(alert.isDisplayed())
|
|
||||||
.withFailMessage(
|
|
||||||
() -> alertType + " alert was not displayed. Full page source:\n\n" + this.driver.getPageSource())
|
|
||||||
.isTrue();
|
|
||||||
|
|
||||||
return assertThat(alert.getText()).startsWith(alertMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Await until the assertion passes. If the assertion fails, it will display the
|
|
||||||
* assertion error in stdout. WebDriver-related exceptions are ignored, so that
|
|
||||||
* {@code assertion}s can interact with the page and be retried on error, e.g.
|
|
||||||
* {@code assertThat(this.driver.findElement(By.Id("some-id")).isNotNull()}.
|
|
||||||
*/
|
|
||||||
private void await(Supplier<AbstractAssert<?, ?>> assertion) {
|
|
||||||
new FluentWait<>(this.driver).withTimeout(Duration.ofSeconds(2))
|
|
||||||
.pollingEvery(Duration.ofMillis(100))
|
|
||||||
.ignoring(AssertionError.class, WebDriverException.class)
|
|
||||||
.until((d) -> {
|
|
||||||
assertion.get();
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static By.ById passkeyLabel() {
|
|
||||||
return new By.ById("label");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static By.ById registerPasskeyButton() {
|
|
||||||
return new By.ById("register");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static By.ByCssSelector passkeyTableRows() {
|
|
||||||
return new By.ByCssSelector("table > tbody > tr");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static By.ByCssSelector firstCell() {
|
|
||||||
return new By.ByCssSelector("td:first-child");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static By.ById passwordField() {
|
|
||||||
return new By.ById(PASSWORD);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static By.ById usernameField() {
|
|
||||||
return new By.ById("username");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static By.ByCssSelector signinWithUsernamePasswordButton() {
|
|
||||||
return new By.ByCssSelector("form > button[type=\"submit\"]");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static By.ById signinWithPasskeyButton() {
|
|
||||||
return new By.ById("passkey-signin");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static By.ByCssSelector logoutButton() {
|
|
||||||
return new By.ByCssSelector("button");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The configuration for WebAuthN tests. It accesses the Server's current port, so we
|
|
||||||
* can configurer WebAuthnConfigurer#allowedOrigin
|
|
||||||
*/
|
|
||||||
@Configuration
|
|
||||||
@EnableWebMvc
|
|
||||||
@EnableWebSecurity
|
|
||||||
static class WebAuthnConfiguration {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
UserDetailsService userDetailsService() {
|
|
||||||
return new InMemoryUserDetailsManager(
|
|
||||||
User.withDefaultPasswordEncoder().username(USERNAME).password(PASSWORD).build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
FilterChainProxy securityFilterChain(HttpSecurity http, Environment environment) throws Exception {
|
|
||||||
SecurityFilterChain securityFilterChain = http
|
|
||||||
.authorizeHttpRequests((auth) -> auth.anyRequest().authenticated())
|
|
||||||
.formLogin(Customizer.withDefaults())
|
|
||||||
.webAuthn((passkeys) -> passkeys.rpId("localhost")
|
|
||||||
.rpName("Spring Security WebAuthN tests")
|
|
||||||
.allowedOrigins("http://localhost:" + environment.getProperty("server.port")))
|
|
||||||
.build();
|
|
||||||
return new FilterChainProxy(securityFilterChain);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -74,7 +74,8 @@ 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) -> registry.forSocketAcceptor(this.interceptor)
|
.interceptors((registry) ->
|
||||||
|
registry.forSocketAcceptor(this.interceptor)
|
||||||
)
|
)
|
||||||
.acceptor(this.handler.responder())
|
.acceptor(this.handler.responder())
|
||||||
.bind(TcpServerTransport.create("localhost", 0))
|
.bind(TcpServerTransport.create("localhost", 0))
|
||||||
|
@ -1,199 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2019 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.rsocket;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import io.micrometer.observation.Observation;
|
|
||||||
import io.micrometer.observation.ObservationHandler;
|
|
||||||
import io.micrometer.observation.ObservationRegistry;
|
|
||||||
import io.rsocket.core.RSocketServer;
|
|
||||||
import io.rsocket.frame.decoder.PayloadDecoder;
|
|
||||||
import io.rsocket.metadata.WellKnownMimeType;
|
|
||||||
import io.rsocket.transport.netty.server.CloseableChannel;
|
|
||||||
import io.rsocket.transport.netty.server.TcpServerTransport;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
|
||||||
import org.mockito.ArgumentCaptor;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
|
||||||
import org.springframework.messaging.rsocket.RSocketRequester;
|
|
||||||
import org.springframework.messaging.rsocket.RSocketStrategies;
|
|
||||||
import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
|
|
||||||
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
|
|
||||||
import org.springframework.security.core.userdetails.User;
|
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
|
||||||
import org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor;
|
|
||||||
import org.springframework.security.rsocket.metadata.SimpleAuthenticationEncoder;
|
|
||||||
import org.springframework.security.rsocket.metadata.UsernamePasswordMetadata;
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
|
||||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
|
||||||
import org.springframework.util.MimeTypeUtils;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.BDDMockito.given;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.times;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Rob Winch
|
|
||||||
*/
|
|
||||||
@ContextConfiguration
|
|
||||||
@ExtendWith(SpringExtension.class)
|
|
||||||
public class HelloRSocketObservationITests {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
RSocketMessageHandler handler;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
SecuritySocketAcceptorInterceptor interceptor;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
ServerController controller;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
ObservationHandler<Observation.Context> observationHandler;
|
|
||||||
|
|
||||||
private CloseableChannel server;
|
|
||||||
|
|
||||||
private RSocketRequester requester;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void setup() {
|
|
||||||
// @formatter:off
|
|
||||||
this.server = RSocketServer.create()
|
|
||||||
.payloadDecoder(PayloadDecoder.ZERO_COPY)
|
|
||||||
.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
|
|
||||||
)
|
|
||||||
.acceptor(this.handler.responder())
|
|
||||||
.bind(TcpServerTransport.create("localhost", 0))
|
|
||||||
.block();
|
|
||||||
// @formatter:on
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterEach
|
|
||||||
public void dispose() {
|
|
||||||
this.requester.rsocket().dispose();
|
|
||||||
this.server.dispose();
|
|
||||||
this.controller.payloads.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getWhenUsingObservationRegistryThenObservesRequest() {
|
|
||||||
UsernamePasswordMetadata credentials = new UsernamePasswordMetadata("rob", "password");
|
|
||||||
// @formatter:off
|
|
||||||
this.requester = RSocketRequester.builder()
|
|
||||||
.setupMetadata(credentials, MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString()))
|
|
||||||
.rsocketStrategies(this.handler.getRSocketStrategies())
|
|
||||||
.connectTcp("localhost", this.server.address().getPort())
|
|
||||||
.block();
|
|
||||||
// @formatter:on
|
|
||||||
String data = "rob";
|
|
||||||
// @formatter:off
|
|
||||||
this.requester.route("secure.retrieve-mono")
|
|
||||||
.metadata(credentials, MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString()))
|
|
||||||
.data(data)
|
|
||||||
.retrieveMono(String.class)
|
|
||||||
.block();
|
|
||||||
// @formatter:on
|
|
||||||
ArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);
|
|
||||||
verify(this.observationHandler, times(2)).onStart(captor.capture());
|
|
||||||
Iterator<Observation.Context> contexts = captor.getAllValues().iterator();
|
|
||||||
// once for setup
|
|
||||||
assertThat(contexts.next().getName()).isEqualTo("spring.security.authentications");
|
|
||||||
// once for request
|
|
||||||
assertThat(contexts.next().getName()).isEqualTo("spring.security.authentications");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@EnableRSocketSecurity
|
|
||||||
static class Config {
|
|
||||||
|
|
||||||
private ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
ServerController controller() {
|
|
||||||
return new ServerController();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
RSocketMessageHandler messageHandler() {
|
|
||||||
RSocketMessageHandler handler = new RSocketMessageHandler();
|
|
||||||
handler.setRSocketStrategies(rsocketStrategies());
|
|
||||||
return handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
RSocketStrategies rsocketStrategies() {
|
|
||||||
return RSocketStrategies.builder().encoder(new SimpleAuthenticationEncoder()).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
MapReactiveUserDetailsService uds() {
|
|
||||||
// @formatter:off
|
|
||||||
UserDetails rob = User.withDefaultPasswordEncoder()
|
|
||||||
.username("rob")
|
|
||||||
.password("password")
|
|
||||||
.roles("USER", "ADMIN")
|
|
||||||
.build();
|
|
||||||
// @formatter:on
|
|
||||||
return new MapReactiveUserDetailsService(rob);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
ObservationHandler<Observation.Context> observationHandler() {
|
|
||||||
return this.handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
ObservationRegistry observationRegistry() {
|
|
||||||
given(this.handler.supportsContext(any())).willReturn(true);
|
|
||||||
ObservationRegistry registry = ObservationRegistry.create();
|
|
||||||
registry.observationConfig().observationHandler(this.handler);
|
|
||||||
return registry;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Controller
|
|
||||||
static class ServerController {
|
|
||||||
|
|
||||||
private List<String> payloads = new ArrayList<>();
|
|
||||||
|
|
||||||
@MessageMapping("**")
|
|
||||||
String retrieveMono(String payload) {
|
|
||||||
add(payload);
|
|
||||||
return "Hi " + payload;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void add(String p) {
|
|
||||||
this.payloads.add(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,168 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2019-2024 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.rsocket;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import io.rsocket.core.RSocketServer;
|
|
||||||
import io.rsocket.exceptions.RejectedSetupException;
|
|
||||||
import io.rsocket.frame.decoder.PayloadDecoder;
|
|
||||||
import io.rsocket.transport.netty.server.CloseableChannel;
|
|
||||||
import io.rsocket.transport.netty.server.TcpServerTransport;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
|
||||||
import org.springframework.messaging.rsocket.RSocketRequester;
|
|
||||||
import org.springframework.messaging.rsocket.RSocketStrategies;
|
|
||||||
import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
|
|
||||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
|
||||||
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
|
|
||||||
import org.springframework.security.core.userdetails.User;
|
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
|
||||||
import org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor;
|
|
||||||
import org.springframework.security.rsocket.metadata.BasicAuthenticationEncoder;
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
|
||||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Rob Winch
|
|
||||||
*/
|
|
||||||
@ContextConfiguration
|
|
||||||
@ExtendWith(SpringExtension.class)
|
|
||||||
public class HelloRSocketWithWebFluxITests {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
RSocketMessageHandler handler;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
SecuritySocketAcceptorInterceptor interceptor;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
ServerController controller;
|
|
||||||
|
|
||||||
private CloseableChannel server;
|
|
||||||
|
|
||||||
private RSocketRequester requester;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void setup() {
|
|
||||||
// @formatter:off
|
|
||||||
this.server = RSocketServer.create()
|
|
||||||
.payloadDecoder(PayloadDecoder.ZERO_COPY)
|
|
||||||
.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
|
|
||||||
)
|
|
||||||
.acceptor(this.handler.responder())
|
|
||||||
.bind(TcpServerTransport.create("localhost", 0))
|
|
||||||
.block();
|
|
||||||
// @formatter:on
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterEach
|
|
||||||
public void dispose() {
|
|
||||||
this.requester.rsocket().dispose();
|
|
||||||
this.server.dispose();
|
|
||||||
this.controller.payloads.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// gh-16161
|
|
||||||
@Test
|
|
||||||
public void retrieveMonoWhenSecureThenDenied() {
|
|
||||||
// @formatter:off
|
|
||||||
this.requester = RSocketRequester.builder()
|
|
||||||
.rsocketStrategies(this.handler.getRSocketStrategies())
|
|
||||||
.connectTcp("localhost", this.server.address().getPort())
|
|
||||||
.block();
|
|
||||||
// @formatter:on
|
|
||||||
String data = "rob";
|
|
||||||
// @formatter:off
|
|
||||||
assertThatExceptionOfType(Exception.class).isThrownBy(
|
|
||||||
() -> this.requester.route("secure.retrieve-mono")
|
|
||||||
.data(data)
|
|
||||||
.retrieveMono(String.class)
|
|
||||||
.block()
|
|
||||||
)
|
|
||||||
.matches((ex) -> ex instanceof RejectedSetupException
|
|
||||||
|| ex.getClass().toString().contains("ReactiveException"));
|
|
||||||
// @formatter:on
|
|
||||||
assertThat(this.controller.payloads).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@EnableRSocketSecurity
|
|
||||||
@EnableWebFluxSecurity
|
|
||||||
static class Config {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
ServerController controller() {
|
|
||||||
return new ServerController();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
RSocketMessageHandler messageHandler() {
|
|
||||||
RSocketMessageHandler handler = new RSocketMessageHandler();
|
|
||||||
handler.setRSocketStrategies(rsocketStrategies());
|
|
||||||
return handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
RSocketStrategies rsocketStrategies() {
|
|
||||||
return RSocketStrategies.builder().encoder(new BasicAuthenticationEncoder()).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
MapReactiveUserDetailsService uds() {
|
|
||||||
// @formatter:off
|
|
||||||
UserDetails rob = User.withDefaultPasswordEncoder()
|
|
||||||
.username("rob")
|
|
||||||
.password("password")
|
|
||||||
.roles("USER", "ADMIN")
|
|
||||||
.build();
|
|
||||||
// @formatter:on
|
|
||||||
return new MapReactiveUserDetailsService(rob);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Controller
|
|
||||||
static class ServerController {
|
|
||||||
|
|
||||||
private List<String> payloads = new ArrayList<>();
|
|
||||||
|
|
||||||
@MessageMapping("**")
|
|
||||||
String retrieveMono(String payload) {
|
|
||||||
add(payload);
|
|
||||||
return "Hi " + payload;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void add(String p) {
|
|
||||||
this.payloads.add(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -86,7 +86,8 @@ 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) -> registry.forSocketAcceptor(this.interceptor)
|
.interceptors((registry) ->
|
||||||
|
registry.forSocketAcceptor(this.interceptor)
|
||||||
)
|
)
|
||||||
.acceptor(this.handler.responder())
|
.acceptor(this.handler.responder())
|
||||||
.bind(TcpServerTransport.create("localhost", 0))
|
.bind(TcpServerTransport.create("localhost", 0))
|
||||||
|
@ -81,7 +81,8 @@ 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) -> registry.forSocketAcceptor(this.interceptor)
|
.interceptors((registry) ->
|
||||||
|
registry.forSocketAcceptor(this.interceptor)
|
||||||
)
|
)
|
||||||
.acceptor(this.handler.responder())
|
.acceptor(this.handler.responder())
|
||||||
.bind(TcpServerTransport.create("localhost", 0))
|
.bind(TcpServerTransport.create("localhost", 0))
|
||||||
|
@ -79,7 +79,8 @@ 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) -> registry.forSocketAcceptor(this.interceptor)
|
.interceptors((registry) ->
|
||||||
|
registry.forSocketAcceptor(this.interceptor)
|
||||||
)
|
)
|
||||||
.acceptor(this.handler.responder())
|
.acceptor(this.handler.responder())
|
||||||
.bind(TcpServerTransport.create("localhost", 0))
|
.bind(TcpServerTransport.create("localhost", 0))
|
||||||
|
@ -79,7 +79,8 @@ 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) -> registry.forSocketAcceptor(this.interceptor)
|
.interceptors((registry) ->
|
||||||
|
registry.forSocketAcceptor(this.interceptor)
|
||||||
)
|
)
|
||||||
.acceptor(this.handler.responder())
|
.acceptor(this.handler.responder())
|
||||||
.bind(TcpServerTransport.create("localhost", 0))
|
.bind(TcpServerTransport.create("localhost", 0))
|
||||||
|
@ -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.UnboundIdContainer;
|
import org.springframework.security.ldap.server.ApacheDSContainer;
|
||||||
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 UnboundIdContainer container;
|
private ApacheDSContainer container;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
UnboundIdContainer ldapServer() {
|
ApacheDSContainer ldapServer() throws Exception {
|
||||||
this.container = new UnboundIdContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
|
this.container = new ApacheDSContainer("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(UnboundIdContainer container) {
|
BaseLdapPathContextSource contextSource(ApacheDSContainer container) {
|
||||||
int port = container.getPort();
|
int port = container.getLocalPort();
|
||||||
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org");
|
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.UnboundIdContainer;
|
import org.springframework.security.ldap.server.ApacheDSContainer;
|
||||||
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 UnboundIdContainer container;
|
private ApacheDSContainer container;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
UnboundIdContainer ldapServer() {
|
ApacheDSContainer ldapServer() throws Exception {
|
||||||
this.container = new UnboundIdContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
|
this.container = new ApacheDSContainer("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(UnboundIdContainer container) {
|
BaseLdapPathContextSource contextSource(ApacheDSContainer container) {
|
||||||
int port = container.getPort();
|
int port = container.getLocalPort();
|
||||||
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org");
|
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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("otherben", "otherbenspassword"));
|
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("ben", "benspassword"));
|
||||||
UserDetails ben = (UserDetails) auth.getPrincipal();
|
UserDetails ben = (UserDetails) auth.getPrincipal();
|
||||||
assertThat(ben.getAuthorities()).hasSize(3);
|
assertThat(ben.getAuthorities()).hasSize(3);
|
||||||
}
|
}
|
||||||
@ -127,27 +127,6 @@ 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(
|
||||||
|
@ -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.UnboundIdContainer;
|
import org.springframework.security.ldap.server.ApacheDSContainer;
|
||||||
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/>");
|
||||||
UnboundIdContainer dsContainer = this.appCtx.getBean(UnboundIdContainer.class);
|
ApacheDSContainer dsContainer = this.appCtx.getBean(ApacheDSContainer.class);
|
||||||
|
|
||||||
assertThat(ReflectionTestUtils.getField(dsContainer, "ldif")).isEqualTo("classpath*:*.ldif");
|
assertThat(ReflectionTestUtils.getField(dsContainer, "ldifResources")).isEqualTo("classpath*:*.ldif");
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getDefaultPort() throws IOException {
|
private int getDefaultPort() throws IOException {
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
<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"/>
|
||||||
|
@ -54,6 +54,8 @@ 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";
|
||||||
|
@ -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 7.0. Please update your schema declarations to the 7.0 schema.",
|
+ "with Spring Security 6.4. Please update your schema declarations to the 6.4 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-7\\.0.*.xsd.*")
|
return schemaLocation.matches("(?m).*spring-security-6\\.4.*.xsd.*")
|
||||||
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
|
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
|
||||||
|| !schemaLocation.matches("(?m).*spring-security.*");
|
|| !schemaLocation.matches("(?m).*spring-security.*");
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2024 the original author or authors.
|
* Copyright 2002-2023 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.
|
||||||
@ -79,15 +79,6 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
|
|||||||
this(objectPostProcessor, false);
|
this(objectPostProcessor, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
@Deprecated(since = "6.4", forRemoval = true)
|
|
||||||
protected AbstractConfiguredSecurityBuilder(
|
|
||||||
org.springframework.security.config.annotation.ObjectPostProcessor<Object> objectPostProcessor) {
|
|
||||||
this(objectPostProcessor, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Creates a new instance with the provided {@link ObjectPostProcessor}. This post
|
* Creates a new instance with the provided {@link ObjectPostProcessor}. This post
|
||||||
* processor must support Object since there are many types of objects that may be
|
* processor must support Object since there are many types of objects that may be
|
||||||
@ -103,18 +94,6 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
|
|||||||
this.allowConfigurersOfSameType = allowConfigurersOfSameType;
|
this.allowConfigurersOfSameType = allowConfigurersOfSameType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
@Deprecated(since = "6.4", forRemoval = true)
|
|
||||||
protected AbstractConfiguredSecurityBuilder(
|
|
||||||
org.springframework.security.config.annotation.ObjectPostProcessor<Object> objectPostProcessor,
|
|
||||||
boolean allowConfigurersOfSameType) {
|
|
||||||
Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null");
|
|
||||||
this.objectPostProcessor = objectPostProcessor;
|
|
||||||
this.allowConfigurersOfSameType = allowConfigurersOfSameType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Similar to {@link #build()} and {@link #getObject()} but checks the state to
|
* Similar to {@link #build()} and {@link #getObject()} but checks the state to
|
||||||
* determine if {@link #build()} needs to be called first.
|
* determine if {@link #build()} needs to be called first.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2024 the original author or authors.
|
* Copyright 2002-2023 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.
|
||||||
@ -50,6 +50,17 @@ 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}
|
||||||
@ -81,15 +92,6 @@ public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>>
|
|||||||
this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);
|
this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
@Deprecated(since = "6.4", forRemoval = true)
|
|
||||||
public void addObjectPostProcessor(
|
|
||||||
org.springframework.security.config.annotation.ObjectPostProcessor<?> objectPostProcessor) {
|
|
||||||
this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@link SecurityBuilder} to be used. This is automatically set when using
|
* Sets the {@link SecurityBuilder} to be used. This is automatically set when using
|
||||||
* {@link AbstractConfiguredSecurityBuilder#apply(SecurityConfigurerAdapter)}
|
* {@link AbstractConfiguredSecurityBuilder#apply(SecurityConfigurerAdapter)}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2024 the original author or authors.
|
* Copyright 2002-2023 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.
|
||||||
@ -73,15 +73,6 @@ public class AuthenticationManagerBuilder
|
|||||||
super(objectPostProcessor, true);
|
super(objectPostProcessor, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
@Deprecated(since = "6.4", forRemoval = true)
|
|
||||||
public AuthenticationManagerBuilder(
|
|
||||||
org.springframework.security.config.annotation.ObjectPostProcessor<Object> objectPostProcessor) {
|
|
||||||
super(objectPostProcessor, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows providing a parent {@link AuthenticationManager} that will be tried if this
|
* Allows providing a parent {@link AuthenticationManager} that will be tried if this
|
||||||
* {@link AuthenticationManager} was unable to attempt to authenticate the provided
|
* {@link AuthenticationManager} was unable to attempt to authenticate the provided
|
||||||
|
@ -16,7 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.security.config.annotation.authentication.configuration;
|
package org.springframework.security.config.annotation.authentication.configuration;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
@ -62,24 +63,62 @@ class InitializeAuthenticationProviderBeanManagerConfigurer extends GlobalAuthen
|
|||||||
if (auth.isConfigured()) {
|
if (auth.isConfigured()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String[] beanNames = InitializeAuthenticationProviderBeanManagerConfigurer.this.context
|
List<BeanWithName<AuthenticationProvider>> authenticationProviders = getBeansWithName(
|
||||||
.getBeanNamesForType(AuthenticationProvider.class);
|
AuthenticationProvider.class);
|
||||||
if (beanNames.length == 0) {
|
if (authenticationProviders.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (beanNames.length > 1) {
|
else if (authenticationProviders.size() > 1) {
|
||||||
|
List<String> beanNames = authenticationProviders.stream().map(BeanWithName::getName).toList();
|
||||||
this.logger.info(LogMessage.format("Found %s AuthenticationProvider beans, with names %s. "
|
this.logger.info(LogMessage.format("Found %s AuthenticationProvider beans, with names %s. "
|
||||||
+ "Global Authentication Manager will not be configured with AuthenticationProviders. "
|
+ "Global Authentication Manager will not be configured with AuthenticationProviders. "
|
||||||
+ "Consider publishing a single AuthenticationProvider bean, or wiring your Providers directly "
|
+ "Consider publishing a single AuthenticationProvider bean, or wiring your Providers directly "
|
||||||
+ "using the DSL.", beanNames.length, Arrays.toString(beanNames)));
|
+ "using the DSL.", authenticationProviders.size(), beanNames));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
AuthenticationProvider authenticationProvider = InitializeAuthenticationProviderBeanManagerConfigurer.this.context
|
AuthenticationProvider authenticationProvider = authenticationProviders.get(0).getBean();
|
||||||
.getBean(beanNames[0], AuthenticationProvider.class);
|
String authenticationProviderBeanName = authenticationProviders.get(0).getName();
|
||||||
|
|
||||||
auth.authenticationProvider(authenticationProvider);
|
auth.authenticationProvider(authenticationProvider);
|
||||||
this.logger.info(LogMessage.format(
|
this.logger.info(LogMessage.format(
|
||||||
"Global AuthenticationManager configured with AuthenticationProvider bean with name %s",
|
"Global AuthenticationManager configured with AuthenticationProvider bean with name %s",
|
||||||
beanNames[0]));
|
authenticationProviderBeanName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a list of beans of the requested class, along with their names. If
|
||||||
|
* there are no registered beans of that type, the list is empty.
|
||||||
|
*/
|
||||||
|
private <T> List<BeanWithName<T>> getBeansWithName(Class<T> type) {
|
||||||
|
List<BeanWithName<T>> beanWithNames = new ArrayList<>();
|
||||||
|
String[] beanNames = InitializeAuthenticationProviderBeanManagerConfigurer.this.context
|
||||||
|
.getBeanNamesForType(type);
|
||||||
|
for (String beanName : beanNames) {
|
||||||
|
T bean = InitializeAuthenticationProviderBeanManagerConfigurer.this.context.getBean(beanName, type);
|
||||||
|
beanWithNames.add(new BeanWithName<>(bean, beanName));
|
||||||
|
}
|
||||||
|
return beanWithNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class BeanWithName<T> {
|
||||||
|
|
||||||
|
private final T bean;
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
BeanWithName(T bean, String name) {
|
||||||
|
this.bean = bean;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
T getBean() {
|
||||||
|
return this.bean;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.security.config.annotation.authentication.configuration;
|
package org.springframework.security.config.annotation.authentication.configuration;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
@ -66,10 +67,9 @@ class InitializeUserDetailsBeanManagerConfigurer extends GlobalAuthenticationCon
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configure(AuthenticationManagerBuilder auth) throws Exception {
|
public void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||||
String[] beanNames = InitializeUserDetailsBeanManagerConfigurer.this.context
|
List<BeanWithName<UserDetailsService>> userDetailsServices = getBeansWithName(UserDetailsService.class);
|
||||||
.getBeanNamesForType(UserDetailsService.class);
|
|
||||||
if (auth.isConfigured()) {
|
if (auth.isConfigured()) {
|
||||||
if (beanNames.length > 0) {
|
if (!userDetailsServices.isEmpty()) {
|
||||||
this.logger.warn("Global AuthenticationManager configured with an AuthenticationProvider bean. "
|
this.logger.warn("Global AuthenticationManager configured with an AuthenticationProvider bean. "
|
||||||
+ "UserDetailsService beans will not be used by Spring Security for automatically configuring username/password login. "
|
+ "UserDetailsService beans will not be used by Spring Security for automatically configuring username/password login. "
|
||||||
+ "Consider removing the AuthenticationProvider bean. "
|
+ "Consider removing the AuthenticationProvider bean. "
|
||||||
@ -80,25 +80,30 @@ class InitializeUserDetailsBeanManagerConfigurer extends GlobalAuthenticationCon
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (beanNames.length == 0) {
|
if (userDetailsServices.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (beanNames.length > 1) {
|
else if (userDetailsServices.size() > 1) {
|
||||||
|
List<String> beanNames = userDetailsServices.stream().map(BeanWithName::getName).toList();
|
||||||
this.logger.warn(LogMessage.format("Found %s UserDetailsService beans, with names %s. "
|
this.logger.warn(LogMessage.format("Found %s UserDetailsService beans, with names %s. "
|
||||||
+ "Global Authentication Manager will not use a UserDetailsService for username/password login. "
|
+ "Global Authentication Manager will not use a UserDetailsService for username/password login. "
|
||||||
+ "Consider publishing a single UserDetailsService bean.", beanNames.length,
|
+ "Consider publishing a single UserDetailsService bean.", userDetailsServices.size(),
|
||||||
Arrays.toString(beanNames)));
|
beanNames));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
UserDetailsService userDetailsService = InitializeUserDetailsBeanManagerConfigurer.this.context
|
UserDetailsService userDetailsService = userDetailsServices.get(0).getBean();
|
||||||
.getBean(beanNames[0], UserDetailsService.class);
|
String userDetailsServiceBeanName = userDetailsServices.get(0).getName();
|
||||||
PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
|
PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
|
||||||
UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);
|
UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);
|
||||||
CompromisedPasswordChecker passwordChecker = getBeanOrNull(CompromisedPasswordChecker.class);
|
CompromisedPasswordChecker passwordChecker = getBeanOrNull(CompromisedPasswordChecker.class);
|
||||||
DaoAuthenticationProvider provider = new DaoAuthenticationProvider(userDetailsService);
|
DaoAuthenticationProvider provider;
|
||||||
if (passwordEncoder != null) {
|
if (passwordEncoder != null) {
|
||||||
provider.setPasswordEncoder(passwordEncoder);
|
provider = new DaoAuthenticationProvider(passwordEncoder);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
provider = new DaoAuthenticationProvider();
|
||||||
|
}
|
||||||
|
provider.setUserDetailsService(userDetailsService);
|
||||||
if (passwordManager != null) {
|
if (passwordManager != null) {
|
||||||
provider.setUserDetailsPasswordService(passwordManager);
|
provider.setUserDetailsPasswordService(passwordManager);
|
||||||
}
|
}
|
||||||
@ -108,7 +113,8 @@ class InitializeUserDetailsBeanManagerConfigurer extends GlobalAuthenticationCon
|
|||||||
provider.afterPropertiesSet();
|
provider.afterPropertiesSet();
|
||||||
auth.authenticationProvider(provider);
|
auth.authenticationProvider(provider);
|
||||||
this.logger.info(LogMessage.format(
|
this.logger.info(LogMessage.format(
|
||||||
"Global AuthenticationManager configured with UserDetailsService bean with name %s", beanNames[0]));
|
"Global AuthenticationManager configured with UserDetailsService bean with name %s",
|
||||||
|
userDetailsServiceBeanName));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -119,6 +125,41 @@ class InitializeUserDetailsBeanManagerConfigurer extends GlobalAuthenticationCon
|
|||||||
return InitializeUserDetailsBeanManagerConfigurer.this.context.getBeanProvider(type).getIfUnique();
|
return InitializeUserDetailsBeanManagerConfigurer.this.context.getBeanProvider(type).getIfUnique();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a list of beans of the requested class, along with their names. If
|
||||||
|
* there are no registered beans of that type, the list is empty.
|
||||||
|
*/
|
||||||
|
private <T> List<BeanWithName<T>> getBeansWithName(Class<T> type) {
|
||||||
|
List<BeanWithName<T>> beanWithNames = new ArrayList<>();
|
||||||
|
String[] beanNames = InitializeUserDetailsBeanManagerConfigurer.this.context.getBeanNamesForType(type);
|
||||||
|
for (String beanName : beanNames) {
|
||||||
|
T bean = InitializeUserDetailsBeanManagerConfigurer.this.context.getBean(beanName, type);
|
||||||
|
beanWithNames.add(new BeanWithName<>(bean, beanName));
|
||||||
|
}
|
||||||
|
return beanWithNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class BeanWithName<T> {
|
||||||
|
|
||||||
|
private final T bean;
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
BeanWithName(T bean, String name) {
|
||||||
|
this.bean = bean;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
T getBean() {
|
||||||
|
return this.bean;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2024 the original author or authors.
|
* Copyright 2002-2021 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.
|
||||||
@ -25,6 +25,7 @@ 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;
|
||||||
@ -37,6 +38,7 @@ 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;
|
||||||
@ -59,8 +61,12 @@ 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";
|
||||||
@ -95,6 +101,7 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,23 +133,13 @@ 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 LdapAuthenticationProviderConfigurer} for further customizations
|
* @return the {@link ChannelSecurityConfigurer} for further customizations
|
||||||
*/
|
*/
|
||||||
public LdapAuthenticationProviderConfigurer<B> withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
|
public LdapAuthenticationProviderConfigurer<B> withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
|
||||||
addObjectPostProcessor(objectPostProcessor);
|
addObjectPostProcessor(objectPostProcessor);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
@Deprecated(since = "6.4", forRemoval = true)
|
|
||||||
public LdapAuthenticationProviderConfigurer<B> withObjectPostProcessor(
|
|
||||||
org.springframework.security.config.annotation.ObjectPostProcessor<?> objectPostProcessor) {
|
|
||||||
addObjectPostProcessor(objectPostProcessor);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the {@link LdapAuthoritiesPopulator} and defaults to
|
* Gets the {@link LdapAuthoritiesPopulator} and defaults to
|
||||||
* {@link DefaultLdapAuthoritiesPopulator}
|
* {@link DefaultLdapAuthoritiesPopulator}
|
||||||
@ -386,10 +383,6 @@ 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());
|
||||||
@ -465,6 +458,8 @@ 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;
|
||||||
@ -580,8 +575,14 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
|
|||||||
return contextSource;
|
return contextSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startEmbeddedLdapServer() {
|
private void startEmbeddedLdapServer() throws Exception {
|
||||||
if (unboundIdPresent) {
|
if (apacheDsPresent) {
|
||||||
|
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);
|
||||||
|
@ -41,8 +41,4 @@ public class InMemoryUserDetailsManagerConfigurer<B extends ProviderManagerBuild
|
|||||||
super(new InMemoryUserDetailsManager(new ArrayList<>()));
|
super(new InMemoryUserDetailsManager(new ArrayList<>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public B and() {
|
|
||||||
return getBuilder();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2024 the original author or authors.
|
* Copyright 2002-2013 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.
|
||||||
@ -36,7 +36,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
|||||||
public abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, C extends AbstractDaoAuthenticationConfigurer<B, C, U>, U extends UserDetailsService>
|
public abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, C extends AbstractDaoAuthenticationConfigurer<B, C, U>, U extends UserDetailsService>
|
||||||
extends UserDetailsAwareConfigurer<B, U> {
|
extends UserDetailsAwareConfigurer<B, U> {
|
||||||
|
|
||||||
private DaoAuthenticationProvider provider;
|
private DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
|
||||||
|
|
||||||
private final U userDetailsService;
|
private final U userDetailsService;
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ public abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderMana
|
|||||||
*/
|
*/
|
||||||
AbstractDaoAuthenticationConfigurer(U userDetailsService) {
|
AbstractDaoAuthenticationConfigurer(U userDetailsService) {
|
||||||
this.userDetailsService = userDetailsService;
|
this.userDetailsService = userDetailsService;
|
||||||
this.provider = new DaoAuthenticationProvider(userDetailsService);
|
this.provider.setUserDetailsService(userDetailsService);
|
||||||
if (userDetailsService instanceof UserDetailsPasswordService) {
|
if (userDetailsService instanceof UserDetailsPasswordService) {
|
||||||
this.provider.setUserDetailsPasswordService((UserDetailsPasswordService) userDetailsService);
|
this.provider.setUserDetailsPasswordService((UserDetailsPasswordService) userDetailsService);
|
||||||
}
|
}
|
||||||
@ -63,17 +63,6 @@ public abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderMana
|
|||||||
return (C) this;
|
return (C) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
@Deprecated(since = "6.4", forRemoval = true)
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public C withObjectPostProcessor(
|
|
||||||
org.springframework.security.config.annotation.ObjectPostProcessor<?> objectPostProcessor) {
|
|
||||||
addObjectPostProcessor(objectPostProcessor);
|
|
||||||
return (C) this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows specifying the {@link PasswordEncoder} to use with the
|
* Allows specifying the {@link PasswordEncoder} to use with the
|
||||||
* {@link DaoAuthenticationProvider}. The default is to use plain text.
|
* {@link DaoAuthenticationProvider}. The default is to use plain text.
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
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;
|
||||||
|
|
||||||
@ -32,7 +31,6 @@ 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;
|
||||||
|
|
||||||
@ -42,23 +40,21 @@ 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) {
|
||||||
List<AuthorizationAdvisor> advisors = new ArrayList<>();
|
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory(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() {
|
static MethodInterceptor authorizeReturnObjectMethodInterceptor(ObjectProvider<AuthorizationAdvisor> provider,
|
||||||
return new AuthorizeReturnObjectMethodInterceptor();
|
AuthorizationAdvisorProxyFactory authorizationProxyFactory) {
|
||||||
|
provider.forEach(authorizationProxyFactory::addAdvisor);
|
||||||
|
AuthorizeReturnObjectMethodInterceptor interceptor = new AuthorizeReturnObjectMethodInterceptor(
|
||||||
|
authorizationProxyFactory);
|
||||||
|
authorizationProxyFactory.addAdvisor(interceptor);
|
||||||
|
return interceptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2025 the original author or authors.
|
* Copyright 2002-2024 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,22 +16,13 @@
|
|||||||
|
|
||||||
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)
|
||||||
@ -43,45 +34,4 @@ 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,114 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -407,16 +407,6 @@ public class GlobalMethodSecurityConfiguration implements ImportAware, SmartInit
|
|||||||
this.objectPostProcessor = objectPostProcessor;
|
this.objectPostProcessor = objectPostProcessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
@Deprecated(since = "6.4", forRemoval = true)
|
|
||||||
@Autowired(required = false)
|
|
||||||
public void setObjectPostProcessor(
|
|
||||||
org.springframework.security.config.annotation.ObjectPostProcessor<Object> objectPostProcessor) {
|
|
||||||
this.objectPostProcessor = objectPostProcessor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
public void setMethodSecurityExpressionHandler(List<MethodSecurityExpressionHandler> handlers) {
|
public void setMethodSecurityExpressionHandler(List<MethodSecurityExpressionHandler> handlers) {
|
||||||
if (handlers.size() != 1) {
|
if (handlers.size() != 1) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2025 the original author or authors.
|
* Copyright 2002-2024 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,9 +41,6 @@ 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);
|
||||||
|
|
||||||
@ -70,9 +67,6 @@ 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());
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2025 the original author or authors.
|
* Copyright 2002-2024 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,11 +83,10 @@ final class ReactiveAuthorizationManagerMethodSecurityConfiguration
|
|||||||
|
|
||||||
private final AuthorizationManagerAfterReactiveMethodInterceptor postAuthorizeMethodInterceptor;
|
private final AuthorizationManagerAfterReactiveMethodInterceptor postAuthorizeMethodInterceptor;
|
||||||
|
|
||||||
ReactiveAuthorizationManagerMethodSecurityConfiguration(
|
@Autowired(required = false)
|
||||||
ObjectProvider<MethodSecurityExpressionHandler> expressionHandlers,
|
ReactiveAuthorizationManagerMethodSecurityConfiguration(MethodSecurityExpressionHandler expressionHandler,
|
||||||
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);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2025 the original author or authors.
|
* Copyright 2002-2022 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,9 +38,6 @@ 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);
|
||||||
|
|
||||||
@ -64,9 +61,6 @@ 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());
|
||||||
}
|
}
|
||||||
|
@ -33,16 +33,12 @@ 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.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2019-2024 the original author or authors.
|
* Copyright 2019 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,8 +16,6 @@
|
|||||||
|
|
||||||
package org.springframework.security.config.annotation.rsocket;
|
package org.springframework.security.config.annotation.rsocket;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@ -64,12 +62,8 @@ class RSocketSecurityConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
void setAuthenticationManagerPostProcessor(
|
void setAuthenticationManagerPostProcessor(ObjectPostProcessor<ReactiveAuthenticationManager> postProcessor) {
|
||||||
Map<String, ObjectPostProcessor<ReactiveAuthenticationManager>> postProcessors) {
|
this.postProcessor = postProcessor;
|
||||||
if (postProcessors.size() == 1) {
|
|
||||||
this.postProcessor = postProcessors.values().iterator().next();
|
|
||||||
}
|
|
||||||
this.postProcessor = postProcessors.get("rSocketAuthenticationManagerPostProcessor");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = RSOCKET_SECURITY_BEAN_NAME)
|
@Bean(name = RSOCKET_SECURITY_BEAN_NAME)
|
||||||
|
@ -29,7 +29,9 @@ import org.springframework.security.authorization.ObservationReactiveAuthorizati
|
|||||||
import org.springframework.security.authorization.ReactiveAuthorizationManager;
|
import org.springframework.security.authorization.ReactiveAuthorizationManager;
|
||||||
import org.springframework.security.config.ObjectPostProcessor;
|
import org.springframework.security.config.ObjectPostProcessor;
|
||||||
import org.springframework.security.config.observation.SecurityObservationSettings;
|
import org.springframework.security.config.observation.SecurityObservationSettings;
|
||||||
import org.springframework.security.rsocket.api.PayloadExchange;
|
import org.springframework.security.web.server.ObservationWebFilterChainDecorator;
|
||||||
|
import org.springframework.security.web.server.WebFilterChainProxy.WebFilterChainDecorator;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
@ -43,7 +45,7 @@ class ReactiveObservationConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
static ObjectPostProcessor<ReactiveAuthorizationManager<PayloadExchange>> rSocketAuthorizationManagerPostProcessor(
|
static ObjectPostProcessor<ReactiveAuthorizationManager<ServerWebExchange>> webAuthorizationManagerPostProcessor(
|
||||||
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||||
return new ObjectPostProcessor<>() {
|
return new ObjectPostProcessor<>() {
|
||||||
@Override
|
@Override
|
||||||
@ -57,7 +59,7 @@ class ReactiveObservationConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
static ObjectPostProcessor<ReactiveAuthenticationManager> rSocketAuthenticationManagerPostProcessor(
|
static ObjectPostProcessor<ReactiveAuthenticationManager> authenticationManagerPostProcessor(
|
||||||
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||||
return new ObjectPostProcessor<>() {
|
return new ObjectPostProcessor<>() {
|
||||||
@Override
|
@Override
|
||||||
@ -69,4 +71,18 @@ class ReactiveObservationConfiguration {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
static ObjectPostProcessor<WebFilterChainDecorator> filterChainDecoratorPostProcessor(
|
||||||
|
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||||
|
return new ObjectPostProcessor<>() {
|
||||||
|
@Override
|
||||||
|
public WebFilterChainDecorator postProcess(WebFilterChainDecorator object) {
|
||||||
|
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||||
|
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveRequests();
|
||||||
|
return active ? new ObservationWebFilterChainDecorator(r) : object;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2025 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
@ -40,7 +40,7 @@ import org.springframework.core.ResolvableType;
|
|||||||
import org.springframework.http.HttpMethod;
|
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.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;
|
||||||
@ -169,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 AbstractRequestMatcherRegistry}
|
* {@link AbstractConfigAttributeRequestMatcherRegistry}
|
||||||
* @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}
|
||||||
*/
|
*/
|
||||||
@ -199,12 +199,6 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
|||||||
* @since 5.8
|
* @since 5.8
|
||||||
*/
|
*/
|
||||||
public C requestMatchers(HttpMethod method, String... patterns) {
|
public C requestMatchers(HttpMethod method, String... patterns) {
|
||||||
if (anyPathsDontStartWithLeadingSlash(patterns)) {
|
|
||||||
this.logger.warn("One of the patterns in " + Arrays.toString(patterns)
|
|
||||||
+ " is missing a leading slash. This is discouraged; please include the "
|
|
||||||
+ "leading slash in all your request matcher patterns. In future versions of "
|
|
||||||
+ "Spring Security, leaving out the leading slash will result in an exception.");
|
|
||||||
}
|
|
||||||
if (!mvcPresent) {
|
if (!mvcPresent) {
|
||||||
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
|
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
|
||||||
}
|
}
|
||||||
@ -218,63 +212,119 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
|||||||
}
|
}
|
||||||
List<RequestMatcher> matchers = new ArrayList<>();
|
List<RequestMatcher> matchers = new ArrayList<>();
|
||||||
for (String pattern : patterns) {
|
for (String pattern : patterns) {
|
||||||
if (RequestMatcherFactory.usesPathPatterns()) {
|
AntPathRequestMatcher ant = new AntPathRequestMatcher(pattern, (method != null) ? method.name() : null);
|
||||||
matchers.add(RequestMatcherFactory.matcher(method, pattern));
|
MvcRequestMatcher mvc = createMvcMatchers(method, pattern).get(0);
|
||||||
}
|
matchers.add(new DeferredRequestMatcher((c) -> resolve(ant, mvc, c), mvc, ant));
|
||||||
else {
|
|
||||||
AntPathRequestMatcher ant = new AntPathRequestMatcher(pattern, (method != null) ? method.name() : null);
|
|
||||||
MvcRequestMatcher mvc = createMvcMatchers(method, pattern).get(0);
|
|
||||||
matchers.add(new DeferredRequestMatcher((c) -> resolve(ant, mvc, c), mvc, ant));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return requestMatchers(matchers.toArray(new RequestMatcher[0]));
|
return requestMatchers(matchers.toArray(new RequestMatcher[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean anyPathsDontStartWithLeadingSlash(String... patterns) {
|
private RequestMatcher resolve(AntPathRequestMatcher ant, MvcRequestMatcher mvc, ServletContext servletContext) {
|
||||||
for (String pattern : patterns) {
|
Map<String, ? extends ServletRegistration> registrations = mappableServletRegistrations(servletContext);
|
||||||
if (!pattern.startsWith("/")) {
|
if (registrations.isEmpty()) {
|
||||||
|
return new DispatcherServletDelegatingRequestMatcher(ant, mvc, new MockMvcRequestMatcher());
|
||||||
|
}
|
||||||
|
if (!hasDispatcherServlet(registrations)) {
|
||||||
|
return new DispatcherServletDelegatingRequestMatcher(ant, mvc, new MockMvcRequestMatcher());
|
||||||
|
}
|
||||||
|
ServletRegistration dispatcherServlet = requireOneRootDispatcherServlet(registrations);
|
||||||
|
if (dispatcherServlet != null) {
|
||||||
|
if (registrations.size() == 1) {
|
||||||
|
return mvc;
|
||||||
|
}
|
||||||
|
return new DispatcherServletDelegatingRequestMatcher(ant, mvc, servletContext);
|
||||||
|
}
|
||||||
|
dispatcherServlet = requireOnlyPathMappedDispatcherServlet(registrations);
|
||||||
|
if (dispatcherServlet != null) {
|
||||||
|
String mapping = dispatcherServlet.getMappings().iterator().next();
|
||||||
|
mvc.setServletPath(mapping.substring(0, mapping.length() - 2));
|
||||||
|
return mvc;
|
||||||
|
}
|
||||||
|
String errorMessage = computeErrorMessage(registrations.values());
|
||||||
|
throw new IllegalArgumentException(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, ? extends ServletRegistration> mappableServletRegistrations(ServletContext servletContext) {
|
||||||
|
Map<String, ServletRegistration> mappable = new LinkedHashMap<>();
|
||||||
|
for (Map.Entry<String, ? extends ServletRegistration> entry : servletContext.getServletRegistrations()
|
||||||
|
.entrySet()) {
|
||||||
|
if (!entry.getValue().getMappings().isEmpty()) {
|
||||||
|
mappable.put(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mappable;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasDispatcherServlet(Map<String, ? extends ServletRegistration> registrations) {
|
||||||
|
if (registrations == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (ServletRegistration registration : registrations.values()) {
|
||||||
|
if (isDispatcherServlet(registration)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private RequestMatcher resolve(AntPathRequestMatcher ant, MvcRequestMatcher mvc, ServletContext servletContext) {
|
private ServletRegistration requireOneRootDispatcherServlet(
|
||||||
ServletRegistrationsSupport registrations = new ServletRegistrationsSupport(servletContext);
|
Map<String, ? extends ServletRegistration> registrations) {
|
||||||
Collection<RegistrationMapping> mappings = registrations.mappings();
|
ServletRegistration rootDispatcherServlet = null;
|
||||||
if (mappings.isEmpty()) {
|
for (ServletRegistration registration : registrations.values()) {
|
||||||
return new DispatcherServletDelegatingRequestMatcher(ant, mvc, new MockMvcRequestMatcher());
|
if (!isDispatcherServlet(registration)) {
|
||||||
}
|
continue;
|
||||||
Collection<RegistrationMapping> dispatcherServletMappings = registrations.dispatcherServletMappings();
|
|
||||||
if (dispatcherServletMappings.isEmpty()) {
|
|
||||||
return new DispatcherServletDelegatingRequestMatcher(ant, mvc, new MockMvcRequestMatcher());
|
|
||||||
}
|
|
||||||
if (dispatcherServletMappings.size() > 1) {
|
|
||||||
String errorMessage = computeErrorMessage(servletContext.getServletRegistrations().values());
|
|
||||||
throw new IllegalArgumentException(errorMessage);
|
|
||||||
}
|
|
||||||
RegistrationMapping dispatcherServlet = dispatcherServletMappings.iterator().next();
|
|
||||||
if (mappings.size() > 1 && !dispatcherServlet.isDefault()) {
|
|
||||||
String errorMessage = computeErrorMessage(servletContext.getServletRegistrations().values());
|
|
||||||
throw new IllegalArgumentException(errorMessage);
|
|
||||||
}
|
|
||||||
if (dispatcherServlet.isDefault()) {
|
|
||||||
if (mappings.size() == 1) {
|
|
||||||
return mvc;
|
|
||||||
}
|
}
|
||||||
return new DispatcherServletDelegatingRequestMatcher(ant, mvc);
|
if (registration.getMappings().size() > 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!"/".equals(registration.getMappings().iterator().next())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
rootDispatcherServlet = registration;
|
||||||
|
}
|
||||||
|
return rootDispatcherServlet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServletRegistration requireOnlyPathMappedDispatcherServlet(
|
||||||
|
Map<String, ? extends ServletRegistration> registrations) {
|
||||||
|
ServletRegistration pathDispatcherServlet = null;
|
||||||
|
for (ServletRegistration registration : registrations.values()) {
|
||||||
|
if (!isDispatcherServlet(registration)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (registration.getMappings().size() > 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String mapping = registration.getMappings().iterator().next();
|
||||||
|
if (!mapping.startsWith("/") || !mapping.endsWith("/*")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (pathDispatcherServlet != null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
pathDispatcherServlet = registration;
|
||||||
|
}
|
||||||
|
return pathDispatcherServlet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isDispatcherServlet(ServletRegistration registration) {
|
||||||
|
Class<?> dispatcherServlet = ClassUtils.resolveClassName("org.springframework.web.servlet.DispatcherServlet",
|
||||||
|
null);
|
||||||
|
try {
|
||||||
|
Class<?> clazz = Class.forName(registration.getClassName());
|
||||||
|
return dispatcherServlet.isAssignableFrom(clazz);
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException ex) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return mvc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String computeErrorMessage(Collection<? extends ServletRegistration> registrations) {
|
private static String computeErrorMessage(Collection<? extends ServletRegistration> registrations) {
|
||||||
String template = """
|
String template = "This method cannot decide whether these patterns are Spring MVC patterns or not. "
|
||||||
This method cannot decide whether these patterns are Spring MVC patterns or not. \
|
+ "If this endpoint is a Spring MVC endpoint, please use requestMatchers(MvcRequestMatcher); "
|
||||||
This is because there is more than one mappable servlet in your servlet context: %s.
|
+ "otherwise, please use requestMatchers(AntPathRequestMatcher).\n\n"
|
||||||
|
+ "This is because there is more than one mappable servlet in your servlet context: %s.\n\n"
|
||||||
To address this, please create one PathPatternRequestMatcher.Builder#servletPath for each servlet that has \
|
+ "For each MvcRequestMatcher, call MvcRequestMatcher#setServletPath to indicate the servlet path.";
|
||||||
authorized endpoints and use them to construct request matchers manually.
|
|
||||||
""";
|
|
||||||
Map<String, Collection<String>> mappings = new LinkedHashMap<>();
|
Map<String, Collection<String>> mappings = new LinkedHashMap<>();
|
||||||
for (ServletRegistration registration : registrations) {
|
for (ServletRegistration registration : registrations) {
|
||||||
mappings.put(registration.getClassName(), registration.getMappings());
|
mappings.put(registration.getClassName(), registration.getMappings());
|
||||||
@ -453,12 +503,18 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
|||||||
|
|
||||||
static class DispatcherServletRequestMatcher implements RequestMatcher {
|
static class DispatcherServletRequestMatcher implements RequestMatcher {
|
||||||
|
|
||||||
|
private final ServletContext servletContext;
|
||||||
|
|
||||||
|
DispatcherServletRequestMatcher(ServletContext servletContext) {
|
||||||
|
this.servletContext = servletContext;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(HttpServletRequest request) {
|
public boolean matches(HttpServletRequest request) {
|
||||||
String name = request.getHttpServletMapping().getServletName();
|
String name = request.getHttpServletMapping().getServletName();
|
||||||
ServletRegistration registration = request.getServletContext().getServletRegistration(name);
|
ServletRegistration registration = this.servletContext.getServletRegistration(name);
|
||||||
Assert.notNull(registration,
|
Assert.notNull(registration,
|
||||||
() -> computeErrorMessage(request.getServletContext().getServletRegistrations().values()));
|
() -> computeErrorMessage(this.servletContext.getServletRegistrations().values()));
|
||||||
try {
|
try {
|
||||||
Class<?> clazz = Class.forName(registration.getClassName());
|
Class<?> clazz = Class.forName(registration.getClassName());
|
||||||
return DispatcherServlet.class.isAssignableFrom(clazz);
|
return DispatcherServlet.class.isAssignableFrom(clazz);
|
||||||
@ -478,8 +534,10 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
|||||||
|
|
||||||
private final RequestMatcher dispatcherServlet;
|
private final RequestMatcher dispatcherServlet;
|
||||||
|
|
||||||
DispatcherServletDelegatingRequestMatcher(AntPathRequestMatcher ant, MvcRequestMatcher mvc) {
|
DispatcherServletDelegatingRequestMatcher(AntPathRequestMatcher ant, MvcRequestMatcher mvc,
|
||||||
this(ant, mvc, new OrRequestMatcher(new MockMvcRequestMatcher(), new DispatcherServletRequestMatcher()));
|
ServletContext servletContext) {
|
||||||
|
this(ant, mvc, new OrRequestMatcher(new MockMvcRequestMatcher(),
|
||||||
|
new DispatcherServletRequestMatcher(servletContext)));
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatcherServletDelegatingRequestMatcher(AntPathRequestMatcher ant, MvcRequestMatcher mvc,
|
DispatcherServletDelegatingRequestMatcher(AntPathRequestMatcher ant, MvcRequestMatcher mvc,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2025 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
@ -135,7 +135,6 @@ public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>>
|
|||||||
* <li>{@link DisableEncodeUrlFilter}</li>
|
* <li>{@link DisableEncodeUrlFilter}</li>
|
||||||
* <li>{@link ForceEagerSessionCreationFilter}</li>
|
* <li>{@link ForceEagerSessionCreationFilter}</li>
|
||||||
* <li>{@link ChannelProcessingFilter}</li>
|
* <li>{@link ChannelProcessingFilter}</li>
|
||||||
* <li>{@link org.springframework.security.web.transport.HttpsRedirectFilter}</li>
|
|
||||||
* <li>{@link WebAsyncManagerIntegrationFilter}</li>
|
* <li>{@link WebAsyncManagerIntegrationFilter}</li>
|
||||||
* <li>{@link SecurityContextHolderFilter}</li>
|
* <li>{@link SecurityContextHolderFilter}</li>
|
||||||
* <li>{@link SecurityContextPersistenceFilter}</li>
|
* <li>{@link SecurityContextPersistenceFilter}</li>
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.web;
|
|
||||||
|
|
||||||
import org.springframework.context.ApplicationContext;
|
|
||||||
import org.springframework.http.HttpMethod;
|
|
||||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
|
||||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
|
||||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This utility exists only to facilitate applications opting into using path patterns in
|
|
||||||
* the HttpSecurity DSL. It is for internal use only.
|
|
||||||
*
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
@Deprecated(forRemoval = true)
|
|
||||||
public final class RequestMatcherFactory {
|
|
||||||
|
|
||||||
private static PathPatternRequestMatcher.Builder builder;
|
|
||||||
|
|
||||||
public static void setApplicationContext(ApplicationContext context) {
|
|
||||||
builder = context.getBeanProvider(PathPatternRequestMatcher.Builder.class).getIfUnique();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean usesPathPatterns() {
|
|
||||||
return builder != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RequestMatcher matcher(String path) {
|
|
||||||
return matcher(null, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RequestMatcher matcher(HttpMethod method, String path) {
|
|
||||||
if (builder != null) {
|
|
||||||
return builder.matcher(method, path);
|
|
||||||
}
|
|
||||||
return new AntPathRequestMatcher(path, (method != null) ? method.name() : null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private RequestMatcherFactory() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,77 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.web;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import jakarta.servlet.ServletContext;
|
|
||||||
import jakarta.servlet.ServletRegistration;
|
|
||||||
|
|
||||||
import org.springframework.util.ClassUtils;
|
|
||||||
|
|
||||||
class ServletRegistrationsSupport {
|
|
||||||
|
|
||||||
private final Collection<RegistrationMapping> registrations;
|
|
||||||
|
|
||||||
ServletRegistrationsSupport(ServletContext servletContext) {
|
|
||||||
Map<String, ? extends ServletRegistration> registrations = servletContext.getServletRegistrations();
|
|
||||||
Collection<RegistrationMapping> mappings = new ArrayList<>();
|
|
||||||
for (Map.Entry<String, ? extends ServletRegistration> entry : registrations.entrySet()) {
|
|
||||||
if (!entry.getValue().getMappings().isEmpty()) {
|
|
||||||
for (String mapping : entry.getValue().getMappings()) {
|
|
||||||
mappings.add(new RegistrationMapping(entry.getValue(), mapping));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.registrations = mappings;
|
|
||||||
}
|
|
||||||
|
|
||||||
Collection<RegistrationMapping> dispatcherServletMappings() {
|
|
||||||
Collection<RegistrationMapping> mappings = new ArrayList<>();
|
|
||||||
for (RegistrationMapping registration : this.registrations) {
|
|
||||||
if (registration.isDispatcherServlet()) {
|
|
||||||
mappings.add(registration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mappings;
|
|
||||||
}
|
|
||||||
|
|
||||||
Collection<RegistrationMapping> mappings() {
|
|
||||||
return this.registrations;
|
|
||||||
}
|
|
||||||
|
|
||||||
record RegistrationMapping(ServletRegistration registration, String mapping) {
|
|
||||||
boolean isDispatcherServlet() {
|
|
||||||
Class<?> dispatcherServlet = ClassUtils
|
|
||||||
.resolveClassName("org.springframework.web.servlet.DispatcherServlet", null);
|
|
||||||
try {
|
|
||||||
Class<?> clazz = Class.forName(this.registration.getClassName());
|
|
||||||
return dispatcherServlet.isAssignableFrom(clazz);
|
|
||||||
}
|
|
||||||
catch (ClassNotFoundException ex) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isDefault() {
|
|
||||||
return "/".equals(this.mapping);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2025 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
@ -31,7 +31,6 @@ import org.springframework.security.web.authentication.AuthenticationFilter;
|
|||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
import org.springframework.security.web.authentication.logout.LogoutFilter;
|
import org.springframework.security.web.authentication.logout.LogoutFilter;
|
||||||
import org.springframework.security.web.authentication.ott.GenerateOneTimeTokenFilter;
|
import org.springframework.security.web.authentication.ott.GenerateOneTimeTokenFilter;
|
||||||
import org.springframework.security.web.authentication.ott.OneTimeTokenAuthenticationFilter;
|
|
||||||
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
|
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
|
||||||
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
|
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
|
||||||
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;
|
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;
|
||||||
@ -54,7 +53,6 @@ import org.springframework.security.web.session.ConcurrentSessionFilter;
|
|||||||
import org.springframework.security.web.session.DisableEncodeUrlFilter;
|
import org.springframework.security.web.session.DisableEncodeUrlFilter;
|
||||||
import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
|
import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
|
||||||
import org.springframework.security.web.session.SessionManagementFilter;
|
import org.springframework.security.web.session.SessionManagementFilter;
|
||||||
import org.springframework.security.web.transport.HttpsRedirectFilter;
|
|
||||||
import org.springframework.web.filter.CorsFilter;
|
import org.springframework.web.filter.CorsFilter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,7 +77,6 @@ final class FilterOrderRegistration {
|
|||||||
put(DisableEncodeUrlFilter.class, order.next());
|
put(DisableEncodeUrlFilter.class, order.next());
|
||||||
put(ForceEagerSessionCreationFilter.class, order.next());
|
put(ForceEagerSessionCreationFilter.class, order.next());
|
||||||
put(ChannelProcessingFilter.class, order.next());
|
put(ChannelProcessingFilter.class, order.next());
|
||||||
put(HttpsRedirectFilter.class, order.next());
|
|
||||||
order.next(); // gh-8105
|
order.next(); // gh-8105
|
||||||
put(WebAsyncManagerIntegrationFilter.class, order.next());
|
put(WebAsyncManagerIntegrationFilter.class, order.next());
|
||||||
put(SecurityContextHolderFilter.class, order.next());
|
put(SecurityContextHolderFilter.class, order.next());
|
||||||
@ -104,7 +101,6 @@ final class FilterOrderRegistration {
|
|||||||
"org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter",
|
"org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter",
|
||||||
order.next());
|
order.next());
|
||||||
put(UsernamePasswordAuthenticationFilter.class, order.next());
|
put(UsernamePasswordAuthenticationFilter.class, order.next());
|
||||||
put(OneTimeTokenAuthenticationFilter.class, order.next());
|
|
||||||
order.next(); // gh-8105
|
order.next(); // gh-8105
|
||||||
put(DefaultResourcesFilter.class, order.next());
|
put(DefaultResourcesFilter.class, order.next());
|
||||||
put(DefaultLoginPageGeneratingFilter.class, order.next());
|
put(DefaultLoginPageGeneratingFilter.class, order.next());
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -35,9 +35,7 @@ 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;
|
||||||
@ -60,8 +58,6 @@ 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;
|
||||||
@ -139,14 +135,6 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
|||||||
super(objectPostProcessor);
|
super(objectPostProcessor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
@Deprecated(since = "6.4", forRemoval = true)
|
|
||||||
public WebSecurity(org.springframework.security.config.annotation.ObjectPostProcessor<Object> objectPostProcessor) {
|
|
||||||
super(objectPostProcessor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* Allows adding {@link RequestMatcher} instances that Spring Security should ignore.
|
* Allows adding {@link RequestMatcher} instances that Spring Security should ignore.
|
||||||
@ -235,8 +223,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 AuthorizationManagerWebInvocationPrivilegeEvaluator} will
|
* specified, then a {@link RequestMatcherDelegatingWebInvocationPrivilegeEvaluator}
|
||||||
* be created based on the list of {@link SecurityFilterChain}.
|
* will 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
|
||||||
*/
|
*/
|
||||||
@ -305,33 +293,34 @@ 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);
|
||||||
RequestMatcherDelegatingAuthorizationManager.Builder builder = RequestMatcherDelegatingAuthorizationManager
|
List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries = new ArrayList<>();
|
||||||
.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);
|
||||||
builder.add(ignoredRequest, SingleResultAuthorizationManager.permitAll());
|
requestMatcherPrivilegeEvaluatorsEntries
|
||||||
mappings = true;
|
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
|
||||||
}
|
}
|
||||||
|
boolean anyRequestConfigured = false;
|
||||||
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
|
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
|
||||||
SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
|
SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
|
||||||
|
Assert.isTrue(!anyRequestConfigured,
|
||||||
|
"A filter chain that matches any request has already been configured, which means that this filter chain ["
|
||||||
|
+ securityFilterChain
|
||||||
|
+ "] will never get invoked. Please use `HttpSecurity#securityMatcher` to ensure that there is only one filter chain configured for 'any request' and that the 'any request' filter chain is published last.");
|
||||||
|
if (securityFilterChain instanceof DefaultSecurityFilterChain defaultSecurityFilterChain) {
|
||||||
|
if (defaultSecurityFilterChain.getRequestMatcher() instanceof AnyRequestMatcher) {
|
||||||
|
anyRequestConfigured = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
securityFilterChains.add(securityFilterChain);
|
securityFilterChains.add(securityFilterChain);
|
||||||
mappings = addAuthorizationManager(securityFilterChain, builder) || mappings;
|
requestMatcherPrivilegeEvaluatorsEntries
|
||||||
|
.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(
|
||||||
List.of(new RequestMatcherEntry<>(AnyRequestMatcher.INSTANCE, List.of(privilegeEvaluator))));
|
requestMatcherPrivilegeEvaluatorsEntries);
|
||||||
}
|
}
|
||||||
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
|
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
|
||||||
if (this.httpFirewall != null) {
|
if (this.httpFirewall != null) {
|
||||||
@ -346,7 +335,6 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
|||||||
new HttpStatusRequestRejectedHandler());
|
new HttpStatusRequestRejectedHandler());
|
||||||
filterChainProxy.setRequestRejectedHandler(requestRejectedHandler);
|
filterChainProxy.setRequestRejectedHandler(requestRejectedHandler);
|
||||||
}
|
}
|
||||||
filterChainProxy.setFilterChainValidator(new WebSecurityFilterChainValidator());
|
|
||||||
filterChainProxy.setFilterChainDecorator(getFilterChainDecorator());
|
filterChainProxy.setFilterChainDecorator(getFilterChainDecorator());
|
||||||
filterChainProxy.afterPropertiesSet();
|
filterChainProxy.afterPropertiesSet();
|
||||||
|
|
||||||
@ -364,33 +352,30 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean addAuthorizationManager(SecurityFilterChain securityFilterChain,
|
private RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> getRequestMatcherPrivilegeEvaluatorsEntry(
|
||||||
RequestMatcherDelegatingAuthorizationManager.Builder builder) {
|
SecurityFilterChain securityFilterChain) {
|
||||||
boolean mappings = false;
|
List<WebInvocationPrivilegeEvaluator> privilegeEvaluators = new ArrayList<>();
|
||||||
for (Filter filter : securityFilterChain.getFilters()) {
|
for (Filter filter : securityFilterChain.getFilters()) {
|
||||||
if (filter instanceof FilterSecurityInterceptor securityInterceptor) {
|
if (filter instanceof FilterSecurityInterceptor) {
|
||||||
DefaultWebInvocationPrivilegeEvaluator privilegeEvaluator = new DefaultWebInvocationPrivilegeEvaluator(
|
DefaultWebInvocationPrivilegeEvaluator defaultWebInvocationPrivilegeEvaluator = new DefaultWebInvocationPrivilegeEvaluator(
|
||||||
securityInterceptor);
|
(FilterSecurityInterceptor) filter);
|
||||||
privilegeEvaluator.setServletContext(this.servletContext);
|
defaultWebInvocationPrivilegeEvaluator.setServletContext(this.servletContext);
|
||||||
AuthorizationManager<RequestAuthorizationContext> authorizationManager = (authentication, context) -> {
|
privilegeEvaluators.add(defaultWebInvocationPrivilegeEvaluator);
|
||||||
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 authorization) {
|
if (filter instanceof AuthorizationFilter) {
|
||||||
AuthorizationManager<HttpServletRequest> authorizationManager = authorization.getAuthorizationManager();
|
AuthorizationManager<HttpServletRequest> authorizationManager = ((AuthorizationFilter) filter)
|
||||||
builder.add(securityFilterChain::matches,
|
.getAuthorizationManager();
|
||||||
(authentication, context) -> (AuthorizationDecision) authorizationManager
|
AuthorizationManagerWebInvocationPrivilegeEvaluator evaluator = new AuthorizationManagerWebInvocationPrivilegeEvaluator(
|
||||||
.authorize(authentication, context.getRequest()));
|
authorizationManager);
|
||||||
mappings = true;
|
evaluator.setServletContext(this.servletContext);
|
||||||
|
if (this.privilegeEvaluatorRequestTransformer != null) {
|
||||||
|
evaluator.setRequestTransformer(this.privilegeEvaluatorRequestTransformer);
|
||||||
|
}
|
||||||
|
privilegeEvaluators.add(evaluator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mappings;
|
return new RequestMatcherEntry<>(securityFilterChain::matches, privilegeEvaluators);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,113 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2002-2024 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.builders;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import jakarta.servlet.Filter;
|
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
|
|
||||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
|
||||||
import org.springframework.security.web.FilterChainProxy;
|
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
|
||||||
import org.springframework.security.web.UnreachableFilterChainException;
|
|
||||||
import org.springframework.security.web.access.intercept.AuthorizationFilter;
|
|
||||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
|
||||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A filter chain validator for filter chains built by {@link WebSecurity}
|
|
||||||
*
|
|
||||||
* @author Josh Cummings
|
|
||||||
* @author Max Batischev
|
|
||||||
* @since 6.5
|
|
||||||
*/
|
|
||||||
final class WebSecurityFilterChainValidator implements FilterChainProxy.FilterChainValidator {
|
|
||||||
|
|
||||||
private final Log logger = LogFactory.getLog(getClass());
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void validate(FilterChainProxy filterChainProxy) {
|
|
||||||
List<SecurityFilterChain> chains = filterChainProxy.getFilterChains();
|
|
||||||
checkForAnyRequestRequestMatcher(chains);
|
|
||||||
checkForDuplicateMatchers(chains);
|
|
||||||
checkAuthorizationFilters(chains);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkForAnyRequestRequestMatcher(List<SecurityFilterChain> chains) {
|
|
||||||
DefaultSecurityFilterChain anyRequestFilterChain = null;
|
|
||||||
for (SecurityFilterChain chain : chains) {
|
|
||||||
if (anyRequestFilterChain != null) {
|
|
||||||
String message = "A filter chain that matches any request [" + anyRequestFilterChain
|
|
||||||
+ "] has already been configured, which means that this filter chain [" + chain
|
|
||||||
+ "] will never get invoked. Please use `HttpSecurity#securityMatcher` to ensure that there is only one filter chain configured for 'any request' and that the 'any request' filter chain is published last.";
|
|
||||||
throw new UnreachableFilterChainException(message, anyRequestFilterChain, chain);
|
|
||||||
}
|
|
||||||
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
|
|
||||||
if (defaultChain.getRequestMatcher() instanceof AnyRequestMatcher) {
|
|
||||||
anyRequestFilterChain = defaultChain;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkForDuplicateMatchers(List<SecurityFilterChain> chains) {
|
|
||||||
DefaultSecurityFilterChain filterChain = null;
|
|
||||||
for (SecurityFilterChain chain : chains) {
|
|
||||||
if (filterChain != null) {
|
|
||||||
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
|
|
||||||
if (defaultChain.getRequestMatcher().equals(filterChain.getRequestMatcher())) {
|
|
||||||
throw new UnreachableFilterChainException(
|
|
||||||
"The FilterChainProxy contains two filter chains using the" + " matcher "
|
|
||||||
+ defaultChain.getRequestMatcher(),
|
|
||||||
filterChain, defaultChain);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
|
|
||||||
filterChain = defaultChain;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkAuthorizationFilters(List<SecurityFilterChain> chains) {
|
|
||||||
Filter authorizationFilter = null;
|
|
||||||
Filter filterSecurityInterceptor = null;
|
|
||||||
for (SecurityFilterChain chain : chains) {
|
|
||||||
for (Filter filter : chain.getFilters()) {
|
|
||||||
if (filter instanceof AuthorizationFilter) {
|
|
||||||
authorizationFilter = filter;
|
|
||||||
}
|
|
||||||
if (filter instanceof FilterSecurityInterceptor) {
|
|
||||||
filterSecurityInterceptor = filter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (authorizationFilter != null && filterSecurityInterceptor != null) {
|
|
||||||
this.logger.warn(
|
|
||||||
"It is not recommended to use authorizeRequests in the configuration. Please only use authorizeHttpRequests");
|
|
||||||
}
|
|
||||||
if (filterSecurityInterceptor != null) {
|
|
||||||
this.logger.warn(
|
|
||||||
"Usage of authorizeRequests is deprecated. Please use authorizeHttpRequests in the configuration");
|
|
||||||
}
|
|
||||||
authorizationFilter = null;
|
|
||||||
filterSecurityInterceptor = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -46,9 +46,8 @@ import org.springframework.security.web.SecurityFilterChain;
|
|||||||
*
|
*
|
||||||
* @Bean
|
* @Bean
|
||||||
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
* http.authorizeHttpRequests((authorize) -> authorize
|
* http.authorizeHttpRequests().requestMatchers("/public/**").permitAll().anyRequest()
|
||||||
* .requestMatchers("/public/**").permitAll()
|
* .hasRole("USER").and()
|
||||||
* .anyRequest().hasRole("USER"))
|
|
||||||
* // 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
|
||||||
|
@ -35,7 +35,6 @@ import org.springframework.security.config.annotation.authentication.configurati
|
|||||||
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
|
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
|
||||||
import org.springframework.security.config.annotation.authentication.configurers.provisioning.JdbcUserDetailsManagerConfigurer;
|
import org.springframework.security.config.annotation.authentication.configurers.provisioning.JdbcUserDetailsManagerConfigurer;
|
||||||
import org.springframework.security.config.annotation.authentication.configurers.userdetails.DaoAuthenticationConfigurer;
|
import org.springframework.security.config.annotation.authentication.configurers.userdetails.DaoAuthenticationConfigurer;
|
||||||
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;
|
||||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||||
import org.springframework.security.config.annotation.web.configurers.DefaultLoginPageConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.DefaultLoginPageConfigurer;
|
||||||
@ -105,7 +104,6 @@ class HttpSecurityConfiguration {
|
|||||||
@Bean(HTTPSECURITY_BEAN_NAME)
|
@Bean(HTTPSECURITY_BEAN_NAME)
|
||||||
@Scope("prototype")
|
@Scope("prototype")
|
||||||
HttpSecurity httpSecurity() throws Exception {
|
HttpSecurity httpSecurity() throws Exception {
|
||||||
RequestMatcherFactory.setApplicationContext(this.context);
|
|
||||||
LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(this.context);
|
LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(this.context);
|
||||||
AuthenticationManagerBuilder authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(
|
AuthenticationManagerBuilder authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(
|
||||||
this.objectPostProcessor, passwordEncoder);
|
this.objectPostProcessor, passwordEncoder);
|
||||||
@ -180,17 +178,6 @@ class HttpSecurityConfiguration {
|
|||||||
this.defaultPasswordEncoder = defaultPasswordEncoder;
|
this.defaultPasswordEncoder = defaultPasswordEncoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
@Deprecated(since = "6.4", forRemoval = true)
|
|
||||||
DefaultPasswordEncoderAuthenticationManagerBuilder(
|
|
||||||
org.springframework.security.config.annotation.ObjectPostProcessor<Object> objectPostProcessor,
|
|
||||||
PasswordEncoder defaultPasswordEncoder) {
|
|
||||||
super(objectPostProcessor);
|
|
||||||
this.defaultPasswordEncoder = defaultPasswordEncoder;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication()
|
public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2025 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
@ -34,8 +34,6 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
|||||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||||
import org.springframework.context.ApplicationEventPublisher;
|
|
||||||
import org.springframework.context.ApplicationEventPublisherAware;
|
|
||||||
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
|
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@ -162,7 +160,7 @@ final class OAuth2ClientConfiguration {
|
|||||||
* @since 6.2.0
|
* @since 6.2.0
|
||||||
*/
|
*/
|
||||||
static final class OAuth2AuthorizedClientManagerRegistrar
|
static final class OAuth2AuthorizedClientManagerRegistrar
|
||||||
implements ApplicationEventPublisherAware, BeanDefinitionRegistryPostProcessor, BeanFactoryAware {
|
implements BeanDefinitionRegistryPostProcessor, BeanFactoryAware {
|
||||||
|
|
||||||
static final String BEAN_NAME = "authorizedClientManagerRegistrar";
|
static final String BEAN_NAME = "authorizedClientManagerRegistrar";
|
||||||
|
|
||||||
@ -181,8 +179,6 @@ final class OAuth2ClientConfiguration {
|
|||||||
|
|
||||||
private final AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
|
private final AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
|
||||||
|
|
||||||
private ApplicationEventPublisher applicationEventPublisher;
|
|
||||||
|
|
||||||
private ListableBeanFactory beanFactory;
|
private ListableBeanFactory beanFactory;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -306,10 +302,6 @@ final class OAuth2ClientConfiguration {
|
|||||||
authorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient);
|
authorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.applicationEventPublisher != null) {
|
|
||||||
authorizedClientProvider.setApplicationEventPublisher(this.applicationEventPublisher);
|
|
||||||
}
|
|
||||||
|
|
||||||
return authorizedClientProvider;
|
return authorizedClientProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,11 +423,6 @@ final class OAuth2ClientConfiguration {
|
|||||||
return objectProvider.getIfAvailable();
|
return objectProvider.getIfAvailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
|
||||||
this.applicationEventPublisher = applicationEventPublisher;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -78,8 +78,6 @@ 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
|
||||||
@ -121,6 +119,17 @@ 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() {
|
||||||
@ -135,15 +144,12 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
|
|||||||
}
|
}
|
||||||
|
|
||||||
String hmiRequestTransformerBeanName = HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME + "RequestTransformer";
|
String hmiRequestTransformerBeanName = HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME + "RequestTransformer";
|
||||||
if (!registry.containsBeanDefinition(PATH_PATTERN_REQUEST_TRANSFORMER_BEAN_NAME)
|
if (!registry.containsBeanDefinition(hmiRequestTransformerBeanName)) {
|
||||||
&& !registry.containsBeanDefinition(hmiRequestTransformerBeanName)) {
|
BeanDefinition hmiRequestTransformer = BeanDefinitionBuilder
|
||||||
if (!registry.containsBeanDefinition(hmiRequestTransformerBeanName)) {
|
.rootBeanDefinition(HandlerMappingIntrospectorRequestTransformer.class)
|
||||||
BeanDefinition hmiRequestTransformer = BeanDefinitionBuilder
|
.addConstructorArgReference(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)
|
||||||
.rootBeanDefinition(HandlerMappingIntrospectorRequestTransformer.class)
|
.getBeanDefinition();
|
||||||
.addConstructorArgReference(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)
|
registry.registerBeanDefinition(hmiRequestTransformerBeanName, hmiRequestTransformer);
|
||||||
.getBeanDefinition();
|
|
||||||
registry.registerBeanDefinition(hmiRequestTransformerBeanName, hmiRequestTransformer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BeanDefinition filterChainProxy = registry
|
BeanDefinition filterChainProxy = registry
|
||||||
@ -172,11 +178,7 @@ 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> {
|
||||||
|
|
||||||
@ -205,11 +207,7 @@ 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 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2025 the original author or authors.
|
* Copyright 2002-2022 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,29 +16,16 @@
|
|||||||
|
|
||||||
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.factory.BeanClassLoaderAware;
|
||||||
import org.springframework.beans.BeansException;
|
|
||||||
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;
|
||||||
@ -58,18 +45,11 @@ 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
|
||||||
@ -85,16 +65,26 @@ import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
|||||||
* @see WebSecurity
|
* @see WebSecurity
|
||||||
*/
|
*/
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
public class WebSecurityConfiguration implements ImportAware {
|
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
|
||||||
|
|
||||||
private WebSecurity webSecurity;
|
private WebSecurity webSecurity;
|
||||||
|
|
||||||
private Boolean debugEnabled;
|
private Boolean debugEnabled;
|
||||||
|
|
||||||
|
private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
|
||||||
|
|
||||||
private List<SecurityFilterChain> securityFilterChains = Collections.emptyList();
|
private List<SecurityFilterChain> securityFilterChains = Collections.emptyList();
|
||||||
|
|
||||||
private List<WebSecurityCustomizer> webSecurityCustomizers = Collections.emptyList();
|
private List<WebSecurityCustomizer> webSecurityCustomizers = Collections.emptyList();
|
||||||
|
|
||||||
|
private ClassLoader beanClassLoader;
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private ObjectPostProcessor<Object> objectObjectPostProcessor;
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private HttpSecurity httpSecurity;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public static DelegatingApplicationListener delegatingApplicationListener() {
|
public static DelegatingApplicationListener delegatingApplicationListener() {
|
||||||
return new DelegatingApplicationListener();
|
return new DelegatingApplicationListener();
|
||||||
@ -112,15 +102,14 @@ public class WebSecurityConfiguration implements ImportAware {
|
|||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
|
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
|
||||||
public Filter springSecurityFilterChain(ObjectProvider<HttpSecurity> provider) throws Exception {
|
public Filter springSecurityFilterChain() throws Exception {
|
||||||
boolean hasFilterChain = !this.securityFilterChains.isEmpty();
|
boolean hasFilterChain = !this.securityFilterChains.isEmpty();
|
||||||
if (!hasFilterChain) {
|
if (!hasFilterChain) {
|
||||||
this.webSecurity.addSecurityFilterChainBuilder(() -> {
|
this.webSecurity.addSecurityFilterChainBuilder(() -> {
|
||||||
HttpSecurity httpSecurity = provider.getObject();
|
this.httpSecurity.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated());
|
||||||
httpSecurity.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated());
|
this.httpSecurity.formLogin(Customizer.withDefaults());
|
||||||
httpSecurity.formLogin(Customizer.withDefaults());
|
this.httpSecurity.httpBasic(Customizer.withDefaults());
|
||||||
httpSecurity.httpBasic(Customizer.withDefaults());
|
return this.httpSecurity.build();
|
||||||
return httpSecurity.build();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
|
for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
|
||||||
@ -178,6 +167,7 @@ public class WebSecurityConfiguration implements ImportAware {
|
|||||||
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
|
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
|
||||||
this.webSecurity.apply(webSecurityConfigurer);
|
this.webSecurity.apply(webSecurityConfigurer);
|
||||||
}
|
}
|
||||||
|
this.webSecurityConfigurers = webSecurityConfigurers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
@ -206,49 +196,9 @@ public class WebSecurityConfiguration implements ImportAware {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Used to ensure Spring MVC request matching is cached.
|
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||||
*
|
this.beanClassLoader = classLoader;
|
||||||
* 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());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -284,121 +234,4 @@ 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ import java.util.Collections;
|
|||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.springframework.context.ApplicationContext;
|
|
||||||
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;
|
||||||
@ -29,7 +28,6 @@ import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
|||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
import org.springframework.security.web.PortMapper;
|
import org.springframework.security.web.PortMapper;
|
||||||
import org.springframework.security.web.PortResolver;
|
|
||||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||||
@ -274,10 +272,6 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
|
|||||||
if (portMapper != null) {
|
if (portMapper != null) {
|
||||||
this.authenticationEntryPoint.setPortMapper(portMapper);
|
this.authenticationEntryPoint.setPortMapper(portMapper);
|
||||||
}
|
}
|
||||||
PortResolver portResolver = getBeanOrNull(http, PortResolver.class);
|
|
||||||
if (portResolver != null) {
|
|
||||||
this.authenticationEntryPoint.setPortResolver(portResolver);
|
|
||||||
}
|
|
||||||
RequestCache requestCache = http.getSharedObject(RequestCache.class);
|
RequestCache requestCache = http.getSharedObject(RequestCache.class);
|
||||||
if (requestCache != null) {
|
if (requestCache != null) {
|
||||||
this.defaultSuccessHandler.setRequestCache(requestCache);
|
this.defaultSuccessHandler.setRequestCache(requestCache);
|
||||||
@ -418,14 +412,6 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
|
|||||||
this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(loginPage);
|
this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(loginPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <C> C getBeanOrNull(B http, Class<C> clazz) {
|
|
||||||
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
|
|
||||||
if (context == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return context.getBeanProvider(clazz).getIfUnique();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private T getSelf() {
|
private T getSelf() {
|
||||||
return (T) this;
|
return (T) this;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2025 the original author or authors.
|
* Copyright 2002-2013 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.
|
||||||
@ -22,9 +22,7 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.security.access.ConfigAttribute;
|
import org.springframework.security.access.ConfigAttribute;
|
||||||
import org.springframework.security.authorization.AuthorizationManager;
|
|
||||||
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
|
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
|
||||||
import org.springframework.security.core.annotation.SecurityAnnotationScanner;
|
|
||||||
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;
|
||||||
|
|
||||||
@ -38,14 +36,7 @@ import org.springframework.util.Assert;
|
|||||||
* @see ChannelSecurityConfigurer
|
* @see ChannelSecurityConfigurer
|
||||||
* @see UrlAuthorizationConfigurer
|
* @see UrlAuthorizationConfigurer
|
||||||
* @see ExpressionUrlAuthorizationConfigurer
|
* @see ExpressionUrlAuthorizationConfigurer
|
||||||
* @deprecated In modern Spring Security APIs, each API manages its own configuration
|
|
||||||
* context. As such there is no direct replacement for this interface. In the case of
|
|
||||||
* method security, please see {@link SecurityAnnotationScanner} and
|
|
||||||
* {@link AuthorizationManager}. In the case of channel security, please see
|
|
||||||
* {@code HttpsRedirectFilter}. In the case of web security, please see
|
|
||||||
* {@link AuthorizationManager}.
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
|
||||||
public abstract class AbstractConfigAttributeRequestMatcherRegistry<C> extends AbstractRequestMatcherRegistry<C> {
|
public abstract class AbstractConfigAttributeRequestMatcherRegistry<C> extends AbstractRequestMatcherRegistry<C> {
|
||||||
|
|
||||||
private List<UrlMapping> urlMappings = new ArrayList<>();
|
private List<UrlMapping> urlMappings = new ArrayList<>();
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2025 the original author or authors.
|
* Copyright 2002-2022 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.
|
||||||
@ -31,7 +31,6 @@ import org.springframework.security.web.DefaultSecurityFilterChain;
|
|||||||
* {@link HttpSecurity}.
|
* {@link HttpSecurity}.
|
||||||
*
|
*
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
* @author Ding Hao
|
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>, B extends HttpSecurityBuilder<B>>
|
public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>, B extends HttpSecurityBuilder<B>>
|
||||||
extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {
|
extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {
|
||||||
@ -55,24 +54,18 @@ public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T,
|
|||||||
return (T) this;
|
return (T) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
@Deprecated(since = "6.4", forRemoval = true)
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public T withObjectPostProcessor(
|
|
||||||
org.springframework.security.config.annotation.ObjectPostProcessor<?> objectPostProcessor) {
|
|
||||||
addObjectPostProcessor(objectPostProcessor);
|
|
||||||
return (T) this;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected SecurityContextHolderStrategy getSecurityContextHolderStrategy() {
|
protected SecurityContextHolderStrategy getSecurityContextHolderStrategy() {
|
||||||
if (this.securityContextHolderStrategy != null) {
|
if (this.securityContextHolderStrategy != null) {
|
||||||
return this.securityContextHolderStrategy;
|
return this.securityContextHolderStrategy;
|
||||||
}
|
}
|
||||||
ApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);
|
ApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);
|
||||||
this.securityContextHolderStrategy = context.getBeanProvider(SecurityContextHolderStrategy.class)
|
String[] names = context.getBeanNamesForType(SecurityContextHolderStrategy.class);
|
||||||
.getIfUnique(SecurityContextHolder::getContextHolderStrategy);
|
if (names.length == 1) {
|
||||||
|
this.securityContextHolderStrategy = context.getBean(SecurityContextHolderStrategy.class);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy();
|
||||||
|
}
|
||||||
return this.securityContextHolderStrategy;
|
return this.securityContextHolderStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2025 the original author or authors.
|
* Copyright 2002-2022 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.
|
||||||
@ -151,7 +151,6 @@ public abstract class AbstractInterceptUrlConfigurer<C extends AbstractIntercept
|
|||||||
return securityInterceptor;
|
return securityInterceptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public abstract class AbstractInterceptUrlRegistry<R extends AbstractInterceptUrlRegistry<R, T>, T>
|
public abstract class AbstractInterceptUrlRegistry<R extends AbstractInterceptUrlRegistry<R, T>, T>
|
||||||
extends AbstractConfigAttributeRequestMatcherRegistry<T> {
|
extends AbstractConfigAttributeRequestMatcherRegistry<T> {
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user