Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b512ea6352 | |||
| 076ef2cc74 | |||
| 3cedc1a2e4 | |||
| d32b23e497 | |||
| b58eef0d5d | |||
| 1202f08f3e | |||
| 8e315eae11 | |||
| 03a504b4ff | |||
| 3c458bc661 | |||
| 1bb9ed906e | |||
| fc9a3f95ec | |||
| 7f1700d043 | |||
| 56456601ab | |||
| 249ea55494 | |||
| 43f42d8e32 | |||
| 734d989c32 | |||
| 2f572278d4 | |||
| 816066ab60 | |||
| fb3f864d27 | |||
| ab29661a68 | |||
| 35abf87e2e | |||
| 4e3811cb29 |
@@ -0,0 +1,7 @@
|
|||||||
|
name: Build Release
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Build Release
|
||||||
|
shell: bash
|
||||||
|
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository publishAllPublicationsToDeploymentRepository
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
name: Test Release
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Test Release
|
||||||
|
shell: bash
|
||||||
|
run: ./gradlew build
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
workflow:
|
||||||
|
generator:
|
||||||
|
project:
|
||||||
|
java:
|
||||||
|
versions:
|
||||||
|
primary: 25
|
||||||
|
workflows:
|
||||||
|
release-train:
|
||||||
|
build:
|
||||||
|
env:
|
||||||
|
COMMERCIAL_REPO_USERNAME: secrets.COMMERCIAL_ARTIFACTORY_USERNAME
|
||||||
|
COMMERCIAL_REPO_PASSWORD: secrets.COMMERCIAL_ARTIFACTORY_PASSWORD
|
||||||
|
test:
|
||||||
|
env:
|
||||||
|
COMMERCIAL_REPO_USERNAME: secrets.COMMERCIAL_ARTIFACTORY_USERNAME
|
||||||
|
COMMERCIAL_REPO_PASSWORD: secrets.COMMERCIAL_ARTIFACTORY_PASSWORD
|
||||||
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
name: Merge Dependabot PR
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- 'docs-build'
|
|
||||||
|
|
||||||
run-name: Merge Dependabot PR ${{ github.ref_name }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
merge-dependabot-pr:
|
|
||||||
permissions: write-all
|
|
||||||
uses: spring-io/spring-github-workflows/.github/workflows/spring-merge-dependabot-pr.yml@0d3f15bb384839966a1ff5c4383731a2b747f24b # v7
|
|
||||||
with:
|
|
||||||
mergeArguments: --auto --rebase
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
name: CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
|
||||||
workflow_dispatch: # Manual trigger
|
|
||||||
|
|
||||||
env:
|
|
||||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
snapshot-test:
|
|
||||||
name: Test Against Snapshots
|
|
||||||
uses: spring-io/spring-security-release-tools/.github/workflows/test.yml@3f6cc7ffc137ca160061749d5f34dc30d5f36986 # v1.0.17
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- java-version: 25
|
|
||||||
toolchain: 25
|
|
||||||
with:
|
|
||||||
java-version: ${{ matrix.java-version }}
|
|
||||||
test-args: --refresh-dependencies -PforceMavenRepositories=snapshot,https://oss.sonatype.org/content/repositories/snapshots -PisOverrideVersionCatalog -PtestToolchain=${{ matrix.toolchain }} -PspringFrameworkVersion=7.0.+ -PreactorVersion=2025.+ -PspringDataVersion=2025.+ -PmicrometerVersion=1.+ --stacktrace
|
|
||||||
secrets: inherit
|
|
||||||
send-notification:
|
|
||||||
name: Send Notification
|
|
||||||
needs: [ snapshot-test ]
|
|
||||||
if: ${{ !success() }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Send Notification
|
|
||||||
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@3f6cc7ffc137ca160061749d5f34dc30d5f36986 # v1.0.17
|
|
||||||
with:
|
|
||||||
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
name: Clean build artifacts
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
main:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
|
||||||
permissions:
|
|
||||||
contents: none
|
|
||||||
steps:
|
|
||||||
- name: Delete artifacts in cron job
|
|
||||||
env:
|
|
||||||
GH_ACTIONS_REPO_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
|
||||||
run: |
|
|
||||||
echo "Running clean build artifacts logic"
|
|
||||||
output=$(curl -X GET -H "Authorization: token $GH_ACTIONS_REPO_TOKEN" https://api.github.com/repos/spring-projects/spring-security/actions/artifacts | grep '"id"' | cut -d : -f2 | sed 's/,*$//g')
|
|
||||||
echo Output is $output
|
|
||||||
for id in $output; do curl -X DELETE -H "Authorization: token $GH_ACTIONS_REPO_TOKEN" https://api.github.com/repos/spring-projects/spring-security/actions/artifacts/$id; done;
|
|
||||||
@@ -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@e415dadd0910c901e7a7fabd67bbb355b2324500 # 1
|
|
||||||
@@ -8,49 +8,46 @@ on:
|
|||||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||||
workflow_dispatch: # Manual trigger
|
workflow_dispatch: # Manual trigger
|
||||||
|
|
||||||
env:
|
|
||||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build
|
name: Build
|
||||||
uses: spring-io/spring-security-release-tools/.github/workflows/build.yml@3f6cc7ffc137ca160061749d5f34dc30d5f36986 # v1.0.17
|
uses: spring-projects/spring-security-commercial/.github/workflows/build.yml@workflows/v1
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ ubuntu-latest, windows-latest ]
|
|
||||||
jdk: [ 25 ]
|
|
||||||
with:
|
with:
|
||||||
runs-on: ${{ matrix.os }}
|
java-version: '25'
|
||||||
java-version: ${{ matrix.jdk }}
|
|
||||||
distribution: temurin
|
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
test:
|
||||||
|
name: Test Against Snapshots
|
||||||
|
uses: spring-projects/spring-security-commercial/.github/workflows/test.yml@workflows/v1
|
||||||
|
with:
|
||||||
|
java-version: '25'
|
||||||
|
test-args: --refresh-dependencies -PforceMavenRepositories=snapshot -PisOverrideVersionCatalog -PtestToolchain=25 -PspringFrameworkVersion=7.0.+ -PreactorVersion=2025.0.+ -PspringDataVersion=2026.0.+ -PmicrometerVersion=1.17.+ --stacktrace
|
||||||
|
secrets: inherit
|
||||||
|
compute-version:
|
||||||
|
name: Compute Version
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
snapshot: ${{ steps.project-version.outputs.snapshot }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # 7.0.0
|
||||||
|
- id: project-version
|
||||||
|
name: Extract Project Version
|
||||||
|
uses: spring-io/spring-release-actions/compute-version@a1f321783a0769dd2aea4fad6c2ae2f95a52b885 # 0.0.5
|
||||||
deploy-artifacts:
|
deploy-artifacts:
|
||||||
name: Deploy Artifacts
|
name: Deploy Artifacts
|
||||||
needs: [ build ]
|
needs: [ build, test, compute-version ]
|
||||||
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml@3f6cc7ffc137ca160061749d5f34dc30d5f36986 # v1.0.17
|
if: needs.compute-version.outputs.snapshot == 'true'
|
||||||
with:
|
uses: spring-projects/spring-security-commercial/.github/workflows/deploy-artifacts.yml@workflows/v1
|
||||||
should-deploy-artifacts: ${{ needs.build.outputs.should-deploy-artifacts }}
|
|
||||||
default-publish-milestones-central: true
|
|
||||||
java-version: 25
|
|
||||||
secrets: inherit
|
|
||||||
deploy-schema:
|
|
||||||
name: Deploy Schema
|
|
||||||
needs: [ build ]
|
|
||||||
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml@3f6cc7ffc137ca160061749d5f34dc30d5f36986 # v1.0.17
|
|
||||||
with:
|
|
||||||
should-deploy-schema: ${{ needs.build.outputs.should-deploy-artifacts }}
|
|
||||||
java-version: 25
|
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
send-notification:
|
send-notification:
|
||||||
name: Send Notification
|
name: Send Notification
|
||||||
needs: [ deploy-artifacts, deploy-schema ]
|
needs: [ deploy-artifacts ]
|
||||||
if: ${{ !success() }}
|
if: ${{ !success() }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Send Notification
|
- name: Send Notification
|
||||||
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@3f6cc7ffc137ca160061749d5f34dc30d5f36986 # v1.0.17
|
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
|
||||||
with:
|
with:
|
||||||
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
|
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
name: Defer Issues
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
defer-issues:
|
|
||||||
name: Defer Issues
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.repository_owner == 'spring-projects'
|
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
- name: Compute Version
|
|
||||||
id: compute-version
|
|
||||||
uses: spring-io/spring-release-actions/compute-version@1b8671612c3eb3d9b9763e2d7b66f1a80d00ee95 # 0.0.6
|
|
||||||
- name: Get Today's Release Version
|
|
||||||
id: todays-release
|
|
||||||
uses: spring-io/spring-release-actions/get-todays-release-version@1b8671612c3eb3d9b9763e2d7b66f1a80d00ee95 # 0.0.6
|
|
||||||
with:
|
|
||||||
snapshot-version: ${{ steps.compute-version.outputs.version }}
|
|
||||||
milestone-repository: ${{ github.repository }}
|
|
||||||
milestone-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: Compute Next Version
|
|
||||||
id: next-version
|
|
||||||
uses: spring-io/spring-release-actions/compute-next-version@1b8671612c3eb3d9b9763e2d7b66f1a80d00ee95 # 0.0.6
|
|
||||||
with:
|
|
||||||
version: ${{ steps.todays-release.outputs.release-version }}
|
|
||||||
- name: Schedule Next Milestone
|
|
||||||
uses: spring-io/spring-release-actions/schedule-milestone@1b8671612c3eb3d9b9763e2d7b66f1a80d00ee95 # 0.0.6
|
|
||||||
with:
|
|
||||||
version: ${{ steps.next-version.outputs.version }}
|
|
||||||
version-date: ${{ steps.next-version.outputs.version-date }}
|
|
||||||
repository: ${{ github.repository }}
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: Move Open Issues to Next Milestone
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
CURRENT_MILESTONE: ${{ steps.todays-release.outputs.release-version }}
|
|
||||||
NEXT_MILESTONE: ${{ steps.next-version.outputs.version }}
|
|
||||||
run: |
|
|
||||||
current_milestone_number=$(gh api repos/${{ github.repository }}/milestones \
|
|
||||||
--jq ".[] | select(.title == \"$CURRENT_MILESTONE\") | .number")
|
|
||||||
if [ -z "$current_milestone_number" ]; then
|
|
||||||
echo "No milestone found for $CURRENT_MILESTONE"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
next_milestone_number=$(gh api repos/${{ github.repository }}/milestones \
|
|
||||||
--jq ".[] | select(.title == \"$NEXT_MILESTONE\") | .number")
|
|
||||||
if [ -z "$next_milestone_number" ]; then
|
|
||||||
echo "No milestone found for $NEXT_MILESTONE"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Moving open issues from milestone '$CURRENT_MILESTONE' (#$current_milestone_number) to '$NEXT_MILESTONE' (#$next_milestone_number)"
|
|
||||||
page=1
|
|
||||||
while true; do
|
|
||||||
issues=$(gh api "repos/${{ github.repository }}/issues?milestone=$current_milestone_number&state=open&per_page=100&page=$page" \
|
|
||||||
--jq '.[].number')
|
|
||||||
if [ -z "$issues" ]; then
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
for issue in $issues; do
|
|
||||||
echo "Moving issue/PR #$issue to milestone $NEXT_MILESTONE"
|
|
||||||
gh api repos/${{ github.repository }}/issues/$issue \
|
|
||||||
--method PATCH \
|
|
||||||
--field milestone=$next_milestone_number \
|
|
||||||
--silent
|
|
||||||
done
|
|
||||||
page=$((page + 1))
|
|
||||||
done
|
|
||||||
echo "Done."
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
name: Dependabot PR Build
|
||||||
|
|
||||||
|
on: pull_request_target
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build
|
||||||
|
uses: spring-projects/spring-security-commercial/.github/workflows/build-pull-request.yml@workflows/v1
|
||||||
|
if: ${{ github.actor == 'dependabot[bot]' }}
|
||||||
|
secrets: inherit
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
name: Deploy Docs
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches-ignore:
|
|
||||||
- "gh-pages"
|
|
||||||
- "dependabot/**"
|
|
||||||
tags: '**'
|
|
||||||
repository_dispatch:
|
|
||||||
types: request-build-reference # legacy
|
|
||||||
#schedule:
|
|
||||||
#- cron: '0 10 * * *' # Once per day at 10am UTC
|
|
||||||
workflow_dispatch:
|
|
||||||
permissions: read-all
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.repository_owner == 'spring-projects'
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
with:
|
|
||||||
ref: docs-build
|
|
||||||
fetch-depth: 1
|
|
||||||
- name: Dispatch (partial build)
|
|
||||||
if: github.ref_type == 'branch'
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
|
||||||
run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) -f build-refname=${{ github.ref_name }}
|
|
||||||
- name: Dispatch (full build)
|
|
||||||
if: github.ref_type == 'tag'
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
|
||||||
run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD)
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
name: Execute Gradle Wrapper Upgrade
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 2 * * *' # 2am UTC
|
|
||||||
workflow_dispatch:
|
|
||||||
permissions:
|
|
||||||
pull-requests: write
|
|
||||||
jobs:
|
|
||||||
upgrade_wrapper:
|
|
||||||
name: Execution
|
|
||||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Set up Git configuration
|
|
||||||
env:
|
|
||||||
TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
git config --global url."https://unused-username:${TOKEN}@github.com/".insteadOf "https://github.com/"
|
|
||||||
git config --global user.name 'github-actions[bot]'
|
|
||||||
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
- name: Set up JDK 25
|
|
||||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
|
||||||
with:
|
|
||||||
java-version: '25'
|
|
||||||
distribution: 'temurin'
|
|
||||||
- name: Set up Gradle
|
|
||||||
uses: gradle/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1
|
|
||||||
- name: Upgrade Wrappers
|
|
||||||
run: ./gradlew clean upgradeGradleWrapperAll --continue -Porg.gradle.java.installations.auto-download=false
|
|
||||||
env:
|
|
||||||
WRAPPER_UPGRADE_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
@@ -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-commercial' }}
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
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 }}
|
||||||
@@ -8,44 +8,6 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build
|
name: Build
|
||||||
runs-on: ubuntu-latest
|
uses: spring-projects/spring-security-commercial/.github/workflows/build-pull-request.yml@workflows/v1
|
||||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
if: ${{ github.actor != 'dependabot[bot]' }}
|
||||||
steps:
|
secrets: inherit
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
- name: Set up gradle
|
|
||||||
uses: spring-io/spring-gradle-build-action@c8668747d7c264864c8c7f7026d0d277d14a78dc # v2.0.6
|
|
||||||
with:
|
|
||||||
java-version: '25'
|
|
||||||
distribution: 'temurin'
|
|
||||||
- name: Build with Gradle
|
|
||||||
run: ./gradlew clean build -PskipCheckExpectedBranchVersion --continue --scan
|
|
||||||
generate-docs:
|
|
||||||
name: Generate Docs
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
- name: Set up gradle
|
|
||||||
uses: spring-io/spring-gradle-build-action@c8668747d7c264864c8c7f7026d0d277d14a78dc # v2.0.6
|
|
||||||
with:
|
|
||||||
java-version: '25'
|
|
||||||
distribution: 'temurin'
|
|
||||||
- name: Run Antora
|
|
||||||
run: ./gradlew -PbuildSrc.skipTests=true :spring-security-docs:antora
|
|
||||||
- name: Upload Docs
|
|
||||||
id: upload
|
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
||||||
with:
|
|
||||||
name: docs
|
|
||||||
path: docs/build/site
|
|
||||||
overwrite: true
|
|
||||||
send-notification:
|
|
||||||
name: Send Notification
|
|
||||||
needs: [ build, generate-docs ]
|
|
||||||
if: ${{ failure() && github.event.pull_request.user.login == 'dependabot[bot]' && github.repository == 'spring-projects/spring-security' }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Send Notification
|
|
||||||
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@3f6cc7ffc137ca160061749d5f34dc30d5f36986 # v1.0.17
|
|
||||||
with:
|
|
||||||
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
|
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
name: Release Announcements - Publish
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
version:
|
|
||||||
description: The version to publish
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
actions: write
|
|
||||||
contents: read
|
|
||||||
issues: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy-schema:
|
|
||||||
name: Deploy Schema
|
|
||||||
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml@3f6cc7ffc137ca160061749d5f34dc30d5f36986 # v1.0.17
|
|
||||||
with:
|
|
||||||
should-deploy-schema: true
|
|
||||||
secrets: inherit
|
|
||||||
publish-release-announcements:
|
|
||||||
name: Publish Release Announcements
|
|
||||||
needs: [ deploy-schema ]
|
|
||||||
if: ${{ !github.event.repository.fork }}
|
|
||||||
uses: spring-io/spring-security-release-tools/.github/workflows/release-announcements-publish.yml@3f6cc7ffc137ca160061749d5f34dc30d5f36986 # v1.0.17
|
|
||||||
with:
|
|
||||||
version: ${{ inputs.version }}
|
|
||||||
secrets: inherit
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
name: Release Announcements - Stage
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- '[0-9]+.[0-9]+.[0-9]+'
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
version:
|
|
||||||
description: The version to stage
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
stage-release-announcements:
|
|
||||||
name: Stage Release Announcements
|
|
||||||
if: ${{ !github.event.repository.fork }}
|
|
||||||
uses: spring-io/spring-security-release-tools/.github/workflows/release-announcements-stage.yml@3f6cc7ffc137ca160061749d5f34dc30d5f36986 # v1.0.17
|
|
||||||
with:
|
|
||||||
version: ${{ inputs.version || github.ref_name }}
|
|
||||||
secrets: inherit
|
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
|
||||||
|
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
|
||||||
|
|
||||||
|
name: "Release Train – Build"
|
||||||
|
run-name: "${{ inputs.callback-ref }} – Build"
|
||||||
|
"on":
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
callback:
|
||||||
|
description: "Repository to which a callback should be made upon completion"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
callback-ref:
|
||||||
|
description: "Ref in the callback repository to which a callback should be made upon completion"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
release-train-maven-repository-url:
|
||||||
|
description: "URL of a Maven repository to be used to resolve artifacts of projects earlier in the train"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
permissions:
|
||||||
|
contents: "read"
|
||||||
|
concurrency:
|
||||||
|
group: "${{ github.workflow }}-${{ github.ref }}"
|
||||||
|
jobs:
|
||||||
|
build-release:
|
||||||
|
name: "Build Release"
|
||||||
|
runs-on: "ubuntu22-2-8"
|
||||||
|
steps:
|
||||||
|
- name: "Prevent Re-runs"
|
||||||
|
id: "prevent-re-runs"
|
||||||
|
run: |-
|
||||||
|
if [ "$GITHUB_RUN_ATTEMPT" -gt 1 ]; then
|
||||||
|
echo "Re-runs are prohibited. Use the 'Release Train – Retry' workflow to retry build failures"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
- name: "Set up Java"
|
||||||
|
id: "set-up-java"
|
||||||
|
uses: "actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95" # v5.6.0
|
||||||
|
with:
|
||||||
|
distribution: "liberica"
|
||||||
|
java-version: "25"
|
||||||
|
- name: "Check Out Code"
|
||||||
|
id: "check-out-code"
|
||||||
|
uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0
|
||||||
|
- name: "Build Release"
|
||||||
|
id: "build-release"
|
||||||
|
uses: "./.github/actions/release-train-build"
|
||||||
|
env:
|
||||||
|
COMMERCIAL_REPO_PASSWORD: "${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}"
|
||||||
|
COMMERCIAL_REPO_USERNAME: "${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}"
|
||||||
|
RELEASE_TRAIN_MAVEN_REPOSITORY_PASSWORD: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_PASSWORD }}"
|
||||||
|
RELEASE_TRAIN_MAVEN_REPOSITORY_URL: "${{ inputs.release-train-maven-repository-url }}"
|
||||||
|
RELEASE_TRAIN_MAVEN_REPOSITORY_USERNAME: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_USERNAME }}"
|
||||||
|
- name: "Upload Deployment Repository"
|
||||||
|
id: "upload-deployment-repository"
|
||||||
|
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1
|
||||||
|
with:
|
||||||
|
name: "deployment-repository"
|
||||||
|
path: "deployment-repository/**"
|
||||||
|
- name: "Upload Deployment Spec"
|
||||||
|
id: "upload-deployment-spec"
|
||||||
|
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1
|
||||||
|
with:
|
||||||
|
archive: "false"
|
||||||
|
if-no-files-found: "ignore"
|
||||||
|
name: "deployment-spec"
|
||||||
|
path: ".github/actions/release-train-build/deployment-spec.yml"
|
||||||
|
- name: "Save Build System Caches"
|
||||||
|
id: "save-build-system-caches"
|
||||||
|
uses: "actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9" # v6.1.0
|
||||||
|
with:
|
||||||
|
key: "release-train-${{ inputs.callback-ref }}-${{ github.ref_name }}"
|
||||||
|
path: |-
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
|
- name: "Send Callback"
|
||||||
|
id: "send-callback"
|
||||||
|
if: "${{ !cancelled() }}"
|
||||||
|
env:
|
||||||
|
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
|
||||||
|
run: |-
|
||||||
|
gh workflow run callback \
|
||||||
|
--repo ${{ inputs.callback }} \
|
||||||
|
--ref ${{ inputs.callback-ref }} \
|
||||||
|
--field commit-hash=${{ steps.check-out-code.outputs.commit }} \
|
||||||
|
--field deployment-repository-artifact-identifier=${{ steps.upload-deployment-repository.outputs.artifact-id }} \
|
||||||
|
--field deployment-spec-artifact-identifier=${{ steps.upload-deployment-spec.outputs.artifact-id }} \
|
||||||
|
--field release-branch=${{ github.ref_name }} \
|
||||||
|
--field release-repository=${{ github.repository }} \
|
||||||
|
--field result=${{ job.status == 'success' && 'built' || 'build-failed' }} \
|
||||||
|
--field workflow-run-url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
|
||||||
|
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
|
||||||
|
|
||||||
|
name: "Release Train – Join"
|
||||||
|
run-name: "${{ inputs.release-train }} – Join"
|
||||||
|
"on":
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
deployment-destination:
|
||||||
|
description: "Destination to which the release should be deployed"
|
||||||
|
options:
|
||||||
|
- "Maven Central"
|
||||||
|
- "Spring Enterprise"
|
||||||
|
required: true
|
||||||
|
type: "choice"
|
||||||
|
release-train:
|
||||||
|
description: "Release train"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
release-train-repository:
|
||||||
|
default: "spring-io/release-train"
|
||||||
|
description: "Release train repository"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
permissions:
|
||||||
|
contents: "none"
|
||||||
|
jobs:
|
||||||
|
join-release-train:
|
||||||
|
name: "Join Release Train"
|
||||||
|
runs-on: "ubuntu-latest"
|
||||||
|
steps:
|
||||||
|
- name: "Join Release Train"
|
||||||
|
id: "join-release-train"
|
||||||
|
env:
|
||||||
|
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
|
||||||
|
run: |-
|
||||||
|
run_url=$(
|
||||||
|
gh workflow run join \
|
||||||
|
--repo ${{ inputs.release-train-repository }} \
|
||||||
|
--ref ${{ inputs.release-train }} \
|
||||||
|
--field commit-hash=${{ github.sha }} \
|
||||||
|
--field deployment-destination=${{ inputs.deployment-destination == 'Maven Central' && 'maven-central' || 'spring-enterprise' }} \
|
||||||
|
--field release-branch=${{ github.ref_name }} \
|
||||||
|
--field release-repository=${{ github.repository }}
|
||||||
|
)
|
||||||
|
echo "Dispatched workflow run. Waiting for $run_url to complete."
|
||||||
|
run_id=${run_url##*/}
|
||||||
|
watch_exit_code=0
|
||||||
|
gh run watch $run_id --repo ${{ inputs.release-train-repository }} --exit-status --interval=3 > /dev/null 2>&1 || watch_exit_code=$?
|
||||||
|
if [[ $watch_exit_code -eq 0 ]]; then
|
||||||
|
echo "Workflow run succeeded."
|
||||||
|
else
|
||||||
|
echo "Workflow run failed."
|
||||||
|
fi
|
||||||
|
exit $watch_exit_code
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
|
||||||
|
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
|
||||||
|
|
||||||
|
name: "Release Train – Leave"
|
||||||
|
run-name: "${{ inputs.release-train }} – Leave"
|
||||||
|
"on":
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
release-train:
|
||||||
|
description: "Release train"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
release-train-repository:
|
||||||
|
default: "spring-io/release-train"
|
||||||
|
description: "Release train repository"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
permissions:
|
||||||
|
contents: "none"
|
||||||
|
jobs:
|
||||||
|
leave:
|
||||||
|
name: "Leave"
|
||||||
|
runs-on: "ubuntu-latest"
|
||||||
|
steps:
|
||||||
|
- name: "Leave"
|
||||||
|
id: "leave"
|
||||||
|
env:
|
||||||
|
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
|
||||||
|
run: |-
|
||||||
|
run_url=$(
|
||||||
|
gh workflow run leave \
|
||||||
|
--repo ${{ inputs.release-train-repository }} \
|
||||||
|
--ref ${{ inputs.release-train }} \
|
||||||
|
--field release-branch=${{ github.ref_name }} \
|
||||||
|
--field release-repository=${{ github.repository }}
|
||||||
|
)
|
||||||
|
echo "Dispatched workflow run. Waiting for $run_url to complete."
|
||||||
|
run_id=${run_url##*/}
|
||||||
|
watch_exit_code=0
|
||||||
|
gh run watch $run_id --repo ${{ inputs.release-train-repository }} --exit-status --interval=3 > /dev/null 2>&1 || watch_exit_code=$?
|
||||||
|
if [[ $watch_exit_code -eq 0 ]]; then
|
||||||
|
echo "Workflow run succeeded."
|
||||||
|
else
|
||||||
|
echo "Workflow run failed."
|
||||||
|
fi
|
||||||
|
exit $watch_exit_code
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
|
||||||
|
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
|
||||||
|
|
||||||
|
name: "Release Train – Ready"
|
||||||
|
run-name: "${{ inputs.release-train }} – Ready"
|
||||||
|
"on":
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
release-train:
|
||||||
|
description: "Release train"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
release-train-repository:
|
||||||
|
default: "spring-io/release-train"
|
||||||
|
description: "Release train repository"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
permissions:
|
||||||
|
contents: "none"
|
||||||
|
jobs:
|
||||||
|
ready:
|
||||||
|
name: "Ready"
|
||||||
|
runs-on: "ubuntu-latest"
|
||||||
|
steps:
|
||||||
|
- name: "Ready"
|
||||||
|
id: "ready"
|
||||||
|
env:
|
||||||
|
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
|
||||||
|
run: |-
|
||||||
|
run_url=$(
|
||||||
|
gh workflow run ready \
|
||||||
|
--repo ${{ inputs.release-train-repository }} \
|
||||||
|
--ref ${{ inputs.release-train }} \
|
||||||
|
--field commit-hash=${{ github.sha }} \
|
||||||
|
--field release-branch=${{ github.ref_name }} \
|
||||||
|
--field release-repository=${{ github.repository }}
|
||||||
|
)
|
||||||
|
echo "Dispatched workflow run. Waiting for $run_url to complete."
|
||||||
|
run_id=${run_url##*/}
|
||||||
|
watch_exit_code=0
|
||||||
|
gh run watch $run_id --repo ${{ inputs.release-train-repository }} --exit-status --interval=3 > /dev/null 2>&1 || watch_exit_code=$?
|
||||||
|
if [[ $watch_exit_code -eq 0 ]]; then
|
||||||
|
echo "Workflow run succeeded."
|
||||||
|
else
|
||||||
|
echo "Workflow run failed."
|
||||||
|
fi
|
||||||
|
exit $watch_exit_code
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
|
||||||
|
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
|
||||||
|
|
||||||
|
name: "Release Train – Retry"
|
||||||
|
run-name: "${{ inputs.release-train }} – Retry"
|
||||||
|
"on":
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
release-train:
|
||||||
|
description: "Release train"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
release-train-repository:
|
||||||
|
default: "spring-io/release-train"
|
||||||
|
description: "Release train repository"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
permissions:
|
||||||
|
contents: "none"
|
||||||
|
jobs:
|
||||||
|
trigger-retry:
|
||||||
|
name: "Trigger Retry"
|
||||||
|
runs-on: "ubuntu-latest"
|
||||||
|
steps:
|
||||||
|
- name: "Trigger Retry"
|
||||||
|
id: "trigger-retry"
|
||||||
|
env:
|
||||||
|
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
|
||||||
|
run: |-
|
||||||
|
gh workflow run retry \
|
||||||
|
--repo ${{ inputs.release-train-repository }} \
|
||||||
|
--ref ${{ inputs.release-train }} \
|
||||||
|
--field release-branch=${{ github.ref_name }} \
|
||||||
|
--field release-repository=${{ github.repository }}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
|
||||||
|
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
|
||||||
|
|
||||||
|
name: "Release Train – Test"
|
||||||
|
run-name: "${{ inputs.callback-ref }} – Test"
|
||||||
|
"on":
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
callback:
|
||||||
|
description: "Repository to which a callback should be made upon completion"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
callback-ref:
|
||||||
|
description: "Ref in the callback repository to which a callback should be made upon completion"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
release-train-maven-repository-url:
|
||||||
|
description: "URL of a Maven repository to be used to resolve artifacts of projects earlier in the train"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
permissions:
|
||||||
|
contents: "read"
|
||||||
|
concurrency:
|
||||||
|
group: "${{ github.workflow }}-${{ github.ref }}"
|
||||||
|
jobs:
|
||||||
|
test-release:
|
||||||
|
name: "Test Release"
|
||||||
|
runs-on: "ubuntu22-2-8"
|
||||||
|
steps:
|
||||||
|
- name: "Prevent Re-runs"
|
||||||
|
id: "prevent-re-runs"
|
||||||
|
run: |-
|
||||||
|
if [ "$GITHUB_RUN_ATTEMPT" -gt 1 ]; then
|
||||||
|
echo "Re-runs are prohibited. Use the 'Release Train – Retry' workflow to retry test failures"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
- name: "Set up Java"
|
||||||
|
id: "set-up-java"
|
||||||
|
uses: "actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95" # v5.6.0
|
||||||
|
with:
|
||||||
|
distribution: "liberica"
|
||||||
|
java-version: "25"
|
||||||
|
- name: "Check Out Code"
|
||||||
|
id: "check-out-code"
|
||||||
|
uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0
|
||||||
|
- name: "Restore Build System Caches"
|
||||||
|
id: "restore-build-system-caches"
|
||||||
|
uses: "actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9" # v6.1.0
|
||||||
|
with:
|
||||||
|
key: "release-train-${{ inputs.callback-ref }}-${{ github.ref_name }}"
|
||||||
|
path: |-
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
|
- name: "Test Release"
|
||||||
|
id: "test-release"
|
||||||
|
uses: "./.github/actions/release-train-test"
|
||||||
|
env:
|
||||||
|
COMMERCIAL_REPO_PASSWORD: "${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}"
|
||||||
|
COMMERCIAL_REPO_USERNAME: "${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}"
|
||||||
|
RELEASE_TRAIN_MAVEN_REPOSITORY_PASSWORD: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_PASSWORD }}"
|
||||||
|
RELEASE_TRAIN_MAVEN_REPOSITORY_URL: "${{ inputs.release-train-maven-repository-url }}"
|
||||||
|
RELEASE_TRAIN_MAVEN_REPOSITORY_USERNAME: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_USERNAME }}"
|
||||||
|
- name: "Send Callback"
|
||||||
|
id: "send-callback"
|
||||||
|
if: "${{ !cancelled() }}"
|
||||||
|
env:
|
||||||
|
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
|
||||||
|
run: |-
|
||||||
|
gh workflow run callback \
|
||||||
|
--repo ${{ inputs.callback }} \
|
||||||
|
--ref ${{ inputs.callback-ref }} \
|
||||||
|
--field commit-hash=${{ steps.check-out-code.outputs.commit }} \
|
||||||
|
--field release-branch=${{ github.ref_name }} \
|
||||||
|
--field release-repository=${{ github.repository }} \
|
||||||
|
--field result=${{ job.status == 'success' && 'tested' || 'test-failed' }} \
|
||||||
|
--field workflow-run-url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
- name: "Upload Build System Reports"
|
||||||
|
id: "upload-build-system-reports"
|
||||||
|
if: "${{ failure() }}"
|
||||||
|
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1
|
||||||
|
with:
|
||||||
|
name: "build-system-reports"
|
||||||
|
path: "**/build/reports"
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
name: Update Antora UI Spring
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
pull-requests: write
|
|
||||||
issues: write
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
update-antora-ui-spring:
|
|
||||||
name: Update on Supported Branches
|
|
||||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
branch: [ '6.5.x', '7.0.x', '7.1.x', 'main' ]
|
|
||||||
steps:
|
|
||||||
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@415e2b11a766ba64799fffb5c97a4f7e17f677cf # v0.0.22
|
|
||||||
name: Update
|
|
||||||
with:
|
|
||||||
docs-branch: ${{ matrix.branch }}
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
antora-file-path: 'docs/antora-playbook.yml'
|
|
||||||
update-antora-ui-spring-docs-build:
|
|
||||||
name: Update on docs-build
|
|
||||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@415e2b11a766ba64799fffb5c97a4f7e17f677cf # v0.0.22
|
|
||||||
name: Update
|
|
||||||
with:
|
|
||||||
docs-branch: 'docs-build'
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
+5
-8
@@ -27,6 +27,7 @@ apply plugin: 'org.jetbrains.kotlin.jvm'
|
|||||||
apply plugin: 'org.springframework.security.versions.verify-dependencies-versions'
|
apply plugin: 'org.springframework.security.versions.verify-dependencies-versions'
|
||||||
apply plugin: 'org.springframework.security.check-expected-branch-version'
|
apply plugin: 'org.springframework.security.check-expected-branch-version'
|
||||||
apply plugin: 'io.spring.security.release'
|
apply plugin: 'io.spring.security.release'
|
||||||
|
apply from: 'commercial-settings.gradle'
|
||||||
|
|
||||||
group = 'org.springframework.security'
|
group = 'org.springframework.security'
|
||||||
description = 'Spring Security'
|
description = 'Spring Security'
|
||||||
@@ -41,10 +42,12 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
springRelease {
|
springRelease {
|
||||||
|
repositoryOwner = "spring-projects"
|
||||||
|
repositoryName = "spring-security-commercial"
|
||||||
weekOfMonth = 3
|
weekOfMonth = 3
|
||||||
dayOfWeek = 1
|
dayOfWeek = 1
|
||||||
referenceDocUrl = "https://docs.spring.io/spring-security/reference/{version}/index.html"
|
referenceDocUrl = "https://docs.spring.vmware.com/spring-security/reference/{version}/index.html"
|
||||||
apiDocUrl = "https://docs.spring.io/spring-security/reference/{version}/api/java/index.html"
|
apiDocUrl = "https://docs.spring.vmware.com/spring-security/reference/{version}/api/java/index.html"
|
||||||
replaceSnapshotVersionInReferenceDocUrl = true
|
replaceSnapshotVersionInReferenceDocUrl = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,12 +75,6 @@ allprojects {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
develocity {
|
|
||||||
buildScan {
|
|
||||||
termsOfUseUrl = 'https://gradle.com/help/legal-terms-of-use'
|
|
||||||
termsOfUseAgree = 'yes'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nohttp {
|
nohttp {
|
||||||
source.exclude "buildSrc/build/**", "**/build/**", "**/target/**", "javascript/.gradle/**", "javascript/package-lock.json", "javascript/node_modules/**", "javascript/build/**", "javascript/dist/**"
|
source.exclude "buildSrc/build/**", "**/build/**", "**/target/**", "javascript/.gradle/**", "javascript/package-lock.json", "javascript/node_modules/**", "javascript/build/**", "javascript/dist/**"
|
||||||
|
|||||||
+10
-1
@@ -13,7 +13,16 @@ java {
|
|||||||
repositories {
|
repositories {
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven { url = 'https://repo.spring.io/snapshot' }
|
if (project.hasProperty("artifactoryUsername") && project.hasProperty("artifactoryPassword")) {
|
||||||
|
maven {
|
||||||
|
name "spring-commercial-release"
|
||||||
|
url "https://usw1.packages.broadcom.com/spring-enterprise-maven-prod-local"
|
||||||
|
credentials {
|
||||||
|
username project.artifactoryUsername
|
||||||
|
password project.artifactoryPassword
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (System.getenv("RELEASE_TRAIN_MAVEN_REPOSITORY_URL") != null) {
|
if (System.getenv("RELEASE_TRAIN_MAVEN_REPOSITORY_URL") != null) {
|
||||||
maven {
|
maven {
|
||||||
name = "Release Train"
|
name = "Release Train"
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
subprojects {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
def repoUsername = project.findProperty("artifactoryUsername") ?: System.getenv("COMMERCIAL_REPO_USERNAME")
|
||||||
|
def repoPassword = project.findProperty("artifactoryPassword") ?: System.getenv("COMMERCIAL_REPO_PASSWORD")
|
||||||
|
if (repoUsername && repoPassword) {
|
||||||
|
maven {
|
||||||
|
name "spring-commercial-release"
|
||||||
|
url "https://usw1.packages.broadcom.com/spring-enterprise-maven-prod-local"
|
||||||
|
credentials {
|
||||||
|
username repoUsername
|
||||||
|
password repoPassword
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ("$version".endsWith("-SNAPSHOT")) {
|
||||||
|
maven {
|
||||||
|
name "spring-commercial-snapshot"
|
||||||
|
url "https://usw1.packages.broadcom.com/spring-enterprise-maven-dev-local"
|
||||||
|
credentials {
|
||||||
|
username repoUsername
|
||||||
|
password repoPassword
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
-6
@@ -32,7 +32,6 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
import org.springframework.context.annotation.ImportAware;
|
import org.springframework.context.annotation.ImportAware;
|
||||||
import org.springframework.context.annotation.Role;
|
import org.springframework.context.annotation.Role;
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
import org.springframework.security.access.PermissionEvaluator;
|
|
||||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
||||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
|
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
|
||||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||||
@@ -128,11 +127,6 @@ final class PrePostMethodSecurityConfiguration implements ImportAware, Applicati
|
|||||||
this.expressionHandler.setAuthorizationManagerFactory(authorizationManagerFactory);
|
this.expressionHandler.setAuthorizationManagerFactory(authorizationManagerFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired(required = false)
|
|
||||||
void setPermissionEvaluator(PermissionEvaluator permissionEvaluator) {
|
|
||||||
this.expressionHandler.setPermissionEvaluator(permissionEvaluator);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {
|
void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {
|
||||||
this.preFilterMethodInterceptor.setTemplateDefaults(templateDefaults);
|
this.preFilterMethodInterceptor.setTemplateDefaults(templateDefaults);
|
||||||
|
|||||||
-27
@@ -297,7 +297,6 @@ import org.springframework.web.util.pattern.PathPatternParser;
|
|||||||
* @author Ankur Pathak
|
* @author Ankur Pathak
|
||||||
* @author Alexey Nesterov
|
* @author Alexey Nesterov
|
||||||
* @author Yanming Zhou
|
* @author Yanming Zhou
|
||||||
* @author Iain Henderson
|
|
||||||
* @since 5.0
|
* @since 5.0
|
||||||
*/
|
*/
|
||||||
public class ServerHttpSecurity {
|
public class ServerHttpSecurity {
|
||||||
@@ -4139,8 +4138,6 @@ public class ServerHttpSecurity {
|
|||||||
|
|
||||||
private ServerAuthenticationFailureHandler authenticationFailureHandler;
|
private ServerAuthenticationFailureHandler authenticationFailureHandler;
|
||||||
|
|
||||||
private ServerAuthenticationSuccessHandler authenticationSuccessHandler;
|
|
||||||
|
|
||||||
private ServerAccessDeniedHandler accessDeniedHandler = new BearerTokenServerAccessDeniedHandler();
|
private ServerAccessDeniedHandler accessDeniedHandler = new BearerTokenServerAccessDeniedHandler();
|
||||||
|
|
||||||
private ServerAuthenticationConverter bearerTokenConverter = new ServerBearerTokenAuthenticationConverter();
|
private ServerAuthenticationConverter bearerTokenConverter = new ServerBearerTokenAuthenticationConverter();
|
||||||
@@ -4189,20 +4186,6 @@ public class ServerHttpSecurity {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures the {@link ServerAuthenticationSuccessHandler} to use. The default
|
|
||||||
* is {@link WebFilterChainServerAuthenticationSuccessHandler}
|
|
||||||
* @param authenticationSuccessHandler the
|
|
||||||
* {@link ServerAuthenticationSuccessHandler} to use
|
|
||||||
* @return the {@link OAuth2ClientSpec} to customize
|
|
||||||
* @since 7.2
|
|
||||||
*/
|
|
||||||
public OAuth2ResourceServerSpec authenticationSuccessHandler(
|
|
||||||
ServerAuthenticationSuccessHandler authenticationSuccessHandler) {
|
|
||||||
this.authenticationSuccessHandler = authenticationSuccessHandler;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the {@link ServerAuthenticationConverter} to use for requests
|
* Configures the {@link ServerAuthenticationConverter} to use for requests
|
||||||
* authenticating with
|
* authenticating with
|
||||||
@@ -4271,7 +4254,6 @@ public class ServerHttpSecurity {
|
|||||||
AuthenticationWebFilter oauth2 = new AuthenticationWebFilter(this.authenticationManagerResolver);
|
AuthenticationWebFilter oauth2 = new AuthenticationWebFilter(this.authenticationManagerResolver);
|
||||||
oauth2.setServerAuthenticationConverter(this.bearerTokenConverter);
|
oauth2.setServerAuthenticationConverter(this.bearerTokenConverter);
|
||||||
oauth2.setAuthenticationFailureHandler(authenticationFailureHandler());
|
oauth2.setAuthenticationFailureHandler(authenticationFailureHandler());
|
||||||
oauth2.setAuthenticationSuccessHandler(authenticationSuccessHandler());
|
|
||||||
http.addFilterAt(oauth2, SecurityWebFiltersOrder.AUTHENTICATION);
|
http.addFilterAt(oauth2, SecurityWebFiltersOrder.AUTHENTICATION);
|
||||||
}
|
}
|
||||||
else if (this.jwt != null) {
|
else if (this.jwt != null) {
|
||||||
@@ -4331,13 +4313,6 @@ public class ServerHttpSecurity {
|
|||||||
return new ServerAuthenticationEntryPointFailureHandler(this.entryPoint);
|
return new ServerAuthenticationEntryPointFailureHandler(this.entryPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ServerAuthenticationSuccessHandler authenticationSuccessHandler() {
|
|
||||||
if (this.authenticationSuccessHandler != null) {
|
|
||||||
return this.authenticationSuccessHandler;
|
|
||||||
}
|
|
||||||
return new WebFilterChainServerAuthenticationSuccessHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures JWT Resource Server Support
|
* Configures JWT Resource Server Support
|
||||||
*/
|
*/
|
||||||
@@ -4412,7 +4387,6 @@ public class ServerHttpSecurity {
|
|||||||
AuthenticationWebFilter oauth2 = new AuthenticationWebFilter(authenticationManager);
|
AuthenticationWebFilter oauth2 = new AuthenticationWebFilter(authenticationManager);
|
||||||
oauth2.setServerAuthenticationConverter(OAuth2ResourceServerSpec.this.bearerTokenConverter);
|
oauth2.setServerAuthenticationConverter(OAuth2ResourceServerSpec.this.bearerTokenConverter);
|
||||||
oauth2.setAuthenticationFailureHandler(authenticationFailureHandler());
|
oauth2.setAuthenticationFailureHandler(authenticationFailureHandler());
|
||||||
oauth2.setAuthenticationSuccessHandler(authenticationSuccessHandler());
|
|
||||||
http.addFilterAt(oauth2, SecurityWebFiltersOrder.AUTHENTICATION);
|
http.addFilterAt(oauth2, SecurityWebFiltersOrder.AUTHENTICATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4545,7 +4519,6 @@ public class ServerHttpSecurity {
|
|||||||
AuthenticationWebFilter oauth2 = new AuthenticationWebFilter(authenticationManager);
|
AuthenticationWebFilter oauth2 = new AuthenticationWebFilter(authenticationManager);
|
||||||
oauth2.setServerAuthenticationConverter(OAuth2ResourceServerSpec.this.bearerTokenConverter);
|
oauth2.setServerAuthenticationConverter(OAuth2ResourceServerSpec.this.bearerTokenConverter);
|
||||||
oauth2.setAuthenticationFailureHandler(authenticationFailureHandler());
|
oauth2.setAuthenticationFailureHandler(authenticationFailureHandler());
|
||||||
oauth2.setAuthenticationSuccessHandler(authenticationSuccessHandler());
|
|
||||||
http.addFilterAt(oauth2, SecurityWebFiltersOrder.AUTHENTICATION);
|
http.addFilterAt(oauth2, SecurityWebFiltersOrder.AUTHENTICATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-5
@@ -20,7 +20,6 @@ import org.springframework.security.authentication.ReactiveAuthenticationManager
|
|||||||
import org.springframework.security.web.server.ServerAuthenticationEntryPoint
|
import org.springframework.security.web.server.ServerAuthenticationEntryPoint
|
||||||
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter
|
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter
|
||||||
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler
|
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler
|
||||||
import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler
|
|
||||||
import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler
|
import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler
|
||||||
import org.springframework.web.server.ServerWebExchange
|
import org.springframework.web.server.ServerWebExchange
|
||||||
|
|
||||||
@@ -36,8 +35,6 @@ import org.springframework.web.server.ServerWebExchange
|
|||||||
* @property bearerTokenConverter the [ServerAuthenticationConverter] to use for requests authenticating with
|
* @property bearerTokenConverter the [ServerAuthenticationConverter] to use for requests authenticating with
|
||||||
* Bearer Tokens.
|
* Bearer Tokens.
|
||||||
* @property authenticationManagerResolver the [ReactiveAuthenticationManagerResolver] to use.
|
* @property authenticationManagerResolver the [ReactiveAuthenticationManagerResolver] to use.
|
||||||
* @property authenticationSuccessHandler the [ServerAuthenticationSuccessHandler] to use after
|
|
||||||
* authentication success.
|
|
||||||
*/
|
*/
|
||||||
@ServerSecurityMarker
|
@ServerSecurityMarker
|
||||||
class ServerOAuth2ResourceServerDsl {
|
class ServerOAuth2ResourceServerDsl {
|
||||||
@@ -46,7 +43,6 @@ class ServerOAuth2ResourceServerDsl {
|
|||||||
var authenticationEntryPoint: ServerAuthenticationEntryPoint? = null
|
var authenticationEntryPoint: ServerAuthenticationEntryPoint? = null
|
||||||
var bearerTokenConverter: ServerAuthenticationConverter? = null
|
var bearerTokenConverter: ServerAuthenticationConverter? = null
|
||||||
var authenticationManagerResolver: ReactiveAuthenticationManagerResolver<ServerWebExchange>? = null
|
var authenticationManagerResolver: ReactiveAuthenticationManagerResolver<ServerWebExchange>? = null
|
||||||
var authenticationSuccessHandler: ServerAuthenticationSuccessHandler? = null
|
|
||||||
|
|
||||||
private var jwt: ((ServerHttpSecurity.OAuth2ResourceServerSpec.JwtSpec) -> Unit)? = null
|
private var jwt: ((ServerHttpSecurity.OAuth2ResourceServerSpec.JwtSpec) -> Unit)? = null
|
||||||
private var opaqueToken: ((ServerHttpSecurity.OAuth2ResourceServerSpec.OpaqueTokenSpec) -> Unit)? = null
|
private var opaqueToken: ((ServerHttpSecurity.OAuth2ResourceServerSpec.OpaqueTokenSpec) -> Unit)? = null
|
||||||
@@ -119,7 +115,6 @@ class ServerOAuth2ResourceServerDsl {
|
|||||||
authenticationEntryPoint?.also { oauth2ResourceServer.authenticationEntryPoint(authenticationEntryPoint) }
|
authenticationEntryPoint?.also { oauth2ResourceServer.authenticationEntryPoint(authenticationEntryPoint) }
|
||||||
bearerTokenConverter?.also { oauth2ResourceServer.bearerTokenConverter(bearerTokenConverter) }
|
bearerTokenConverter?.also { oauth2ResourceServer.bearerTokenConverter(bearerTokenConverter) }
|
||||||
authenticationManagerResolver?.also { oauth2ResourceServer.authenticationManagerResolver(authenticationManagerResolver!!) }
|
authenticationManagerResolver?.also { oauth2ResourceServer.authenticationManagerResolver(authenticationManagerResolver!!) }
|
||||||
authenticationSuccessHandler?.also { oauth2ResourceServer.authenticationSuccessHandler(authenticationSuccessHandler) }
|
|
||||||
jwt?.also { oauth2ResourceServer.jwt(jwt) }
|
jwt?.also { oauth2ResourceServer.jwt(jwt) }
|
||||||
opaqueToken?.also { oauth2ResourceServer.opaqueToken(opaqueToken) }
|
opaqueToken?.also { oauth2ResourceServer.opaqueToken(opaqueToken) }
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -25,12 +25,12 @@ public class MockSecurityContextHolderStrategy implements SecurityContextHolderS
|
|||||||
private SecurityContext context;
|
private SecurityContext context;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void clearContext() {
|
public void clearContext() {
|
||||||
this.context = null;
|
this.context = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized SecurityContext getContext() {
|
public SecurityContext getContext() {
|
||||||
if (this.context == null) {
|
if (this.context == null) {
|
||||||
this.context = createEmptyContext();
|
this.context = createEmptyContext();
|
||||||
}
|
}
|
||||||
@@ -38,7 +38,7 @@ public class MockSecurityContextHolderStrategy implements SecurityContextHolderS
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void setContext(SecurityContext context) {
|
public void setContext(SecurityContext context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-4
@@ -1527,13 +1527,13 @@ public class PrePostMethodSecurityConfigurationTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@EnableMethodSecurity
|
@EnableMethodSecurity
|
||||||
static class CustomPermissionEvaluatorConfig {
|
static class CustomPermissionEvaluatorConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
PermissionEvaluator permissionEvaluator() {
|
MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
|
||||||
return new PermissionEvaluator() {
|
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
|
||||||
|
expressionHandler.setPermissionEvaluator(new PermissionEvaluator() {
|
||||||
@Override
|
@Override
|
||||||
public boolean hasPermission(Authentication authentication, Object targetDomainObject,
|
public boolean hasPermission(Authentication authentication, Object targetDomainObject,
|
||||||
Object permission) {
|
Object permission) {
|
||||||
@@ -1545,7 +1545,8 @@ public class PrePostMethodSecurityConfigurationTests {
|
|||||||
Object permission) {
|
Object permission) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
return expressionHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
-180
@@ -73,11 +73,9 @@ import org.springframework.security.oauth2.server.resource.authentication.Reacti
|
|||||||
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
|
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
|
||||||
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenAuthenticationConverter;
|
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenAuthenticationConverter;
|
||||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||||
import org.springframework.security.web.server.WebFilterExchange;
|
|
||||||
import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint;
|
import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint;
|
||||||
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
|
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
|
||||||
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;
|
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;
|
||||||
import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;
|
|
||||||
import org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler;
|
import org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler;
|
||||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
@@ -373,79 +371,6 @@ public class OAuth2ResourceServerSpecTests {
|
|||||||
verify(handler).onAuthenticationFailure(any(), any());
|
verify(handler).onAuthenticationFailure(any(), any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getWhenUsingCustomAuthenticationSuccessHandlerThenUsesIsAccordingly() {
|
|
||||||
this.spring.register(CustomAuthenticationSuccessHandlerAuthenticationManagerResolverConfig.class).autowire();
|
|
||||||
ServerAuthenticationSuccessHandler handler = this.spring.getContext()
|
|
||||||
.getBean(ServerAuthenticationSuccessHandler.class);
|
|
||||||
ReactiveAuthenticationManager authenticationManager = this.spring.getContext()
|
|
||||||
.getBean(ReactiveAuthenticationManager.class);
|
|
||||||
given(authenticationManager.authenticate(any()))
|
|
||||||
.willAnswer((input) -> Mono.just(input.getArgument(0, Authentication.class)));
|
|
||||||
given(handler.onAuthenticationSuccess(any(), any())).willAnswer((input) -> {
|
|
||||||
WebFilterExchange webFilterExchange = input.getArgument(0, WebFilterExchange.class);
|
|
||||||
return webFilterExchange.getChain().filter(webFilterExchange.getExchange());
|
|
||||||
});
|
|
||||||
// @formatter:off
|
|
||||||
this.client.get()
|
|
||||||
.headers((headers) -> headers.setBearerAuth(this.messageReadToken))
|
|
||||||
.exchange()
|
|
||||||
.expectStatus().isUnauthorized();
|
|
||||||
// @formatter:on
|
|
||||||
verify(handler).onAuthenticationSuccess(any(), any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getWhenUsingCustomAuthenticationSuccessHandlerWithJwtThenUsesIsAccordingly() {
|
|
||||||
this.spring.register(CustomAuthenticationSuccessHandlerJwtConfig.class).autowire();
|
|
||||||
ServerAuthenticationSuccessHandler handler = this.spring.getContext()
|
|
||||||
.getBean(ServerAuthenticationSuccessHandler.class);
|
|
||||||
ReactiveAuthenticationManager authenticationManager = this.spring.getContext()
|
|
||||||
.getBean(ReactiveAuthenticationManager.class);
|
|
||||||
given(authenticationManager.authenticate(any()))
|
|
||||||
.willAnswer((input) -> Mono.just(input.getArgument(0, Authentication.class)));
|
|
||||||
given(handler.onAuthenticationSuccess(any(), any())).willAnswer((input) -> {
|
|
||||||
WebFilterExchange webFilterExchange = input.getArgument(0, WebFilterExchange.class);
|
|
||||||
return webFilterExchange.getChain().filter(webFilterExchange.getExchange());
|
|
||||||
});
|
|
||||||
// @formatter:off
|
|
||||||
this.client.get()
|
|
||||||
.headers((headers) -> headers.setBearerAuth(this.messageReadToken))
|
|
||||||
.exchange()
|
|
||||||
.expectStatus().isUnauthorized();
|
|
||||||
// @formatter:on
|
|
||||||
verify(handler).onAuthenticationSuccess(any(), any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getWhenUsingCustomAuthenticationSuccessHandlerWIthOpaqueTokenThenUsesIsAccordingly() {
|
|
||||||
this.spring.register(CustomAuthenticationSuccessHandlerOpaqueTokenConfig.class, RootController.class)
|
|
||||||
.autowire();
|
|
||||||
this.spring.getContext()
|
|
||||||
.getBean(MockWebServer.class)
|
|
||||||
.setDispatcher(requiresAuth(this.clientId, this.clientSecret, this.active));
|
|
||||||
ServerAuthenticationSuccessHandler handler = this.spring.getContext()
|
|
||||||
.getBean(ServerAuthenticationSuccessHandler.class);
|
|
||||||
ReactiveAuthenticationManager authenticationManager = this.spring.getContext()
|
|
||||||
.getBean(ReactiveAuthenticationManager.class);
|
|
||||||
given(authenticationManager.authenticate(any()))
|
|
||||||
.willAnswer((input) -> Mono.just(input.getArgument(0, Authentication.class)));
|
|
||||||
given(handler.onAuthenticationSuccess(any(), any())).willAnswer((input) -> {
|
|
||||||
WebFilterExchange webFilterExchange = input.getArgument(0, WebFilterExchange.class);
|
|
||||||
return webFilterExchange.getChain().filter(webFilterExchange.getExchange());
|
|
||||||
});
|
|
||||||
// @formatter:off
|
|
||||||
this.client.get()
|
|
||||||
.headers((headers) -> headers
|
|
||||||
.setBearerAuth(this.messageReadToken)
|
|
||||||
)
|
|
||||||
.exchange()
|
|
||||||
.expectStatus().isOk();
|
|
||||||
// @formatter:on
|
|
||||||
|
|
||||||
verify(handler).onAuthenticationSuccess(any(), any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void postWhenSignedThenReturnsOk() {
|
public void postWhenSignedThenReturnsOk() {
|
||||||
this.spring.register(PublicKeyConfig.class, RootController.class).autowire();
|
this.spring.register(PublicKeyConfig.class, RootController.class).autowire();
|
||||||
@@ -1025,111 +950,6 @@ public class OAuth2ResourceServerSpecTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@EnableWebFlux
|
|
||||||
@EnableWebFluxSecurity
|
|
||||||
static class CustomAuthenticationSuccessHandlerAuthenticationManagerResolverConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
SecurityWebFilterChain springSecurity(ServerHttpSecurity http) {
|
|
||||||
// @formatter:off
|
|
||||||
http
|
|
||||||
.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())
|
|
||||||
.oauth2ResourceServer((oauth2) -> oauth2
|
|
||||||
.authenticationSuccessHandler(authenticationSuccessHandler())
|
|
||||||
.authenticationManagerResolver((exchange) -> Mono.just(authenticationManager()))
|
|
||||||
);
|
|
||||||
// @formatter:on
|
|
||||||
return http.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
ReactiveAuthenticationManager authenticationManager() {
|
|
||||||
return mock(ReactiveAuthenticationManager.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
ServerAuthenticationSuccessHandler authenticationSuccessHandler() {
|
|
||||||
return mock(ServerAuthenticationSuccessHandler.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@EnableWebFlux
|
|
||||||
@EnableWebFluxSecurity
|
|
||||||
static class CustomAuthenticationSuccessHandlerJwtConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
SecurityWebFilterChain springSecurity(ServerHttpSecurity http) {
|
|
||||||
// @formatter:off
|
|
||||||
http
|
|
||||||
.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())
|
|
||||||
.oauth2ResourceServer((oauth2) -> oauth2
|
|
||||||
.authenticationSuccessHandler(authenticationSuccessHandler())
|
|
||||||
.jwt((jwt) -> jwt.authenticationManager(authenticationManager()))
|
|
||||||
);
|
|
||||||
// @formatter:on
|
|
||||||
return http.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
ReactiveAuthenticationManager authenticationManager() {
|
|
||||||
return mock(ReactiveAuthenticationManager.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
ServerAuthenticationSuccessHandler authenticationSuccessHandler() {
|
|
||||||
return mock(ServerAuthenticationSuccessHandler.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@EnableWebFlux
|
|
||||||
@EnableWebFluxSecurity
|
|
||||||
static class CustomAuthenticationSuccessHandlerOpaqueTokenConfig {
|
|
||||||
|
|
||||||
private MockWebServer mockWebServer = new MockWebServer();
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
SecurityWebFilterChain springSecurity(ServerHttpSecurity http) {
|
|
||||||
String introspectionUri = mockWebServer().url("/introspect").toString();
|
|
||||||
// @formatter:off
|
|
||||||
http
|
|
||||||
.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())
|
|
||||||
.oauth2ResourceServer((oauth2) -> oauth2
|
|
||||||
.authenticationSuccessHandler(authenticationSuccessHandler())
|
|
||||||
.opaqueToken((opaqueToken) -> opaqueToken
|
|
||||||
.introspectionUri(introspectionUri)
|
|
||||||
.introspectionClientCredentials("client", "secret"))
|
|
||||||
);
|
|
||||||
// @formatter:on
|
|
||||||
return http.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
ReactiveAuthenticationManager authenticationManager() {
|
|
||||||
return mock(ReactiveAuthenticationManager.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
ServerAuthenticationSuccessHandler authenticationSuccessHandler() {
|
|
||||||
return mock(ServerAuthenticationSuccessHandler.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
MockWebServer mockWebServer() {
|
|
||||||
return this.mockWebServer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreDestroy
|
|
||||||
void shutdown() throws IOException {
|
|
||||||
this.mockWebServer.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@EnableWebFlux
|
@EnableWebFlux
|
||||||
@EnableWebFluxSecurity
|
@EnableWebFluxSecurity
|
||||||
static class CustomBearerTokenServerAuthenticationConverter {
|
static class CustomBearerTokenServerAuthenticationConverter {
|
||||||
|
|||||||
-42
@@ -17,7 +17,6 @@
|
|||||||
package org.springframework.security.config.web.server
|
package org.springframework.security.config.web.server
|
||||||
|
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
|
||||||
import io.mockk.mockkObject
|
import io.mockk.mockkObject
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
@@ -38,7 +37,6 @@ import org.springframework.security.web.server.SecurityWebFilterChain
|
|||||||
import org.springframework.security.web.server.WebFilterExchange
|
import org.springframework.security.web.server.WebFilterExchange
|
||||||
import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint
|
import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint
|
||||||
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler
|
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler
|
||||||
import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler
|
|
||||||
import org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler
|
import org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler
|
||||||
import org.springframework.test.web.reactive.server.WebTestClient
|
import org.springframework.test.web.reactive.server.WebTestClient
|
||||||
import org.springframework.web.reactive.config.EnableWebFlux
|
import org.springframework.web.reactive.config.EnableWebFlux
|
||||||
@@ -185,46 +183,6 @@ class ServerOAuth2ResourceServerDslTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `request when custom authentication success handler then success handler used`() {
|
|
||||||
this.spring.register(AuthenticationSuccessHandlerConfig::class.java).autowire()
|
|
||||||
every {
|
|
||||||
AuthenticationSuccessHandlerConfig.SUCCESS_HANDLER.onAuthenticationSuccess(any(), any())
|
|
||||||
} returns Mono.empty()
|
|
||||||
|
|
||||||
this.client.get()
|
|
||||||
.uri("/")
|
|
||||||
.headers { it.setBearerAuth(validJwt) }
|
|
||||||
.exchange()
|
|
||||||
|
|
||||||
verify(exactly = 1) { AuthenticationSuccessHandlerConfig.SUCCESS_HANDLER.onAuthenticationSuccess(any(), any()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@EnableWebFluxSecurity
|
|
||||||
@EnableWebFlux
|
|
||||||
open class AuthenticationSuccessHandlerConfig {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val SUCCESS_HANDLER: ServerAuthenticationSuccessHandler = mockk()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
|
||||||
return http {
|
|
||||||
authorizeExchange {
|
|
||||||
authorize(anyExchange, authenticated)
|
|
||||||
}
|
|
||||||
oauth2ResourceServer {
|
|
||||||
authenticationSuccessHandler = SUCCESS_HANDLER
|
|
||||||
jwt {
|
|
||||||
publicKey = publicKey()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `request when custom bearer token converter configured then custom converter used`() {
|
fun `request when custom bearer token converter configured then custom converter used`() {
|
||||||
this.spring.register(BearerTokenConverterConfig::class.java).autowire()
|
this.spring.register(BearerTokenConverterConfig::class.java).autowire()
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import org.springframework.security.core.AuthenticationException;
|
|||||||
import org.springframework.security.core.CredentialsContainer;
|
import org.springframework.security.core.CredentialsContainer;
|
||||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterates an {@link Authentication} request through a list of
|
* Iterates an {@link Authentication} request through a list of
|
||||||
@@ -137,7 +138,8 @@ public class ProviderManager implements AuthenticationManager, MessageSourceAwar
|
|||||||
private void checkState() {
|
private void checkState() {
|
||||||
Assert.isTrue(this.parent != null || !this.providers.isEmpty(),
|
Assert.isTrue(this.parent != null || !this.providers.isEmpty(),
|
||||||
"A parent AuthenticationManager or a list of AuthenticationProviders is required");
|
"A parent AuthenticationManager or a list of AuthenticationProviders is required");
|
||||||
Assert.noNullElements(this.providers, "providers list cannot contain null values");
|
Assert.isTrue(!CollectionUtils.contains(this.providers.iterator(), null),
|
||||||
|
"providers list cannot contain null values");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+1
-1
@@ -123,7 +123,7 @@ public final class PostFilterAuthorizationMethodInterceptor implements Authoriza
|
|||||||
/**
|
/**
|
||||||
* Filter a {@code returnedObject} using the {@link PostFilter} annotation that the
|
* Filter a {@code returnedObject} using the {@link PostFilter} annotation that the
|
||||||
* {@link MethodInvocation} specifies.
|
* {@link MethodInvocation} specifies.
|
||||||
* @param mi the {@link MethodInvocation} to check
|
* @param mi the {@link MethodInvocation} to check check
|
||||||
* @return filtered {@code returnedObject}
|
* @return filtered {@code returnedObject}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+1
-1
@@ -131,7 +131,7 @@ public final class DelegatingSecurityContextCallable<V> implements Callable<V> {
|
|||||||
/**
|
/**
|
||||||
* Creates a {@link DelegatingSecurityContextCallable} and with the given
|
* Creates a {@link DelegatingSecurityContextCallable} and with the given
|
||||||
* {@link Callable} and {@link SecurityContext}, but if the securityContext is null
|
* {@link Callable} and {@link SecurityContext}, but if the securityContext is null
|
||||||
* will default to the current {@link SecurityContext} on the
|
* will defaults to the current {@link SecurityContext} on the
|
||||||
* {@link SecurityContextHolder}
|
* {@link SecurityContextHolder}
|
||||||
* @param delegate the delegate {@link DelegatingSecurityContextCallable} to run with
|
* @param delegate the delegate {@link DelegatingSecurityContextCallable} to run with
|
||||||
* the specified {@link SecurityContext}. Cannot be null.
|
* the specified {@link SecurityContext}. Cannot be null.
|
||||||
|
|||||||
+1
-1
@@ -47,7 +47,7 @@ public final class DelegatingSecurityContextRunnable implements Runnable {
|
|||||||
.getContextHolderStrategy();
|
.getContextHolderStrategy();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link SecurityContext} that the delegate {@link Runnable} will be run as.
|
* The {@link SecurityContext} that the delegate {@link Runnable} will be ran as.
|
||||||
*/
|
*/
|
||||||
private SecurityContext delegateSecurityContext;
|
private SecurityContext delegateSecurityContext;
|
||||||
|
|
||||||
|
|||||||
+1
-4
@@ -32,7 +32,6 @@ import org.springframework.util.Assert;
|
|||||||
* {@link java.util.ServiceLoader} mechanism when context-propagation is on the classpath.
|
* {@link java.util.ServiceLoader} mechanism when context-propagation is on the classpath.
|
||||||
*
|
*
|
||||||
* @author Steve Riesenberg
|
* @author Steve Riesenberg
|
||||||
* @author Tadaya Tsuyukubo
|
|
||||||
* @since 6.5
|
* @since 6.5
|
||||||
* @see io.micrometer.context.ContextRegistry
|
* @see io.micrometer.context.ContextRegistry
|
||||||
*/
|
*/
|
||||||
@@ -54,9 +53,7 @@ public final class SecurityContextHolderThreadLocalAccessor implements ThreadLoc
|
|||||||
@Override
|
@Override
|
||||||
public void setValue(SecurityContext securityContext) {
|
public void setValue(SecurityContext securityContext) {
|
||||||
Assert.notNull(securityContext, "securityContext cannot be null");
|
Assert.notNull(securityContext, "securityContext cannot be null");
|
||||||
SecurityContext newContext = SecurityContextHolder.createEmptyContext();
|
SecurityContextHolder.setContext(securityContext);
|
||||||
newContext.setAuthentication(securityContext.getAuthentication());
|
|
||||||
SecurityContextHolder.setContext(newContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import org.jspecify.annotations.Nullable;
|
|||||||
* building block for more sophisticated token-based solutions. For example,
|
* building block for more sophisticated token-based solutions. For example,
|
||||||
* authentication systems that depend on stateless session keys. These could, for
|
* authentication systems that depend on stateless session keys. These could, for
|
||||||
* instance, place the username inside the user-specified extended information associated
|
* instance, place the username inside the user-specified extended information associated
|
||||||
* with the key. It is important to recognise that we do not intend for this interface to
|
* with the key). It is important to recognise that we do not intend for this interface to
|
||||||
* be expanded to provide such capabilities directly.
|
* be expanded to provide such capabilities directly.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -323,7 +323,7 @@ public class User implements UserDetails, CredentialsContainer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the user to be added. At minimum the username, password, and authorities
|
* Builds the user to be added. At minimum the username, password, and authorities
|
||||||
* should be provided. The remaining attributes have reasonable defaults.
|
* should provided. The remaining attributes have reasonable defaults.
|
||||||
*/
|
*/
|
||||||
public static final class UserBuilder {
|
public static final class UserBuilder {
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -38,7 +38,7 @@ public interface UserDetailsService {
|
|||||||
* may possibly be case sensitive, or case insensitive depending on how the
|
* may possibly be case sensitive, or case insensitive depending on how the
|
||||||
* implementation instance is configured. In this case, the <code>UserDetails</code>
|
* implementation instance is configured. In this case, the <code>UserDetails</code>
|
||||||
* object that comes back may have a username that is of a different case than what
|
* object that comes back may have a username that is of a different case than what
|
||||||
* was actually requested.
|
* was actually requested..
|
||||||
* @param username the username identifying the user whose data is required.
|
* @param username the username identifying the user whose data is required.
|
||||||
* @return a fully populated user record (never <code>null</code>)
|
* @return a fully populated user record (never <code>null</code>)
|
||||||
* @throws UsernameNotFoundException if the user could not be found or the user has no
|
* @throws UsernameNotFoundException if the user could not be found or the user has no
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains simple user and authority group account provisioning interfaces together with
|
* Contains simple user and authority group account provisioning interfaces together with
|
||||||
* a JDBC-based implementation.
|
* a a JDBC-based implementation.
|
||||||
*/
|
*/
|
||||||
@NullMarked
|
@NullMarked
|
||||||
package org.springframework.security.provisioning;
|
package org.springframework.security.provisioning;
|
||||||
|
|||||||
+1
-3
@@ -259,9 +259,7 @@ public final class InetAddressMatchers {
|
|||||||
if (address.isLoopbackAddress() || address.isLinkLocalAddress() || address.isSiteLocalAddress()) {
|
if (address.isLoopbackAddress() || address.isLinkLocalAddress() || address.isSiteLocalAddress()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (address.isAnyLocalAddress()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
byte[] rawAddress = address.getAddress();
|
byte[] rawAddress = address.getAddress();
|
||||||
|
|
||||||
if (rawAddress.length == 16) {
|
if (rawAddress.length == 16) {
|
||||||
|
|||||||
+2
-49
@@ -16,18 +16,11 @@
|
|||||||
|
|
||||||
package org.springframework.security.core.context;
|
package org.springframework.security.core.context;
|
||||||
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.core.task.support.ContextPropagatingTaskDecorator;
|
|
||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
|
||||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
@@ -36,7 +29,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
|||||||
* Tests for {@link SecurityContextHolderThreadLocalAccessor}.
|
* Tests for {@link SecurityContextHolderThreadLocalAccessor}.
|
||||||
*
|
*
|
||||||
* @author Steve Riesenberg
|
* @author Steve Riesenberg
|
||||||
* @author Tadaya Tsuyukubo
|
|
||||||
*/
|
*/
|
||||||
public class SecurityContextHolderThreadLocalAccessorTests {
|
public class SecurityContextHolderThreadLocalAccessorTests {
|
||||||
|
|
||||||
@@ -73,11 +65,9 @@ public class SecurityContextHolderThreadLocalAccessorTests {
|
|||||||
@Test
|
@Test
|
||||||
public void setValueWhenSecurityContextThenSetsSecurityContextHolder() {
|
public void setValueWhenSecurityContextThenSetsSecurityContextHolder() {
|
||||||
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
|
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
|
||||||
Authentication authentication = new TestingAuthenticationToken("user", "password");
|
securityContext.setAuthentication(new TestingAuthenticationToken("user", "password"));
|
||||||
securityContext.setAuthentication(authentication);
|
|
||||||
this.threadLocalAccessor.setValue(securityContext);
|
this.threadLocalAccessor.setValue(securityContext);
|
||||||
assertThat(SecurityContextHolder.getContext()).isNotSameAs(securityContext);
|
assertThat(SecurityContextHolder.getContext()).isSameAs(securityContext);
|
||||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(authentication);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -100,41 +90,4 @@ public class SecurityContextHolderThreadLocalAccessorTests {
|
|||||||
assertThat(SecurityContextHolder.getContext()).isEqualTo(emptyContext);
|
assertThat(SecurityContextHolder.getContext()).isEqualTo(emptyContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void newSecurityContextInDifferentThread() throws Exception {
|
|
||||||
Authentication authA = new TestingAuthenticationToken("foo", "password");
|
|
||||||
Authentication authB = new TestingAuthenticationToken("bar", "password");
|
|
||||||
|
|
||||||
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
|
|
||||||
securityContext.setAuthentication(authA);
|
|
||||||
SecurityContextHolder.setContext(securityContext);
|
|
||||||
|
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
AtomicReference<SecurityContext> contextHolder = new AtomicReference<>();
|
|
||||||
AtomicReference<Authentication> authHolder = new AtomicReference<>();
|
|
||||||
Runnable runnable = () -> {
|
|
||||||
SecurityContext context = SecurityContextHolder.getContext();
|
|
||||||
contextHolder.set(context);
|
|
||||||
authHolder.set(context.getAuthentication());
|
|
||||||
context.setAuthentication(authB);
|
|
||||||
latch.countDown();
|
|
||||||
};
|
|
||||||
|
|
||||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
|
||||||
executor.setTaskDecorator(new ContextPropagatingTaskDecorator());
|
|
||||||
executor.afterPropertiesSet();
|
|
||||||
|
|
||||||
executor.execute(runnable);
|
|
||||||
|
|
||||||
boolean finished = latch.await(10, TimeUnit.SECONDS);
|
|
||||||
assertThat(finished).isTrue();
|
|
||||||
|
|
||||||
assertThat(contextHolder.get()).isNotSameAs(securityContext);
|
|
||||||
assertThat(authHolder.get()).isSameAs(authA);
|
|
||||||
|
|
||||||
SecurityContext current = SecurityContextHolder.getContext();
|
|
||||||
assertThat(current).isSameAs(securityContext);
|
|
||||||
assertThat(current.getAuthentication()).isSameAs(authA);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-5
@@ -276,17 +276,16 @@ class InetAddressMatchersTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ValueSource(strings = { "127.0.0.1", "127.0.0.255", "0.0.0.0" })
|
@ValueSource(strings = { "127.0.0.1", "127.0.0.255" })
|
||||||
void matchesWhenIpv4LoopbackThenReturnsTrue(String address) throws Exception {
|
void matchesWhenIpv4LoopbackThenReturnsTrue(String address) throws Exception {
|
||||||
InetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();
|
InetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();
|
||||||
assertThat(matcher.matches(InetAddress.getByName(address))).isTrue();
|
assertThat(matcher.matches(InetAddress.getByName(address))).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@Test
|
||||||
@ValueSource(strings = { "::1", "::" })
|
void matchesWhenIpv6LoopbackThenReturnsTrue() throws Exception {
|
||||||
void matchesWhenIpv6LoopbackThenReturnsTrue(String address) throws Exception {
|
|
||||||
InetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();
|
InetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();
|
||||||
assertThat(matcher.matches(InetAddress.getByName(address))).isTrue();
|
assertThat(matcher.matches(InetAddress.getByName("::1"))).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
|||||||
@@ -54,13 +54,138 @@ Therefore, a custom javadoc:org.springframework.security.web.server.authenticati
|
|||||||
One of the most common delivery strategies is a Magic Link, via e-mail, SMS, etc.
|
One of the most common delivery strategies is a Magic Link, via e-mail, SMS, etc.
|
||||||
In the following example, we are going to create a magic link and sent it to the user's email.
|
In the following example, we are going to create a magic link and sent it to the user's email.
|
||||||
|
|
||||||
include-code::./SecurityConfig[tag=config,indent=0]
|
.One-Time Token Login Configuration
|
||||||
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebFluxSecurity
|
||||||
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
|
||||||
|
http
|
||||||
|
// ...
|
||||||
|
.formLogin(Customizer.withDefaults())
|
||||||
|
.oneTimeTokenLogin(Customizer.withDefaults());
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
import org.springframework.mail.SimpleMailMessage;
|
||||||
|
import org.springframework.mail.javamail.JavaMailSender;
|
||||||
|
|
||||||
|
@Component <1>
|
||||||
|
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {
|
||||||
|
|
||||||
|
private final MailSender mailSender;
|
||||||
|
|
||||||
|
private final ServerOneTimeTokenGenerationSuccessHandler redirectHandler = new ServerRedirectOneTimeTokenGenerationSuccessHandler("/ott/sent");
|
||||||
|
|
||||||
|
// constructor omitted
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> handle(ServerWebExchange exchange, OneTimeToken oneTimeToken) {
|
||||||
|
return Mono.just(exchange.getRequest())
|
||||||
|
.map((request) ->
|
||||||
|
UriComponentsBuilder.fromUri(request.getURI())
|
||||||
|
.replacePath(request.getPath().contextPath().value())
|
||||||
|
.replaceQuery(null)
|
||||||
|
.fragment(null)
|
||||||
|
.path("/login/ott")
|
||||||
|
.queryParam("token", oneTimeToken.getTokenValue())
|
||||||
|
.toUriString() <2>
|
||||||
|
)
|
||||||
|
.flatMap((uri) -> this.mailSender.send(getUserEmail(oneTimeToken.getUsername()), <3>
|
||||||
|
"Use the following link to sign in into the application: " + magicLink)) <4>
|
||||||
|
.then(this.redirectHandler.handle(exchange, oneTimeToken)); <5>
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getUserEmail() {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
class PageController {
|
||||||
|
|
||||||
|
@GetMapping("/ott/sent")
|
||||||
|
String ottSent() {
|
||||||
|
return "my-template";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebFluxSecurity
|
||||||
|
class SecurityConfig {
|
||||||
|
|
||||||
|
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||||
|
return http {
|
||||||
|
authorizeExchange {
|
||||||
|
authorize(anyExchange, authenticated)
|
||||||
|
}
|
||||||
|
oneTimeTokenLogin { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component (1)
|
||||||
|
class MagicLinkOneTimeTokenGenerationSuccessHandler(val mailSender: MailSender): ServerOneTimeTokenGenerationSuccessHandler {
|
||||||
|
|
||||||
|
private val redirectStrategy: ServerRedirectStrategy = DefaultServerRedirectStrategy()
|
||||||
|
|
||||||
|
override fun handle(exchange: ServerWebExchange, oneTimeToken: OneTimeToken): Mono<Void> {
|
||||||
|
val builder = UriComponentsBuilder.fromUri(exchange.request.uri)
|
||||||
|
.replacePath(null)
|
||||||
|
.replaceQuery(null)
|
||||||
|
.fragment(null)
|
||||||
|
.path("/login/ott")
|
||||||
|
.queryParam("token", oneTimeToken.getTokenValue()) (2)
|
||||||
|
val magicLink = builder.toUriString()
|
||||||
|
builder.replacePath(null)
|
||||||
|
.replaceQuery(null)
|
||||||
|
.path("/ott/sent")
|
||||||
|
val redirectLink = builder.toUriString()
|
||||||
|
return this.mailSender.send(
|
||||||
|
getUserEmail(oneTimeToken.getUsername()), (3)
|
||||||
|
"Use the following link to sign in into the application: $magicLink") (4)
|
||||||
|
.then(this.redirectStrategy.sendRedirect(exchange, URI.create(redirectLink))) (5)
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getUserEmail() {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
class PageController {
|
||||||
|
|
||||||
|
@GetMapping("/ott/sent")
|
||||||
|
fun ottSent(): String {
|
||||||
|
return "my-template"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
<1> Make the `MagicLinkOneTimeTokenGenerationSuccessHandler` a Spring bean
|
<1> Make the `MagicLinkOneTimeTokenGenerationSuccessHandler` a Spring bean
|
||||||
<2> Create a login processing URL with the `token` as a query param
|
<2> Create a login processing URL with the `token` as a query param
|
||||||
<3> Retrieve the user's email based on the username
|
<3> Retrieve the user's email based on the username
|
||||||
<4> Use the `MailSender` API to send the email to the user with the magic link
|
<4> Use the `MailSender` API to send the email to the user with the magic link
|
||||||
<5> Use the `ServerOneTimeTokenGenerationSuccessHandler` to perform a redirect to your desired URL
|
<5> Use the `ServerRedirectStrategy` to perform a redirect to your desired URL
|
||||||
|
|
||||||
The email content will look similar to:
|
The email content will look similar to:
|
||||||
|
|
||||||
@@ -72,10 +197,65 @@ The default submit page will detect that the URL has the `token` query param and
|
|||||||
== Changing the One-Time Token Generate URL
|
== Changing the One-Time Token Generate URL
|
||||||
|
|
||||||
By default, the javadoc:org.springframework.security.web.server.authentication.ott.GenerateOneTimeTokenWebFilter[] listens to `POST /ott/generate` requests.
|
By default, the javadoc:org.springframework.security.web.server.authentication.ott.GenerateOneTimeTokenWebFilter[] listens to `POST /ott/generate` requests.
|
||||||
That URL can be changed by using the `tokenGeneratingUrl(String)` DSL method:
|
That URL can be changed by using the `generateTokenUrl(String)` DSL method:
|
||||||
|
|
||||||
include-code::./SecurityConfig[tag=config,indent=0]
|
.Changing the Generate URL
|
||||||
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebFluxSecurity
|
||||||
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
|
||||||
|
http
|
||||||
|
// ...
|
||||||
|
.formLogin(Customizer.withDefaults())
|
||||||
|
.oneTimeTokenLogin((ott) -> ott
|
||||||
|
.generateTokenUrl("/ott/my-generate-url")
|
||||||
|
);
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebFluxSecurity
|
||||||
|
class SecurityConfig {
|
||||||
|
|
||||||
|
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||||
|
return http {
|
||||||
|
// ...
|
||||||
|
formLogin { }
|
||||||
|
oneTimeTokenLogin {
|
||||||
|
generateTokenUrl = "/ott/my-generate-url"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class MagicLinkOneTimeTokenGenerationSuccessHandler(val mailSender: MailSender): ServerOneTimeTokenGenerationSuccessHandler {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
[[changing-submit-page-url]]
|
[[changing-submit-page-url]]
|
||||||
== Changing the Default Submit Page URL
|
== Changing the Default Submit Page URL
|
||||||
@@ -83,17 +263,151 @@ include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent
|
|||||||
The default One-Time Token submit page is generated by the javadoc:org.springframework.security.web.server.ui.OneTimeTokenSubmitPageGeneratingWebFilter[] and listens to `GET /login/ott`.
|
The default One-Time Token submit page is generated by the javadoc:org.springframework.security.web.server.ui.OneTimeTokenSubmitPageGeneratingWebFilter[] and listens to `GET /login/ott`.
|
||||||
The URL can also be changed, like so:
|
The URL can also be changed, like so:
|
||||||
|
|
||||||
include-code::./SecurityConfig[tag=config,indent=0]
|
.Configuring the Default Submit Page URL
|
||||||
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebFluxSecurity
|
||||||
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
|
||||||
|
http
|
||||||
|
// ...
|
||||||
|
.formLogin(Customizer.withDefaults())
|
||||||
|
.oneTimeTokenLogin((ott) -> ott
|
||||||
|
.submitPageUrl("/ott/submit")
|
||||||
|
);
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebFluxSecurity
|
||||||
|
class SecurityConfig {
|
||||||
|
|
||||||
|
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||||
|
return http {
|
||||||
|
// ...
|
||||||
|
formLogin { }
|
||||||
|
oneTimeTokenLogin {
|
||||||
|
submitPageUrl = "/ott/submit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class MagicLinkOneTimeTokenGenerationSuccessHandler(val mailSender: MailSender): ServerOneTimeTokenGenerationSuccessHandler {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
[[disabling-default-submit-page]]
|
[[disabling-default-submit-page]]
|
||||||
== Disabling the Default Submit Page
|
== Disabling the Default Submit Page
|
||||||
|
|
||||||
If you want to use your own One-Time Token submit page, you can disable the default page and then provide your own endpoint.
|
If you want to use your own One-Time Token submit page, you can disable the default page and then provide your own endpoint.
|
||||||
|
|
||||||
include-code::./SecurityConfig[tag=config,indent=0]
|
.Disabling the Default Submit Page
|
||||||
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
|
[tabs]
|
||||||
include-code::./MyController[tag=snippet,indent=0]
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebFluxSecurity
|
||||||
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
|
||||||
|
http
|
||||||
|
.authorizeExchange((authorize) -> authorize
|
||||||
|
.pathMatchers("/my-ott-submit").permitAll()
|
||||||
|
.anyExchange().authenticated()
|
||||||
|
)
|
||||||
|
.formLogin(Customizer.withDefaults())
|
||||||
|
.oneTimeTokenLogin((ott) -> ott
|
||||||
|
.showDefaultSubmitPage(false)
|
||||||
|
);
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
public class MyController {
|
||||||
|
|
||||||
|
@GetMapping("/my-ott-submit")
|
||||||
|
public String ottSubmitPage() {
|
||||||
|
return "my-ott-submit";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebFluxSecurity
|
||||||
|
class SecurityConfig {
|
||||||
|
|
||||||
|
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||||
|
return http {
|
||||||
|
authorizeExchange {
|
||||||
|
authorize(pathMatchers("/my-ott-submit"), permitAll)
|
||||||
|
authorize(anyExchange, authenticated)
|
||||||
|
}
|
||||||
|
.formLogin { }
|
||||||
|
oneTimeTokenLogin {
|
||||||
|
showDefaultSubmitPage = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
class MyController {
|
||||||
|
|
||||||
|
@GetMapping("/my-ott-submit")
|
||||||
|
fun ottSubmitPage(): String {
|
||||||
|
return "my-ott-submit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class MagicLinkOneTimeTokenGenerationSuccessHandler(val mailSender: MailSender): ServerOneTimeTokenGenerationSuccessHandler {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
[[customize-generate-consume-token]]
|
[[customize-generate-consume-token]]
|
||||||
== Customize How to Generate and Consume One-Time Tokens
|
== Customize How to Generate and Consume One-Time Tokens
|
||||||
@@ -111,21 +425,160 @@ Some of the most common reasons to customize the `ReactiveOneTimeTokenService` a
|
|||||||
There are two options to customize the `ReactiveOneTimeTokenService`.
|
There are two options to customize the `ReactiveOneTimeTokenService`.
|
||||||
One option is to provide it as a bean, so it can be automatically be picked-up by the `oneTimeTokenLogin()` DSL:
|
One option is to provide it as a bean, so it can be automatically be picked-up by the `oneTimeTokenLogin()` DSL:
|
||||||
|
|
||||||
include-code::./OneTimeTokenServiceBeanSecurityConfig[tag=config,indent=0]
|
.Passing the ReactiveOneTimeTokenService as a Bean
|
||||||
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebFluxSecurity
|
||||||
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
|
||||||
|
http
|
||||||
|
// ...
|
||||||
|
.formLogin(Customizer.withDefaults())
|
||||||
|
.oneTimeTokenLogin(Customizer.withDefaults());
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ReactiveOneTimeTokenService oneTimeTokenService() {
|
||||||
|
return new MyCustomReactiveOneTimeTokenService();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebFluxSecurity
|
||||||
|
class SecurityConfig {
|
||||||
|
|
||||||
|
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||||
|
return http {
|
||||||
|
//..
|
||||||
|
.formLogin { }
|
||||||
|
oneTimeTokenLogin { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun oneTimeTokenService():ReactiveOneTimeTokenService {
|
||||||
|
return MyCustomReactiveOneTimeTokenService();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class MagicLinkOneTimeTokenGenerationSuccessHandler(val mailSender: MailSender): ServerOneTimeTokenGenerationSuccessHandler {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
The second option is to pass the `ReactiveOneTimeTokenService` instance to the DSL, which is useful if there are multiple ``SecurityWebFilterChain``s and a different ``ReactiveOneTimeTokenService``s is needed for each of them.
|
The second option is to pass the `ReactiveOneTimeTokenService` instance to the DSL, which is useful if there are multiple ``SecurityWebFilterChain``s and a different ``ReactiveOneTimeTokenService``s is needed for each of them.
|
||||||
|
|
||||||
include-code::./OneTimeTokenServiceDSLSecurityConfig[tag=config,indent=0]
|
.Passing the ReactiveOneTimeTokenService using the DSL
|
||||||
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebFluxSecurity
|
||||||
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
|
||||||
|
http
|
||||||
|
// ...
|
||||||
|
.formLogin(Customizer.withDefaults())
|
||||||
|
.oneTimeTokenLogin((ott) -> ott
|
||||||
|
.oneTimeTokenService(new MyCustomReactiveOneTimeTokenService())
|
||||||
|
);
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebFluxSecurity
|
||||||
|
class SecurityConfig {
|
||||||
|
|
||||||
|
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||||
|
return http {
|
||||||
|
//..
|
||||||
|
.formLogin { }
|
||||||
|
oneTimeTokenLogin {
|
||||||
|
oneTimeTokenService = MyCustomReactiveOneTimeTokenService()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class MagicLinkOneTimeTokenGenerationSuccessHandler(val mailSender: MailSender): ServerOneTimeTokenGenerationSuccessHandler {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
[[customize-generate-token-request]]
|
[[customize-generate-token-request]]
|
||||||
== Customize GenerateOneTimeTokenRequest Instance
|
== Customize GenerateOneTimeTokenRequest Instance
|
||||||
There are a number of reasons that you may want to adjust an GenerateOneTimeTokenRequest. For example, you may want expiresIn to be set to 10 mins, which Spring Security sets to 5 mins by default.
|
There are a number of reasons that you may want to adjust an GenerateOneTimeTokenRequest. For example, you may want expiresIn to be set to 10 mins, which Spring Security sets to 5 mins by default.
|
||||||
|
|
||||||
You can customize elements of GenerateOneTimeTokenRequest by publishing an ServerGenerateOneTimeTokenRequestResolver as a `@Bean`, like so:
|
You can customize elements of GenerateOneTimeTokenRequest by publishing an ServerGenerateOneTimeTokenRequestResolver as a @Bean, like so:
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
@Bean
|
||||||
|
ServerGenerateOneTimeTokenRequestResolver generateOneTimeTokenRequestResolver() {
|
||||||
|
DefaultServerGenerateOneTimeTokenRequestResolver resolver = new DefaultServerGenerateOneTimeTokenRequestResolver();
|
||||||
|
resolver.setExpiresIn(Duration.ofSeconds(600));
|
||||||
|
return resolver;
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
include-code::./SecurityConfig[tag=config,indent=0]
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
|
----
|
||||||
|
@Bean
|
||||||
|
fun generateOneTimeTokenRequestResolver() : ServerGenerateOneTimeTokenRequestResolver {
|
||||||
|
return DefaultServerGenerateOneTimeTokenRequestResolver().apply {
|
||||||
|
this.setExpiresIn(Duration.ofMinutes(10))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
[[validating-account-status]]
|
[[validating-account-status]]
|
||||||
== Validating Account Status
|
== Validating Account Status
|
||||||
|
|||||||
@@ -54,12 +54,133 @@ Therefore, a custom javadoc:org.springframework.security.web.authentication.ott.
|
|||||||
One of the most common delivery strategies is a Magic Link, via e-mail, SMS, etc.
|
One of the most common delivery strategies is a Magic Link, via e-mail, SMS, etc.
|
||||||
In the following example, we are going to create a magic link and sent it to the user's email.
|
In the following example, we are going to create a magic link and sent it to the user's email.
|
||||||
|
|
||||||
include-code::./SecurityConfig[tag=config,indent=0]
|
.One-Time Token Login Configuration
|
||||||
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain filterChain(HttpSecurity http) {
|
||||||
|
http
|
||||||
|
// ...
|
||||||
|
.formLogin(Customizer.withDefaults())
|
||||||
|
.oneTimeTokenLogin(Customizer.withDefaults());
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
import org.springframework.mail.SimpleMailMessage;
|
||||||
|
import org.springframework.mail.javamail.JavaMailSender;
|
||||||
|
|
||||||
|
@Component <1>
|
||||||
|
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {
|
||||||
|
|
||||||
|
private final MailSender mailSender;
|
||||||
|
|
||||||
|
private final OneTimeTokenGenerationSuccessHandler redirectHandler = new RedirectOneTimeTokenGenerationSuccessHandler("/ott/sent");
|
||||||
|
|
||||||
|
// constructor omitted
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(HttpServletRequest request, HttpServletResponse response, OneTimeToken oneTimeToken) throws IOException, ServletException {
|
||||||
|
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(request.getRequestURL().toString())
|
||||||
|
.replacePath(request.getContextPath())
|
||||||
|
.replaceQuery(null)
|
||||||
|
.fragment(null)
|
||||||
|
.path("/login/ott")
|
||||||
|
.queryParam("token", oneTimeToken.getTokenValue()); <2>
|
||||||
|
String magicLink = builder.toUriString();
|
||||||
|
String email = getUserEmail(oneTimeToken.getUsername()); <3>
|
||||||
|
this.mailSender.send(email, "Your Spring Security One Time Token", "Use the following link to sign in into the application: " + magicLink); <4>
|
||||||
|
this.redirectHandler.handle(request, response, oneTimeToken); <5>
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getUserEmail() {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
class PageController {
|
||||||
|
|
||||||
|
@GetMapping("/ott/sent")
|
||||||
|
String ottSent() {
|
||||||
|
return "my-template";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
http{
|
||||||
|
formLogin {}
|
||||||
|
oneTimeTokenLogin { }
|
||||||
|
}
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
import org.springframework.mail.SimpleMailMessage;
|
||||||
|
import org.springframework.mail.javamail.JavaMailSender;
|
||||||
|
|
||||||
|
@Component (1)
|
||||||
|
class MagicLinkOneTimeTokenGenerationSuccessHandler(
|
||||||
|
private val mailSender: MailSender,
|
||||||
|
private val redirectHandler: OneTimeTokenGenerationSuccessHandler = RedirectOneTimeTokenGenerationSuccessHandler("/ott/sent")
|
||||||
|
) : OneTimeTokenGenerationSuccessHandler {
|
||||||
|
|
||||||
|
override fun handle(request: HttpServletRequest, response: HttpServletResponse, oneTimeToken: OneTimeToken) {
|
||||||
|
val builder = UriComponentsBuilder.fromUriString(request.getRequestURL().toString())
|
||||||
|
.replacePath(request.contextPath)
|
||||||
|
.replaceQuery(null)
|
||||||
|
.fragment(null)
|
||||||
|
.path("/login/ott")
|
||||||
|
.queryParam("token", oneTimeToken.getTokenValue()) (2)
|
||||||
|
val magicLink = builder.toUriString()
|
||||||
|
val email = getUserEmail(oneTimeToken.getUsername()) (3)
|
||||||
|
this.mailSender.send(email, "Your Spring Security One Time Token", "Use the following link to sign in into the application: $magicLink")(4)
|
||||||
|
this.redirectHandler.handle(request, response, oneTimeToken) (5)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getUserEmail(): String {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
class PageController {
|
||||||
|
|
||||||
|
@GetMapping("/ott/sent")
|
||||||
|
fun ottSent(): String {
|
||||||
|
return "my-template"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
<1> Make the `MagicLinkOneTimeTokenGenerationSuccessHandler` a Spring bean
|
<1> Make the `MagicLinkOneTimeTokenGenerationSuccessHandler` a Spring bean
|
||||||
<2> Create a login processing URL with the `token` as a query param
|
<2> Create a login processing URL with the `token` as a query param
|
||||||
<3> Retrieve the user's email based on the username
|
<3> Retrieve the user's email based on the username
|
||||||
<4> Use the `MailSender` API to send the email to the user with the magic link
|
<4> Use the `JavaMailSender` API to send the email to the user with the magic link
|
||||||
<5> Use the `RedirectOneTimeTokenGenerationSuccessHandler` to perform a redirect to your desired URL
|
<5> Use the `RedirectOneTimeTokenGenerationSuccessHandler` to perform a redirect to your desired URL
|
||||||
|
|
||||||
The email content will look similar to:
|
The email content will look similar to:
|
||||||
@@ -72,15 +193,70 @@ The default submit page will detect that the URL has the `token` query param and
|
|||||||
== Changing the One-Time Token Generate URL
|
== Changing the One-Time Token Generate URL
|
||||||
|
|
||||||
By default, the javadoc:org.springframework.security.web.authentication.ott.GenerateOneTimeTokenFilter[] listens to `POST /ott/generate` requests.
|
By default, the javadoc:org.springframework.security.web.authentication.ott.GenerateOneTimeTokenFilter[] listens to `POST /ott/generate` requests.
|
||||||
That URL can be changed by using the `tokenGeneratingUrl(String)` DSL method:
|
That URL can be changed by using the `generateTokenUrl(String)` DSL method:
|
||||||
|
|
||||||
include-code::./SecurityConfig[tag=config,indent=0]
|
.Changing the Generate URL
|
||||||
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain filterChain(HttpSecurity http) {
|
||||||
|
http
|
||||||
|
// ...
|
||||||
|
.formLogin(Customizer.withDefaults())
|
||||||
|
.oneTimeTokenLogin((ott) -> ott
|
||||||
|
.tokenGeneratingUrl("/ott/my-generate-url")
|
||||||
|
);
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
http {
|
||||||
|
//...
|
||||||
|
formLogin { }
|
||||||
|
oneTimeTokenLogin {
|
||||||
|
tokenGeneratingUrl = "/ott/my-generate-url"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSuccessHandler {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
[NOTE]
|
[NOTE]
|
||||||
====
|
====
|
||||||
The URI passed to `tokenGeneratingUrl` is matched literally.
|
The URI passed to `generateTokenUrl` is matched literally.
|
||||||
If you want it to live under your application servlet's base path, include that prefix explicitly — for example, `tokenGeneratingUrl("/api/ott/generate")`.
|
If you want it to live under your application servlet's base path, include that prefix explicitly — for example, `generateTokenUrl("/api/ott/generate")`.
|
||||||
The same applies to `loginProcessingUrl` and `tokenGeneratingUrl` elsewhere on this page.
|
The same applies to `loginProcessingUrl` and `tokenGeneratingUrl` elsewhere on this page.
|
||||||
====
|
====
|
||||||
|
|
||||||
@@ -90,17 +266,151 @@ The same applies to `loginProcessingUrl` and `tokenGeneratingUrl` elsewhere on t
|
|||||||
The default One-Time Token submit page is generated by the javadoc:org.springframework.security.web.authentication.ui.DefaultOneTimeTokenSubmitPageGeneratingFilter[] and listens to `GET /login/ott`.
|
The default One-Time Token submit page is generated by the javadoc:org.springframework.security.web.authentication.ui.DefaultOneTimeTokenSubmitPageGeneratingFilter[] and listens to `GET /login/ott`.
|
||||||
The URL can also be changed, like so:
|
The URL can also be changed, like so:
|
||||||
|
|
||||||
include-code::./SecurityConfig[tag=config,indent=0]
|
.Configuring the Default Submit Page URL
|
||||||
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain filterChain(HttpSecurity http) {
|
||||||
|
http
|
||||||
|
// ...
|
||||||
|
.formLogin(Customizer.withDefaults())
|
||||||
|
.oneTimeTokenLogin((ott) -> ott
|
||||||
|
.defaultSubmitPageUrl("/ott/submit")
|
||||||
|
);
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MagicLinkGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
http {
|
||||||
|
//...
|
||||||
|
formLogin { }
|
||||||
|
oneTimeTokenLogin {
|
||||||
|
defaultSubmitPageUrl = "/ott/submit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSuccessHandler {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
[[disabling-default-submit-page]]
|
[[disabling-default-submit-page]]
|
||||||
== Disabling the Default Submit Page
|
== Disabling the Default Submit Page
|
||||||
|
|
||||||
If you want to use your own One-Time Token submit page, you can disable the default page and then provide your own endpoint.
|
If you want to use your own One-Time Token submit page, you can disable the default page and then provide your own endpoint.
|
||||||
|
|
||||||
include-code::./SecurityConfig[tag=config,indent=0]
|
.Disabling the Default Submit Page
|
||||||
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
|
[tabs]
|
||||||
include-code::./MyController[tag=snippet,indent=0]
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain filterChain(HttpSecurity http) {
|
||||||
|
http
|
||||||
|
.authorizeHttpRequests((authorize) -> authorize
|
||||||
|
.requestMatchers("/my-ott-submit").permitAll()
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
)
|
||||||
|
.formLogin(Customizer.withDefaults())
|
||||||
|
.oneTimeTokenLogin((ott) -> ott
|
||||||
|
.showDefaultSubmitPage(false)
|
||||||
|
);
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
public class MyController {
|
||||||
|
|
||||||
|
@GetMapping("/my-ott-submit")
|
||||||
|
public String ottSubmitPage() {
|
||||||
|
return "my-ott-submit";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class OneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
http {
|
||||||
|
authorizeHttpRequests {
|
||||||
|
authorize("/my-ott-submit", authenticated)
|
||||||
|
authorize(anyRequest, authenticated)
|
||||||
|
}
|
||||||
|
formLogin { }
|
||||||
|
oneTimeTokenLogin {
|
||||||
|
showDefaultSubmitPage = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
class MyController {
|
||||||
|
|
||||||
|
@GetMapping("/my-ott-submit")
|
||||||
|
fun ottSubmitPage(): String {
|
||||||
|
return "my-ott-submit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSuccessHandler {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
[[customize-generate-consume-token]]
|
[[customize-generate-consume-token]]
|
||||||
== Customize How to Generate and Consume One-Time Tokens
|
== Customize How to Generate and Consume One-Time Tokens
|
||||||
@@ -119,21 +429,164 @@ Some of the most common reasons to customize the `OneTimeTokenService` are, but
|
|||||||
There are two options to customize the `OneTimeTokenService`.
|
There are two options to customize the `OneTimeTokenService`.
|
||||||
One option is to provide it as a bean, so it can be automatically be picked-up by the `oneTimeTokenLogin()` DSL:
|
One option is to provide it as a bean, so it can be automatically be picked-up by the `oneTimeTokenLogin()` DSL:
|
||||||
|
|
||||||
include-code::./OneTimeTokenServiceBeanSecurityConfig[tag=config,indent=0]
|
.Passing the OneTimeTokenService as a Bean
|
||||||
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain filterChain(HttpSecurity http) {
|
||||||
|
http
|
||||||
|
// ...
|
||||||
|
.formLogin(Customizer.withDefaults())
|
||||||
|
.oneTimeTokenLogin(Customizer.withDefaults());
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public OneTimeTokenService oneTimeTokenService() {
|
||||||
|
return new MyCustomOneTimeTokenService();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
http {
|
||||||
|
//...
|
||||||
|
formLogin { }
|
||||||
|
oneTimeTokenLogin { }
|
||||||
|
}
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun oneTimeTokenService(): OneTimeTokenService {
|
||||||
|
return MyCustomOneTimeTokenService()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSuccessHandler {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
The second option is to pass the `OneTimeTokenService` instance to the DSL, which is useful if there are multiple `SecurityFilterChain` and a different `OneTimeTokenService` is needed for each of them.
|
The second option is to pass the `OneTimeTokenService` instance to the DSL, which is useful if there are multiple `SecurityFilterChain` and a different `OneTimeTokenService` is needed for each of them.
|
||||||
|
|
||||||
include-code::./OneTimeTokenServiceDSLSecurityConfig[tag=config,indent=0]
|
.Passing the OneTimeTokenService using the DSL
|
||||||
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain filterChain(HttpSecurity http) {
|
||||||
|
http
|
||||||
|
// ...
|
||||||
|
.formLogin(Customizer.withDefaults())
|
||||||
|
.oneTimeTokenLogin((ott) -> ott
|
||||||
|
.oneTimeTokenService(new MyCustomOneTimeTokenService())
|
||||||
|
);
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
http {
|
||||||
|
//...
|
||||||
|
formLogin { }
|
||||||
|
oneTimeTokenLogin {
|
||||||
|
oneTimeTokenService = MyCustomOneTimeTokenService()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSuccessHandler {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
[[customize-generate-token-request]]
|
[[customize-generate-token-request]]
|
||||||
== Customize GenerateOneTimeTokenRequest Instance
|
== Customize GenerateOneTimeTokenRequest Instance
|
||||||
There are a number of reasons that you may want to adjust an GenerateOneTimeTokenRequest. For example, you may want expiresIn to be set to 10 mins, which Spring Security sets to 5 mins by default.
|
There are a number of reasons that you may want to adjust an GenerateOneTimeTokenRequest. For example, you may want expiresIn to be set to 10 mins, which Spring Security sets to 5 mins by default.
|
||||||
|
|
||||||
You can customize elements of GenerateOneTimeTokenRequest by publishing an GenerateOneTimeTokenRequestResolver as a `@Bean`, like so:
|
You can customize elements of GenerateOneTimeTokenRequest by publishing an GenerateOneTimeTokenRequestResolver as a @Bean, like so:
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
@Bean
|
||||||
|
GenerateOneTimeTokenRequestResolver generateOneTimeTokenRequestResolver() {
|
||||||
|
DefaultGenerateOneTimeTokenRequestResolver delegate = new DefaultGenerateOneTimeTokenRequestResolver();
|
||||||
|
return (request) -> {
|
||||||
|
GenerateOneTimeTokenRequest generate = delegate.resolve(request);
|
||||||
|
return new GenerateOneTimeTokenRequest(generate.getUsername(), Duration.ofSeconds(600));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
include-code::./SecurityConfig[tag=config,indent=0]
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
|
----
|
||||||
|
@Bean
|
||||||
|
fun generateRequestResolver() : GenerateOneTimeTokenRequestResolver {
|
||||||
|
return DefaultGenerateOneTimeTokenRequestResolver().apply {
|
||||||
|
this.setExpiresIn(Duration.ofMinutes(10))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
[[validating-account-status]]
|
[[validating-account-status]]
|
||||||
== Validating Account Status
|
== Validating Account Status
|
||||||
|
|||||||
@@ -8,8 +8,3 @@
|
|||||||
== Web
|
== Web
|
||||||
|
|
||||||
* Since Spring Framework's `HttpMethod#valueOf` now normalizes casing, `StrictServerWebExchangeFirewall` no longer detects a non-canonical-case HTTP method (for example, `get` instead of `GET`) as a distinct value; such requests are processed as the canonical method instead of being rejected. Applications with a customized `ServerExchangeRejectedHandler` should be aware it is no longer invoked for this case.
|
* Since Spring Framework's `HttpMethod#valueOf` now normalizes casing, `StrictServerWebExchangeFirewall` no longer detects a non-canonical-case HTTP method (for example, `get` instead of `GET`) as a distinct value; such requests are processed as the canonical method instead of being rejected. Applications with a customized `ServerExchangeRejectedHandler` should be aware it is no longer invoked for this case.
|
||||||
|
|
||||||
== OAuth 2.0
|
|
||||||
|
|
||||||
* https://github.com/spring-projects/spring-security/pull/18895[gh-18895] - Add `authenticationSuccessHandler` to the Reactive Resource Server DSL
|
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"antora": "3.2.0-rc.3",
|
"antora": "3.2.0-rc.2",
|
||||||
"@antora/atlas-extension": "1.0.0-alpha.5",
|
"@antora/atlas-extension": "1.0.0-alpha.5",
|
||||||
"@antora/collector-extension": "1.0.3",
|
"@antora/collector-extension": "1.0.3",
|
||||||
"@asciidoctor/tabs": "1.0.0-beta.6",
|
"@asciidoctor/tabs": "1.0.0-beta.6",
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ dependencies {
|
|||||||
testImplementation 'org.springframework:spring-websocket'
|
testImplementation 'org.springframework:spring-websocket'
|
||||||
|
|
||||||
testImplementation 'org.springframework:spring-webmvc'
|
testImplementation 'org.springframework:spring-webmvc'
|
||||||
testImplementation 'org.springframework:spring-context-support'
|
|
||||||
testImplementation 'jakarta.servlet:jakarta.servlet-api'
|
testImplementation 'jakarta.servlet:jakarta.servlet-api'
|
||||||
testImplementation 'io.mockk:mockk'
|
testImplementation 'io.mockk:mockk'
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-api"
|
testImplementation "org.junit.jupiter:junit-jupiter-api"
|
||||||
|
|||||||
-35
@@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.reactive.authentication.changinggenerateurl;
|
|
||||||
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeToken;
|
|
||||||
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.server.ServerWebExchange;
|
|
||||||
import reactor.core.publisher.Mono;
|
|
||||||
|
|
||||||
// tag::snippet[]
|
|
||||||
@Component
|
|
||||||
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<Void> handle(ServerWebExchange exchange, OneTimeToken oneTimeToken) {
|
|
||||||
/**/ return Mono.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::snippet[]
|
|
||||||
-43
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.reactive.authentication.changinggenerateurl;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.security.config.Customizer;
|
|
||||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
|
||||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
|
||||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Configuration
|
|
||||||
@EnableWebFluxSecurity
|
|
||||||
public class SecurityConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
|
|
||||||
http
|
|
||||||
// ...
|
|
||||||
.formLogin(Customizer.withDefaults())
|
|
||||||
.oneTimeTokenLogin((ott) -> ott
|
|
||||||
.tokenGeneratingUrl("/ott/my-generate-url")
|
|
||||||
);
|
|
||||||
return http.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
-35
@@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.reactive.authentication.changingsubmitpageurl;
|
|
||||||
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeToken;
|
|
||||||
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.server.ServerWebExchange;
|
|
||||||
import reactor.core.publisher.Mono;
|
|
||||||
|
|
||||||
// tag::snippet[]
|
|
||||||
@Component
|
|
||||||
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<Void> handle(ServerWebExchange exchange, OneTimeToken oneTimeToken) {
|
|
||||||
/**/ return Mono.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::snippet[]
|
|
||||||
-43
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.reactive.authentication.changingsubmitpageurl;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.security.config.Customizer;
|
|
||||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
|
||||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
|
||||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Configuration
|
|
||||||
@EnableWebFluxSecurity
|
|
||||||
public class SecurityConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
|
|
||||||
http
|
|
||||||
// ...
|
|
||||||
.formLogin(Customizer.withDefaults())
|
|
||||||
.oneTimeTokenLogin((ott) -> ott
|
|
||||||
.defaultSubmitPageUrl("/ott/submit")
|
|
||||||
);
|
|
||||||
return http.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
-35
@@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.reactive.authentication.customizegenerateconsumetoken;
|
|
||||||
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeToken;
|
|
||||||
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.server.ServerWebExchange;
|
|
||||||
import reactor.core.publisher.Mono;
|
|
||||||
|
|
||||||
// tag::snippet[]
|
|
||||||
@Component
|
|
||||||
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<Void> handle(ServerWebExchange exchange, OneTimeToken oneTimeToken) {
|
|
||||||
/**/ return Mono.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::snippet[]
|
|
||||||
-38
@@ -1,38 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.reactive.authentication.customizegenerateconsumetoken;
|
|
||||||
|
|
||||||
import org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeToken;
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken;
|
|
||||||
import org.springframework.security.authentication.ott.reactive.ReactiveOneTimeTokenService;
|
|
||||||
import reactor.core.publisher.Mono;
|
|
||||||
|
|
||||||
class MyCustomReactiveOneTimeTokenService implements ReactiveOneTimeTokenService {
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<OneTimeToken> generate(GenerateOneTimeTokenRequest request) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<OneTimeToken> consume(OneTimeTokenAuthenticationToken authenticationToken) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-48
@@ -1,48 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.reactive.authentication.customizegenerateconsumetoken;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.security.authentication.ott.reactive.ReactiveOneTimeTokenService;
|
|
||||||
import org.springframework.security.config.Customizer;
|
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
|
||||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
|
||||||
import org.springframework.security.kt.docs.reactive.authentication.customizegenerateconsumetoken.MyCustomReactiveOneTimeTokenService;
|
|
||||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Configuration
|
|
||||||
@EnableWebSecurity
|
|
||||||
public class OneTimeTokenServiceBeanSecurityConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
|
|
||||||
http
|
|
||||||
// ...
|
|
||||||
.formLogin(Customizer.withDefaults())
|
|
||||||
.oneTimeTokenLogin(Customizer.withDefaults());
|
|
||||||
return http.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public ReactiveOneTimeTokenService oneTimeTokenService() {
|
|
||||||
return new MyCustomReactiveOneTimeTokenService();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
-43
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.reactive.authentication.customizegenerateconsumetoken;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.security.config.Customizer;
|
|
||||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
|
||||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
|
||||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Configuration
|
|
||||||
@EnableWebFluxSecurity
|
|
||||||
public class OneTimeTokenServiceDSLSecurityConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
|
|
||||||
http
|
|
||||||
// ...
|
|
||||||
.formLogin(Customizer.withDefaults())
|
|
||||||
.oneTimeTokenLogin((ott) -> ott
|
|
||||||
.tokenService(new MyCustomReactiveOneTimeTokenService())
|
|
||||||
);
|
|
||||||
return http.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
-35
@@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.reactive.authentication.customizegeneratetokenrequest;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.security.web.server.authentication.ott.DefaultServerGenerateOneTimeTokenRequestResolver;
|
|
||||||
import org.springframework.security.web.server.authentication.ott.ServerGenerateOneTimeTokenRequestResolver;
|
|
||||||
|
|
||||||
public class SecurityConfig {
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Bean
|
|
||||||
ServerGenerateOneTimeTokenRequestResolver generateOneTimeTokenRequestResolver() {
|
|
||||||
DefaultServerGenerateOneTimeTokenRequestResolver resolver = new DefaultServerGenerateOneTimeTokenRequestResolver();
|
|
||||||
resolver.setExpiresIn(Duration.ofMinutes(10));
|
|
||||||
return resolver;
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
|
|
||||||
}
|
|
||||||
-35
@@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.reactive.authentication.disablingdefaultsubmitpage;
|
|
||||||
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeToken;
|
|
||||||
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.server.ServerWebExchange;
|
|
||||||
import reactor.core.publisher.Mono;
|
|
||||||
|
|
||||||
// tag::snippet[]
|
|
||||||
@Component
|
|
||||||
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<Void> handle(ServerWebExchange exchange, OneTimeToken oneTimeToken) {
|
|
||||||
/**/ return Mono.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::snippet[]
|
|
||||||
-32
@@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.reactive.authentication.disablingdefaultsubmitpage;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
|
|
||||||
// tag::snippet[]
|
|
||||||
@Controller
|
|
||||||
public class MyController {
|
|
||||||
|
|
||||||
@GetMapping("/my-ott-submit")
|
|
||||||
public String ottSubmitPage() {
|
|
||||||
return "my-ott-submit";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::snippet[]
|
|
||||||
-46
@@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.reactive.authentication.disablingdefaultsubmitpage;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.security.config.Customizer;
|
|
||||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
|
||||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
|
||||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Configuration
|
|
||||||
@EnableWebFluxSecurity
|
|
||||||
public class SecurityConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
|
|
||||||
http
|
|
||||||
.authorizeExchange((authorize) -> authorize
|
|
||||||
.pathMatchers("/my-ott-submit").permitAll()
|
|
||||||
.anyExchange().authenticated()
|
|
||||||
)
|
|
||||||
.formLogin(Customizer.withDefaults())
|
|
||||||
.oneTimeTokenLogin((ott) -> ott
|
|
||||||
.showDefaultSubmitPage(false)
|
|
||||||
);
|
|
||||||
return http.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
-84
@@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.reactive.authentication.sendingtokentouser;
|
|
||||||
|
|
||||||
import org.springframework.mail.MailSender;
|
|
||||||
import org.springframework.mail.SimpleMailMessage;
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeToken;
|
|
||||||
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler;
|
|
||||||
import org.springframework.security.web.server.authentication.ott.ServerRedirectOneTimeTokenGenerationSuccessHandler;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.server.ServerWebExchange;
|
|
||||||
import org.springframework.web.util.UriComponentsBuilder;
|
|
||||||
import reactor.core.publisher.Mono;
|
|
||||||
|
|
||||||
// tag::snippet[]
|
|
||||||
@Component // <1>
|
|
||||||
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {
|
|
||||||
|
|
||||||
private final MailSender mailSender;
|
|
||||||
|
|
||||||
private final ServerOneTimeTokenGenerationSuccessHandler redirectHandler = new ServerRedirectOneTimeTokenGenerationSuccessHandler("/ott/sent");
|
|
||||||
|
|
||||||
public MagicLinkOneTimeTokenGenerationSuccessHandler(MailSender mailSender) {
|
|
||||||
this.mailSender = mailSender;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<Void> handle(ServerWebExchange exchange, OneTimeToken oneTimeToken) {
|
|
||||||
|
|
||||||
return Mono.just(exchange.getRequest())
|
|
||||||
.map((request) ->
|
|
||||||
UriComponentsBuilder.fromUri(request.getURI())
|
|
||||||
.replacePath(request.getPath().contextPath().value())
|
|
||||||
.replaceQuery(null)
|
|
||||||
.fragment(null)
|
|
||||||
.path("/login/ott")
|
|
||||||
.queryParam("token", oneTimeToken.getTokenValue())
|
|
||||||
.toUriString() // <2>
|
|
||||||
)
|
|
||||||
.flatMap((uri) -> {
|
|
||||||
|
|
||||||
String email = getUserEmail(oneTimeToken.getUsername()); // <3>
|
|
||||||
SimpleMailMessage message = new SimpleMailMessage();
|
|
||||||
message.setTo(email);
|
|
||||||
message.setSubject("Your Spring Security One Time Token");
|
|
||||||
message.setText("Use the following link to sign in into the application: " + uri);
|
|
||||||
this.mailSender.send(message); // <4>
|
|
||||||
return Mono.empty();
|
|
||||||
})
|
|
||||||
.then(this.redirectHandler.handle(exchange, oneTimeToken)); // <5>
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getUserEmail(String username) {
|
|
||||||
/**/ return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Controller
|
|
||||||
class PageController {
|
|
||||||
|
|
||||||
@GetMapping("/ott/sent")
|
|
||||||
String ottSent() {
|
|
||||||
return "my-template";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::snippet[]
|
|
||||||
-41
@@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.reactive.authentication.sendingtokentouser;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.security.config.Customizer;
|
|
||||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
|
||||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
|
||||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Configuration
|
|
||||||
@EnableWebFluxSecurity
|
|
||||||
public class SecurityConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
|
|
||||||
http
|
|
||||||
// ...
|
|
||||||
.formLogin(Customizer.withDefaults())
|
|
||||||
.oneTimeTokenLogin(Customizer.withDefaults());
|
|
||||||
return http.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
-35
@@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.servlet.authentication.changinggenerateurl;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeToken;
|
|
||||||
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
// tag::snippet[]
|
|
||||||
@Component
|
|
||||||
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handle(HttpServletRequest request, HttpServletResponse response, OneTimeToken oneTimeToken) {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::snippet[]
|
|
||||||
-43
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.servlet.authentication.changinggenerateurl;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
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.web.SecurityFilterChain;
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Configuration
|
|
||||||
@EnableWebSecurity
|
|
||||||
public class SecurityConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http) {
|
|
||||||
http
|
|
||||||
// ...
|
|
||||||
.formLogin(Customizer.withDefaults())
|
|
||||||
.oneTimeTokenLogin((ott) -> ott
|
|
||||||
.tokenGeneratingUrl("/ott/my-generate-url")
|
|
||||||
);
|
|
||||||
return http.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
-35
@@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.servlet.authentication.changingsubmitpageurl;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeToken;
|
|
||||||
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
// tag::snippet[]
|
|
||||||
@Component
|
|
||||||
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handle(HttpServletRequest request, HttpServletResponse response, OneTimeToken oneTimeToken) {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::snippet[]
|
|
||||||
-43
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.servlet.authentication.changingsubmitpageurl;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
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.web.SecurityFilterChain;
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Configuration
|
|
||||||
@EnableWebSecurity
|
|
||||||
public class SecurityConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http) {
|
|
||||||
http
|
|
||||||
// ...
|
|
||||||
.formLogin(Customizer.withDefaults())
|
|
||||||
.oneTimeTokenLogin((ott) -> ott
|
|
||||||
.defaultSubmitPageUrl("/ott/submit")
|
|
||||||
);
|
|
||||||
return http.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
-35
@@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.servlet.authentication.customizegenerateconsumetoken;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeToken;
|
|
||||||
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
// tag::snippet[]
|
|
||||||
@Component
|
|
||||||
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handle(HttpServletRequest request, HttpServletResponse response, OneTimeToken oneTimeToken) {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::snippet[]
|
|
||||||
-37
@@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.servlet.authentication.customizegenerateconsumetoken;
|
|
||||||
|
|
||||||
import org.jspecify.annotations.Nullable;
|
|
||||||
import org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeToken;
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken;
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeTokenService;
|
|
||||||
|
|
||||||
class MyCustomOneTimeTokenService implements OneTimeTokenService {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OneTimeToken generate(GenerateOneTimeTokenRequest request) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable OneTimeToken consume(OneTimeTokenAuthenticationToken authenticationToken) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-47
@@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.servlet.authentication.customizegenerateconsumetoken;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeTokenService;
|
|
||||||
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.web.SecurityFilterChain;
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Configuration
|
|
||||||
@EnableWebSecurity
|
|
||||||
public class OneTimeTokenServiceBeanSecurityConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http) {
|
|
||||||
http
|
|
||||||
// ...
|
|
||||||
.formLogin(Customizer.withDefaults())
|
|
||||||
.oneTimeTokenLogin(Customizer.withDefaults());
|
|
||||||
return http.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public OneTimeTokenService oneTimeTokenService() {
|
|
||||||
return new MyCustomOneTimeTokenService();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
-43
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.servlet.authentication.customizegenerateconsumetoken;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
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.web.SecurityFilterChain;
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Configuration
|
|
||||||
@EnableWebSecurity
|
|
||||||
public class OneTimeTokenServiceDSLSecurityConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http) {
|
|
||||||
http
|
|
||||||
// ...
|
|
||||||
.formLogin(Customizer.withDefaults())
|
|
||||||
.oneTimeTokenLogin((ott) -> ott
|
|
||||||
.tokenService(new MyCustomOneTimeTokenService())
|
|
||||||
);
|
|
||||||
return http.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
-39
@@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.servlet.authentication.customizegeneratetokenrequest;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;
|
|
||||||
import org.springframework.security.web.authentication.ott.DefaultGenerateOneTimeTokenRequestResolver;
|
|
||||||
import org.springframework.security.web.authentication.ott.GenerateOneTimeTokenRequestResolver;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
|
|
||||||
public class SecurityConfig {
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Bean
|
|
||||||
GenerateOneTimeTokenRequestResolver generateOneTimeTokenRequestResolver() {
|
|
||||||
DefaultGenerateOneTimeTokenRequestResolver delegate = new DefaultGenerateOneTimeTokenRequestResolver();
|
|
||||||
return (request) -> {
|
|
||||||
GenerateOneTimeTokenRequest generate = delegate.resolve(request);
|
|
||||||
return new GenerateOneTimeTokenRequest(generate.getUsername(), Duration.ofMinutes(10));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
|
|
||||||
}
|
|
||||||
-35
@@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.servlet.authentication.disablingdefaultsubmitpage;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeToken;
|
|
||||||
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
// tag::snippet[]
|
|
||||||
@Component
|
|
||||||
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handle(HttpServletRequest request, HttpServletResponse response, OneTimeToken oneTimeToken) {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::snippet[]
|
|
||||||
-32
@@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.servlet.authentication.disablingdefaultsubmitpage;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
|
|
||||||
// tag::snippet[]
|
|
||||||
@Controller
|
|
||||||
public class MyController {
|
|
||||||
|
|
||||||
@GetMapping("/my-ott-submit")
|
|
||||||
public String ottSubmitPage() {
|
|
||||||
return "my-ott-submit";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::snippet[]
|
|
||||||
-46
@@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.servlet.authentication.disablingdefaultsubmitpage;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
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.web.SecurityFilterChain;
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Configuration
|
|
||||||
@EnableWebSecurity
|
|
||||||
public class SecurityConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http) {
|
|
||||||
http
|
|
||||||
.authorizeHttpRequests((authorize) -> authorize
|
|
||||||
.requestMatchers("/my-ott-submit").permitAll()
|
|
||||||
.anyRequest().authenticated()
|
|
||||||
)
|
|
||||||
.formLogin(Customizer.withDefaults())
|
|
||||||
.oneTimeTokenLogin((ott) -> ott
|
|
||||||
.showDefaultSubmitPage(false)
|
|
||||||
);
|
|
||||||
return http.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
-80
@@ -1,80 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.servlet.authentication.sendingtokentouser;
|
|
||||||
|
|
||||||
import jakarta.servlet.ServletException;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import org.springframework.mail.MailSender;
|
|
||||||
import org.springframework.mail.SimpleMailMessage;
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeToken;
|
|
||||||
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;
|
|
||||||
import org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler;
|
|
||||||
import org.springframework.security.web.util.UrlUtils;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.util.UriComponentsBuilder;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
// tag::snippet[]
|
|
||||||
@Component // <1>
|
|
||||||
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {
|
|
||||||
|
|
||||||
private final MailSender mailSender;
|
|
||||||
|
|
||||||
private final OneTimeTokenGenerationSuccessHandler redirectHandler = new RedirectOneTimeTokenGenerationSuccessHandler("/ott/sent");
|
|
||||||
|
|
||||||
public MagicLinkOneTimeTokenGenerationSuccessHandler(MailSender mailSender) {
|
|
||||||
this.mailSender = mailSender;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handle(HttpServletRequest request, HttpServletResponse response, OneTimeToken oneTimeToken) throws IOException, ServletException {
|
|
||||||
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(UrlUtils.buildFullRequestUrl(request))
|
|
||||||
.replacePath(request.getContextPath())
|
|
||||||
.replaceQuery(null)
|
|
||||||
.fragment(null)
|
|
||||||
.path("/login/ott")
|
|
||||||
.queryParam("token", oneTimeToken.getTokenValue()); // <2>
|
|
||||||
String magicLink = builder.toUriString();
|
|
||||||
String email = getUserEmail(oneTimeToken.getUsername()); // <3>
|
|
||||||
SimpleMailMessage message = new SimpleMailMessage();
|
|
||||||
message.setTo(email);
|
|
||||||
message.setSubject("Your Spring Security One Time Token");
|
|
||||||
message.setText("Use the following link to sign in into the application: " + magicLink);
|
|
||||||
this.mailSender.send(message); // <4>
|
|
||||||
this.redirectHandler.handle(request, response, oneTimeToken); // <5>
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getUserEmail(String username) {
|
|
||||||
/**/ return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Controller
|
|
||||||
class PageController {
|
|
||||||
|
|
||||||
@GetMapping("/ott/sent")
|
|
||||||
String ottSent() {
|
|
||||||
return "my-template";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::snippet[]
|
|
||||||
-41
@@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.docs.servlet.authentication.sendingtokentouser;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
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.web.SecurityFilterChain;
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Configuration
|
|
||||||
@EnableWebSecurity
|
|
||||||
public class SecurityConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http) {
|
|
||||||
http
|
|
||||||
// ...
|
|
||||||
.formLogin(Customizer.withDefaults())
|
|
||||||
.oneTimeTokenLogin(Customizer.withDefaults());
|
|
||||||
return http.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
-34
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.kt.docs.reactive.authentication.changinggenerateurl
|
|
||||||
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeToken
|
|
||||||
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler
|
|
||||||
import org.springframework.stereotype.Component
|
|
||||||
import org.springframework.web.server.ServerWebExchange
|
|
||||||
import reactor.core.publisher.Mono
|
|
||||||
|
|
||||||
// tag::snippet[]
|
|
||||||
@Component
|
|
||||||
class MagicLinkOneTimeTokenGenerationSuccessHandler : ServerOneTimeTokenGenerationSuccessHandler {
|
|
||||||
|
|
||||||
override fun handle(exchange: ServerWebExchange, oneTimeToken: OneTimeToken): Mono<Void> {
|
|
||||||
/**/ return Mono.empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::snippet[]
|
|
||||||
-41
@@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.kt.docs.reactive.authentication.changinggenerateurl
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Configuration
|
|
||||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
|
|
||||||
import org.springframework.security.config.web.server.ServerHttpSecurity
|
|
||||||
import org.springframework.security.config.web.server.invoke
|
|
||||||
import org.springframework.security.web.server.SecurityWebFilterChain
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Configuration
|
|
||||||
@EnableWebFluxSecurity
|
|
||||||
open class SecurityConfig {
|
|
||||||
|
|
||||||
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
|
||||||
return http {
|
|
||||||
// ...
|
|
||||||
formLogin { }
|
|
||||||
oneTimeTokenLogin {
|
|
||||||
tokenGeneratingUrl = "/ott/my-generate-url"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
-34
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.kt.docs.reactive.authentication.changingsubmitpageurl
|
|
||||||
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeToken
|
|
||||||
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler
|
|
||||||
import org.springframework.stereotype.Component
|
|
||||||
import org.springframework.web.server.ServerWebExchange
|
|
||||||
import reactor.core.publisher.Mono
|
|
||||||
|
|
||||||
// tag::snippet[]
|
|
||||||
@Component
|
|
||||||
class MagicLinkOneTimeTokenGenerationSuccessHandler : ServerOneTimeTokenGenerationSuccessHandler {
|
|
||||||
|
|
||||||
override fun handle(exchange: ServerWebExchange, oneTimeToken: OneTimeToken): Mono<Void> {
|
|
||||||
/**/ return Mono.empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::snippet[]
|
|
||||||
-41
@@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.kt.docs.reactive.authentication.changingsubmitpageurl
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Configuration
|
|
||||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
|
|
||||||
import org.springframework.security.config.web.server.ServerHttpSecurity
|
|
||||||
import org.springframework.security.config.web.server.invoke
|
|
||||||
import org.springframework.security.web.server.SecurityWebFilterChain
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Configuration
|
|
||||||
@EnableWebFluxSecurity
|
|
||||||
open class SecurityConfig {
|
|
||||||
|
|
||||||
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
|
||||||
return http {
|
|
||||||
// ...
|
|
||||||
formLogin { }
|
|
||||||
oneTimeTokenLogin {
|
|
||||||
defaultSubmitPageUrl = "/ott/submit"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
-34
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.kt.docs.reactive.authentication.customizegenerateconsumetoken
|
|
||||||
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeToken
|
|
||||||
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler
|
|
||||||
import org.springframework.stereotype.Component
|
|
||||||
import org.springframework.web.server.ServerWebExchange
|
|
||||||
import reactor.core.publisher.Mono
|
|
||||||
|
|
||||||
// tag::snippet[]
|
|
||||||
@Component
|
|
||||||
class MagicLinkOneTimeTokenGenerationSuccessHandler : ServerOneTimeTokenGenerationSuccessHandler {
|
|
||||||
|
|
||||||
override fun handle(exchange: ServerWebExchange, oneTimeToken: OneTimeToken): Mono<Void> {
|
|
||||||
/**/ return Mono.empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::snippet[]
|
|
||||||
-36
@@ -1,36 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.kt.docs.reactive.authentication.customizegenerateconsumetoken
|
|
||||||
|
|
||||||
import org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeToken
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken
|
|
||||||
import org.springframework.security.authentication.ott.reactive.ReactiveOneTimeTokenService
|
|
||||||
import reactor.core.publisher.Mono
|
|
||||||
|
|
||||||
class MyCustomReactiveOneTimeTokenService: ReactiveOneTimeTokenService {
|
|
||||||
|
|
||||||
override fun generate(request: GenerateOneTimeTokenRequest): Mono<OneTimeToken> {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun consume(authenticationToken: OneTimeTokenAuthenticationToken): Mono<OneTimeToken> {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-46
@@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.kt.docs.reactive.authentication.customizegenerateconsumetoken
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean
|
|
||||||
import org.springframework.context.annotation.Configuration
|
|
||||||
import org.springframework.security.authentication.ott.reactive.ReactiveOneTimeTokenService
|
|
||||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
|
|
||||||
import org.springframework.security.config.web.server.ServerHttpSecurity
|
|
||||||
import org.springframework.security.config.web.server.invoke
|
|
||||||
import org.springframework.security.web.server.SecurityWebFilterChain
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Configuration
|
|
||||||
@EnableWebFluxSecurity
|
|
||||||
open class OneTimeTokenServiceBeanSecurityConfig {
|
|
||||||
|
|
||||||
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
|
||||||
return http {
|
|
||||||
//..
|
|
||||||
formLogin { }
|
|
||||||
oneTimeTokenLogin { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
open fun oneTimeTokenService(): ReactiveOneTimeTokenService {
|
|
||||||
return MyCustomReactiveOneTimeTokenService()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
-41
@@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.kt.docs.reactive.authentication.customizegenerateconsumetoken
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Configuration
|
|
||||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
|
|
||||||
import org.springframework.security.config.web.server.ServerHttpSecurity
|
|
||||||
import org.springframework.security.config.web.server.invoke
|
|
||||||
import org.springframework.security.web.server.SecurityWebFilterChain
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Configuration
|
|
||||||
@EnableWebFluxSecurity
|
|
||||||
open class OneTimeTokenServiceDSLSecurityConfig {
|
|
||||||
|
|
||||||
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
|
||||||
return http {
|
|
||||||
//..
|
|
||||||
formLogin { }
|
|
||||||
oneTimeTokenLogin {
|
|
||||||
tokenService = MyCustomReactiveOneTimeTokenService()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
-35
@@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.kt.docs.reactive.authentication.customizegeneratetokenrequest
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean
|
|
||||||
import org.springframework.security.web.server.authentication.ott.DefaultServerGenerateOneTimeTokenRequestResolver
|
|
||||||
import org.springframework.security.web.server.authentication.ott.ServerGenerateOneTimeTokenRequestResolver
|
|
||||||
import java.time.Duration
|
|
||||||
|
|
||||||
open class SecurityConfig {
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Bean
|
|
||||||
fun generateOneTimeTokenRequestResolver() : ServerGenerateOneTimeTokenRequestResolver {
|
|
||||||
return DefaultServerGenerateOneTimeTokenRequestResolver().apply {
|
|
||||||
this.setExpiresIn(Duration.ofMinutes(10))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
|
|
||||||
}
|
|
||||||
-35
@@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.kt.docs.reactive.authentication.disablingdefaultsubmitpage
|
|
||||||
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeToken
|
|
||||||
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler
|
|
||||||
import org.springframework.stereotype.Component
|
|
||||||
import org.springframework.web.server.ServerWebExchange
|
|
||||||
import reactor.core.publisher.Mono
|
|
||||||
|
|
||||||
// tag::snippet[]
|
|
||||||
@Component
|
|
||||||
class MagicLinkOneTimeTokenGenerationSuccessHandler : ServerOneTimeTokenGenerationSuccessHandler {
|
|
||||||
|
|
||||||
override fun handle(exchange: ServerWebExchange, oneTimeToken: OneTimeToken): Mono<Void> {
|
|
||||||
/**/ return Mono.empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// end::snippet[]
|
|
||||||
-32
@@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.kt.docs.reactive.authentication.disablingdefaultsubmitpage
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Controller
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
|
||||||
|
|
||||||
// tag::snippet[]
|
|
||||||
@Controller
|
|
||||||
class MyController {
|
|
||||||
|
|
||||||
@GetMapping("/my-ott-submit")
|
|
||||||
fun ottSubmitPage(): String {
|
|
||||||
return "my-ott-submit"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::snippet[]
|
|
||||||
-45
@@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.kt.docs.reactive.authentication.disablingdefaultsubmitpage
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Configuration
|
|
||||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
|
|
||||||
import org.springframework.security.config.web.server.ServerHttpSecurity
|
|
||||||
import org.springframework.security.config.web.server.invoke
|
|
||||||
import org.springframework.security.web.server.SecurityWebFilterChain
|
|
||||||
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers.pathMatchers
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Configuration
|
|
||||||
@EnableWebFluxSecurity
|
|
||||||
open class SecurityConfig {
|
|
||||||
|
|
||||||
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
|
||||||
return http {
|
|
||||||
authorizeExchange {
|
|
||||||
authorize(pathMatchers("/my-ott-submit"), permitAll)
|
|
||||||
authorize(anyExchange, authenticated)
|
|
||||||
}
|
|
||||||
formLogin { }
|
|
||||||
oneTimeTokenLogin {
|
|
||||||
showDefaultSubmitPage = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
-78
@@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.kt.docs.reactive.authentication.sendingtokentouser
|
|
||||||
|
|
||||||
import org.springframework.mail.MailSender
|
|
||||||
import org.springframework.mail.SimpleMailMessage
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeToken
|
|
||||||
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler
|
|
||||||
import org.springframework.security.web.server.authentication.ott.ServerRedirectOneTimeTokenGenerationSuccessHandler
|
|
||||||
import org.springframework.stereotype.Component
|
|
||||||
import org.springframework.stereotype.Controller
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
|
||||||
import org.springframework.web.server.ServerWebExchange
|
|
||||||
import org.springframework.web.util.UriComponentsBuilder
|
|
||||||
import reactor.core.publisher.Mono
|
|
||||||
import java.util.function.Function
|
|
||||||
|
|
||||||
|
|
||||||
// tag::snippet[]
|
|
||||||
@Component // <1>
|
|
||||||
class MagicLinkOneTimeTokenGenerationSuccessHandler(val mailSender: MailSender) : ServerOneTimeTokenGenerationSuccessHandler {
|
|
||||||
|
|
||||||
private val redirectHandler: ServerOneTimeTokenGenerationSuccessHandler = ServerRedirectOneTimeTokenGenerationSuccessHandler("/ott/sent")
|
|
||||||
|
|
||||||
override fun handle(exchange: ServerWebExchange, oneTimeToken: OneTimeToken): Mono<Void> {
|
|
||||||
|
|
||||||
return Mono.just(exchange.request)
|
|
||||||
.map(Function { request ->
|
|
||||||
UriComponentsBuilder.fromUri(request.uri)
|
|
||||||
.replacePath(request.path.contextPath().value())
|
|
||||||
.replaceQuery(null)
|
|
||||||
.fragment(null)
|
|
||||||
.path("/login/ott")
|
|
||||||
.queryParam("token", oneTimeToken.getTokenValue())
|
|
||||||
.toUriString() // <2>
|
|
||||||
})
|
|
||||||
.flatMap(Function { uri ->
|
|
||||||
val email = getUserEmail(oneTimeToken.getUsername()) // <3>
|
|
||||||
val message = SimpleMailMessage()
|
|
||||||
message.setTo(email)
|
|
||||||
message.subject = "Your Spring Security One Time Token"
|
|
||||||
message.text = "Use the following link to sign in into the application: $uri"
|
|
||||||
this.mailSender.send(message) // <4>
|
|
||||||
Mono.empty()
|
|
||||||
})
|
|
||||||
.then(this.redirectHandler.handle(exchange, oneTimeToken)) // <5>
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getUserEmail(username: String): String {
|
|
||||||
/**/ return username
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Controller
|
|
||||||
class PageController {
|
|
||||||
|
|
||||||
@GetMapping("/ott/sent")
|
|
||||||
fun ottSent(): String {
|
|
||||||
return "my-template"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::snippet[]
|
|
||||||
-40
@@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.kt.docs.reactive.authentication.sendingtokentouser
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Configuration
|
|
||||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
|
|
||||||
import org.springframework.security.config.web.server.ServerHttpSecurity
|
|
||||||
import org.springframework.security.config.web.server.invoke
|
|
||||||
import org.springframework.security.web.server.SecurityWebFilterChain
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Configuration
|
|
||||||
@EnableWebFluxSecurity
|
|
||||||
open class SecurityConfig {
|
|
||||||
|
|
||||||
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
|
||||||
return http {
|
|
||||||
authorizeExchange {
|
|
||||||
authorize(anyExchange, authenticated)
|
|
||||||
}
|
|
||||||
oneTimeTokenLogin { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
-34
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.kt.docs.servlet.authentication.changinggenerateurl
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest
|
|
||||||
import jakarta.servlet.http.HttpServletResponse
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeToken
|
|
||||||
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler
|
|
||||||
import org.springframework.stereotype.Component
|
|
||||||
|
|
||||||
// tag::snippet[]
|
|
||||||
@Component
|
|
||||||
class MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSuccessHandler {
|
|
||||||
|
|
||||||
override fun handle(request: HttpServletRequest, response: HttpServletResponse, oneTimeToken: OneTimeToken) {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::snippet[]
|
|
||||||
-43
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.kt.docs.servlet.authentication.changinggenerateurl
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean
|
|
||||||
import org.springframework.context.annotation.Configuration
|
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
|
||||||
import org.springframework.security.config.annotation.web.invoke
|
|
||||||
import org.springframework.security.web.SecurityFilterChain
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Configuration
|
|
||||||
@EnableWebSecurity
|
|
||||||
open class SecurityConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
|
||||||
http {
|
|
||||||
//...
|
|
||||||
formLogin { }
|
|
||||||
oneTimeTokenLogin {
|
|
||||||
tokenGeneratingUrl = "/ott/my-generate-url"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return http.build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
-34
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.kt.docs.servlet.authentication.changingsubmitpageurl
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest
|
|
||||||
import jakarta.servlet.http.HttpServletResponse
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeToken
|
|
||||||
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler
|
|
||||||
import org.springframework.stereotype.Component
|
|
||||||
|
|
||||||
// tag::snippet[]
|
|
||||||
@Component
|
|
||||||
class MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSuccessHandler {
|
|
||||||
|
|
||||||
override fun handle(request: HttpServletRequest, response: HttpServletResponse, oneTimeToken: OneTimeToken) {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::snippet[]
|
|
||||||
-43
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.kt.docs.servlet.authentication.changingsubmitpageurl
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean
|
|
||||||
import org.springframework.context.annotation.Configuration
|
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
|
||||||
import org.springframework.security.config.annotation.web.invoke
|
|
||||||
import org.springframework.security.web.SecurityFilterChain
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Configuration
|
|
||||||
@EnableWebSecurity
|
|
||||||
open class SecurityConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
|
||||||
http {
|
|
||||||
//...
|
|
||||||
formLogin { }
|
|
||||||
oneTimeTokenLogin {
|
|
||||||
defaultSubmitPageUrl = "/ott/submit"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return http.build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
-34
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.kt.docs.servlet.authentication.customizegenerateconsumetoken
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest
|
|
||||||
import jakarta.servlet.http.HttpServletResponse
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeToken
|
|
||||||
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler
|
|
||||||
import org.springframework.stereotype.Component
|
|
||||||
|
|
||||||
// tag::snippet[]
|
|
||||||
@Component
|
|
||||||
class MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSuccessHandler {
|
|
||||||
|
|
||||||
override fun handle(request: HttpServletRequest, response: HttpServletResponse, oneTimeToken: OneTimeToken) {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::snippet[]
|
|
||||||
-34
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.kt.docs.servlet.authentication.customizegenerateconsumetoken
|
|
||||||
|
|
||||||
import org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeToken
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeTokenService
|
|
||||||
|
|
||||||
class MyCustomOneTimeTokenService: OneTimeTokenService {
|
|
||||||
|
|
||||||
override fun generate(request: GenerateOneTimeTokenRequest): OneTimeToken {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun consume(authenticationToken: OneTimeTokenAuthenticationToken): OneTimeToken? {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-48
@@ -1,48 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.kt.docs.servlet.authentication.customizegenerateconsumetoken
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean
|
|
||||||
import org.springframework.context.annotation.Configuration
|
|
||||||
import org.springframework.security.authentication.ott.OneTimeTokenService
|
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
|
||||||
import org.springframework.security.config.annotation.web.invoke
|
|
||||||
import org.springframework.security.web.SecurityFilterChain
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Configuration
|
|
||||||
@EnableWebSecurity
|
|
||||||
open class OneTimeTokenServiceBeanSecurityConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
|
||||||
http {
|
|
||||||
//...
|
|
||||||
formLogin { }
|
|
||||||
oneTimeTokenLogin { }
|
|
||||||
}
|
|
||||||
return http.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
open fun oneTimeTokenService(): OneTimeTokenService {
|
|
||||||
return MyCustomOneTimeTokenService()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
-44
@@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2004-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.kt.docs.servlet.authentication.customizegenerateconsumetoken
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean
|
|
||||||
import org.springframework.context.annotation.Configuration
|
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
|
||||||
import org.springframework.security.config.annotation.web.invoke
|
|
||||||
import org.springframework.security.web.SecurityFilterChain
|
|
||||||
|
|
||||||
// tag::config[]
|
|
||||||
@Configuration
|
|
||||||
@EnableWebSecurity
|
|
||||||
open class OneTimeTokenServiceDSLSecurityConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
|
||||||
http {
|
|
||||||
//...
|
|
||||||
formLogin { }
|
|
||||||
oneTimeTokenLogin {
|
|
||||||
tokenService = MyCustomOneTimeTokenService()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return http.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// end::config[]
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user