Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 063e56ce8b | |||
| 1c4d6ed098 | |||
| efaee4e56b | |||
| c9f8d2b111 | |||
| c2d79fcbd6 | |||
| aa225943d2 | |||
| 07ea139ebf | |||
| 67544f36f9 | |||
| 05725af4d8 | |||
| 4fbbfd2c8b | |||
| 66da4301fc | |||
| 8929bd5abc | |||
| e3d1405f67 | |||
| e756a1df19 | |||
| 81fae2db2c | |||
| 054a3f0bc0 | |||
| 409998a3fe | |||
| e5ae35ab71 | |||
| 0d74da4f97 | |||
| 2a336d4f49 | |||
| 5322352427 | |||
| 561f65b34d | |||
| 631076e4dd | |||
| 8d147100ee |
@@ -5,10 +5,14 @@ on:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch: # Manual trigger
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update:
|
||||
name: Update Algolia Index
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -10,20 +10,21 @@ on:
|
||||
env:
|
||||
GH_ACTIONS_REPO_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
env:
|
||||
GRADLE_USER_HOME: ~/.gradle
|
||||
distribution: 'adopt'
|
||||
- name: Generate antora.yml
|
||||
run: ./gradlew :spring-security-docs:generateAntora
|
||||
- name: Extract Branch Name
|
||||
|
||||
@@ -8,9 +8,16 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- '*.x'
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v3
|
||||
|
||||
@@ -3,9 +3,15 @@ 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:
|
||||
|
||||
@@ -2,6 +2,8 @@ name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
schedule:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch: # Manual trigger
|
||||
@@ -16,12 +18,15 @@ env:
|
||||
STRUCTURE101_LICENSEID: ${{ secrets.STRUCTURE101_LICENSEID }}
|
||||
ARTIFACTORY_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
ARTIFACTORY_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
RUN_JOBS: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
prerequisites:
|
||||
name: Pre-requisites for building
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
outputs:
|
||||
runjobs: ${{ steps.continue.outputs.runjobs }}
|
||||
project_version: ${{ steps.continue.outputs.project_version }}
|
||||
@@ -29,8 +34,8 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- id: continue
|
||||
name: Determine if should continue
|
||||
if: env.RUN_JOBS == 'true'
|
||||
run: |
|
||||
# Run jobs if in upstream repository
|
||||
echo "::set-output name=runjobs::true"
|
||||
# Extract version from gradle.properties
|
||||
version=$(cat gradle.properties | grep "version=" | awk -F'=' '{print $2}')
|
||||
@@ -46,17 +51,14 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
- name: Setup Gradle
|
||||
distribution: 'adopt'
|
||||
- name: Set up Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
env:
|
||||
GRADLE_USER_HOME: ~/.gradle
|
||||
- name: Set up gradle user name
|
||||
run: echo 'systemProp.user.name=spring-builds+github' >> gradle.properties
|
||||
- name: Build with Gradle
|
||||
env:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USER }}
|
||||
@@ -70,18 +72,11 @@ jobs:
|
||||
if: needs.prerequisites.outputs.runjobs
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
env:
|
||||
GRADLE_USER_HOME: ~/.gradle
|
||||
distribution: 'adopt'
|
||||
- name: Snapshot Tests
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
@@ -95,18 +90,11 @@ jobs:
|
||||
if: needs.prerequisites.outputs.runjobs
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
env:
|
||||
GRADLE_USER_HOME: ~/.gradle
|
||||
distribution: 'adopt'
|
||||
- name: Check samples project
|
||||
env:
|
||||
LOCAL_REPOSITORY_PATH: ${{ github.workspace }}/build/publications/repos
|
||||
@@ -124,18 +112,11 @@ jobs:
|
||||
if: needs.prerequisites.outputs.runjobs
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
env:
|
||||
GRADLE_USER_HOME: ~/.gradle
|
||||
distribution: 'adopt'
|
||||
- name: Check for package tangles
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
@@ -148,18 +129,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
env:
|
||||
GRADLE_USER_HOME: ~/.gradle
|
||||
distribution: 'adopt'
|
||||
- name: Deploy artifacts
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
@@ -179,18 +153,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
env:
|
||||
GRADLE_USER_HOME: ~/.gradle
|
||||
distribution: 'adopt'
|
||||
- name: Deploy Docs
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
@@ -207,18 +174,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
env:
|
||||
GRADLE_USER_HOME: ~/.gradle
|
||||
distribution: 'adopt'
|
||||
- name: Deploy Schema
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
@@ -233,6 +193,8 @@ jobs:
|
||||
name: Perform release
|
||||
needs: [prerequisites, deploy_artifacts, deploy_docs, deploy_schema]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
timeout-minutes: 90
|
||||
if: ${{ !endsWith(needs.prerequisites.outputs.project_version, '-SNAPSHOT') }}
|
||||
env:
|
||||
@@ -244,18 +206,11 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
env:
|
||||
GRADLE_USER_HOME: ~/.gradle
|
||||
distribution: 'adopt'
|
||||
- name: Wait for Artifactory Artifacts
|
||||
if: ${{ contains(needs.prerequisites.outputs.project_version, '-RC') || contains(needs.prerequisites.outputs.project_version, '-M') }}
|
||||
run: |
|
||||
@@ -321,6 +276,9 @@ jobs:
|
||||
name: Perform post-release
|
||||
needs: [prerequisites, deploy_artifacts, deploy_docs, deploy_schema]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
timeout-minutes: 90
|
||||
if: ${{ endsWith(needs.prerequisites.outputs.project_version, '-SNAPSHOT') }}
|
||||
env:
|
||||
@@ -328,7 +286,8 @@ jobs:
|
||||
VERSION: ${{ needs.prerequisites.outputs.project_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: spring-io/spring-gradle-build-action@v1
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
@@ -336,9 +295,11 @@ jobs:
|
||||
run: ./gradlew scheduleNextRelease -PnextVersion=$VERSION -PgitHubAccessToken=$TOKEN
|
||||
notify_result:
|
||||
name: Check for failures
|
||||
needs: [build_jdk_11, snapshot_tests, check_samples, check_tangles, deploy_artifacts, deploy_docs, deploy_schema, perform_release, perform_post_release]
|
||||
needs: [perform_release, perform_post_release]
|
||||
if: failure()
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
steps:
|
||||
- name: Send Slack message
|
||||
# Workaround while waiting for Gamesight/slack-workflow-status#38 to be fixed
|
||||
|
||||
@@ -7,29 +7,27 @@ on:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch: # Manual trigger
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: deploy
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v2
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
- name: Validate Gradle wrapper
|
||||
uses: gradle/wrapper-validation-action@e6e38bacfdf1a337459f332974bb2327a31aaf4b
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
env:
|
||||
GRADLE_USER_HOME: ~/.gradle
|
||||
with:
|
||||
# Remove some files from the Gradle cache, so they aren't cached by GitHub Actions.
|
||||
# Restoring these files from a GitHub Actions cache might cause problems for future builds.
|
||||
gradle-home-cache-excludes: |
|
||||
caches/modules-2/modules-2.lock
|
||||
caches/modules-2/gc.properties
|
||||
- name: Cleanup Gradle Cache
|
||||
# Remove some files from the Gradle cache, so they aren't cached by GitHub Actions.
|
||||
# Restoring these files from a GitHub Actions cache might cause problems for future builds.
|
||||
run: |
|
||||
rm -f /home/runner/.gradle/caches/modules-2/modules-2.lock
|
||||
rm -f /home/runner/.gradle/caches/modules-2/gc.properties
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew :spring-security-docs:antora --stacktrace
|
||||
- name: Deploy
|
||||
|
||||
@@ -5,10 +5,15 @@ on:
|
||||
env:
|
||||
DUE_ON: ${{ github.event.milestone.due_on }}
|
||||
TITLE: ${{ github.event.milestone.title }}
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
spring-releasetrain-checks:
|
||||
name: Check DueOn is on a Release Date
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
permissions:
|
||||
contents: none
|
||||
steps:
|
||||
- name: Print Milestone Being Checked
|
||||
run: echo "Validating DueOn '$DUE_ON' for milestone '$TITLE'"
|
||||
@@ -23,6 +28,8 @@ jobs:
|
||||
needs: [spring-releasetrain-checks]
|
||||
if: failure()
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
steps:
|
||||
- name: Send Slack message
|
||||
uses: Gamesight/slack-workflow-status@v1.0.1
|
||||
|
||||
@@ -2,28 +2,20 @@ name: PR Build
|
||||
|
||||
on: pull_request
|
||||
|
||||
env:
|
||||
RUN_JOBS: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
steps:
|
||||
- if: env.RUN_JOBS == 'true'
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up JDK
|
||||
if: env.RUN_JOBS == 'true'
|
||||
uses: actions/setup-java@v1
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
- name: Setup Gradle
|
||||
if: env.RUN_JOBS == 'true'
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
cache-read-only: true
|
||||
env:
|
||||
GRADLE_USER_HOME: ~/.gradle
|
||||
distribution: 'adopt'
|
||||
- name: Build with Gradle
|
||||
if: env.RUN_JOBS == 'true'
|
||||
run: ./gradlew clean build --continue --scan
|
||||
|
||||
@@ -9,30 +9,28 @@ env:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }}
|
||||
GRADLE_ENTERPRISE_SECRET_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update_scheduled_release_version:
|
||||
name: Initiate Release If Scheduled
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
steps:
|
||||
- id: checkout-source
|
||||
name: Checkout Source Code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
- id: setup-jdk
|
||||
name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
env:
|
||||
GRADLE_USER_HOME: ~/.gradle
|
||||
distribution: 'adopt'
|
||||
- id: check-release-due
|
||||
name: Check Release Due
|
||||
run: |
|
||||
|
||||
@@ -96,7 +96,7 @@ dependencies {
|
||||
implementation 'io.spring.nohttp:nohttp-gradle:0.0.10'
|
||||
implementation 'net.sourceforge.htmlunit:htmlunit:2.37.0'
|
||||
implementation 'org.hidetake:gradle-ssh-plugin:2.10.1'
|
||||
implementation 'org.jfrog.buildinfo:build-info-extractor-gradle:4.24.20'
|
||||
implementation 'org.jfrog.buildinfo:build-info-extractor-gradle:4.29.0'
|
||||
implementation 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1'
|
||||
|
||||
testImplementation platform('org.junit:junit-bom:5.9.0-RC1')
|
||||
|
||||
@@ -17,6 +17,7 @@ package io.spring.gradle.convention
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin
|
||||
|
||||
class ArtifactoryPlugin implements Plugin<Project> {
|
||||
|
||||
@@ -36,8 +37,14 @@ class ArtifactoryPlugin implements Plugin<Project> {
|
||||
password = artifactoryPassword
|
||||
}
|
||||
}
|
||||
defaults {
|
||||
publications('mavenJava')
|
||||
}
|
||||
}
|
||||
project.plugins.withType(MavenPublishPlugin) {
|
||||
project.artifactory {
|
||||
publish {
|
||||
defaults {
|
||||
publications('mavenJava')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ class RootProjectPlugin implements Plugin<Project> {
|
||||
pluginManager.apply(NoHttpPlugin)
|
||||
pluginManager.apply(SpringNexusPublishPlugin)
|
||||
pluginManager.apply(CheckProhibitedDependenciesLifecyclePlugin)
|
||||
pluginManager.apply(ArtifactoryPlugin)
|
||||
pluginManager.apply("org.sonarqube")
|
||||
|
||||
project.repositories.mavenCentral()
|
||||
|
||||
+29
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,10 +39,19 @@ import org.springframework.security.config.annotation.web.servlet.configuration.
|
||||
* @EnableGlobalAuthentication
|
||||
* public class MyGlobalAuthenticationConfiguration {
|
||||
*
|
||||
* @Autowired
|
||||
* public void configureGlobal(AuthenticationManagerBuilder auth) {
|
||||
* auth.inMemoryAuthentication().withUser("user").password("password").roles("USER")
|
||||
* .and().withUser("admin").password("password").roles("USER", "ADMIN");
|
||||
* @Bean
|
||||
* public UserDetailsService userDetailsService() {
|
||||
* UserDetails user = User.withDefaultPasswordEncoder()
|
||||
* .username("user")
|
||||
* .password("password")
|
||||
* .roles("USER")
|
||||
* .build();
|
||||
* UserDetails admin = User.withDefaultPasswordEncoder()
|
||||
* .username("admin")
|
||||
* .password("password")
|
||||
* .roles("ADMIN", "USER")
|
||||
* .build();
|
||||
* return new InMemoryUserDetailsManager(user, admin);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
@@ -54,15 +63,24 @@ import org.springframework.security.config.annotation.web.servlet.configuration.
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableWebSecurity
|
||||
* public class MyWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
* public class MyWebSecurityConfiguration {
|
||||
*
|
||||
* @Autowired
|
||||
* public void configureGlobal(AuthenticationManagerBuilder auth) {
|
||||
* auth.inMemoryAuthentication().withUser("user").password("password").roles("USER")
|
||||
* .and().withUser("admin").password("password").roles("USER", "ADMIN");
|
||||
* @Bean
|
||||
* public UserDetailsService userDetailsService() {
|
||||
* UserDetails user = User.withDefaultPasswordEncoder()
|
||||
* .username("user")
|
||||
* .password("password")
|
||||
* .roles("USER")
|
||||
* .build();
|
||||
* UserDetails admin = User.withDefaultPasswordEncoder()
|
||||
* .username("admin")
|
||||
* .password("password")
|
||||
* .roles("ADMIN", "USER")
|
||||
* .build();
|
||||
* return new InMemoryUserDetailsManager(user, admin);
|
||||
* }
|
||||
*
|
||||
* // Possibly overridden methods ...
|
||||
* // Possibly more bean methods ...
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
|
||||
+4
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,19 +23,16 @@ import org.springframework.security.config.annotation.SecurityBuilder;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
/**
|
||||
* Allows customization to the {@link WebSecurity}. In most instances users will use
|
||||
* {@link EnableWebSecurity} and either create a {@link Configuration} that extends
|
||||
* {@link WebSecurityConfigurerAdapter} or expose a {@link SecurityFilterChain} bean. Both
|
||||
* will automatically be applied to the {@link WebSecurity} by the
|
||||
* {@link EnableWebSecurity} annotation.
|
||||
* {@link EnableWebSecurity} and create a {@link Configuration} that exposes a
|
||||
* {@link SecurityFilterChain} bean. This will automatically be applied to the
|
||||
* {@link WebSecurity} by the {@link EnableWebSecurity} annotation.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
* @see WebSecurityConfigurerAdapter
|
||||
* @see SecurityFilterChain
|
||||
*/
|
||||
public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends SecurityConfigurer<Filter, T> {
|
||||
|
||||
+772
-406
File diff suppressed because it is too large
Load Diff
+5
-7
@@ -42,7 +42,6 @@ import org.springframework.security.config.annotation.web.AbstractRequestMatcher
|
||||
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
@@ -77,8 +76,7 @@ import org.springframework.web.filter.DelegatingFilterProxy;
|
||||
*
|
||||
* <p>
|
||||
* Customizations to the {@link WebSecurity} can be made by creating a
|
||||
* {@link WebSecurityConfigurer}, overriding {@link WebSecurityConfigurerAdapter} or
|
||||
* exposing a {@link WebSecurityCustomizer} bean.
|
||||
* {@link WebSecurityConfigurer} or exposing a {@link WebSecurityCustomizer} bean.
|
||||
* </p>
|
||||
*
|
||||
* @author Rob Winch
|
||||
@@ -200,7 +198,7 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||
*
|
||||
* <p>
|
||||
* Typically this method is invoked automatically within the framework from
|
||||
* {@link WebSecurityConfigurerAdapter#init(WebSecurity)}
|
||||
* {@link WebSecurityConfiguration#springSecurityFilterChain()}
|
||||
* </p>
|
||||
* @param securityFilterChainBuilder the builder to use to create the
|
||||
* {@link SecurityFilterChain} instances
|
||||
@@ -258,12 +256,13 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||
|
||||
/**
|
||||
* Sets the {@link FilterSecurityInterceptor}. This is typically invoked by
|
||||
* {@link WebSecurityConfigurerAdapter}.
|
||||
* {@link WebSecurityConfiguration#springSecurityFilterChain()}.
|
||||
* @param securityInterceptor the {@link FilterSecurityInterceptor} to use
|
||||
* @return the {@link WebSecurity} for further customizations
|
||||
* @deprecated Use {@link #privilegeEvaluator(WebInvocationPrivilegeEvaluator)}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
public WebSecurity securityInterceptor(FilterSecurityInterceptor securityInterceptor) {
|
||||
this.filterSecurityInterceptor = securityInterceptor;
|
||||
return this;
|
||||
@@ -296,8 +295,7 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||
protected Filter performBuild() throws Exception {
|
||||
Assert.state(!this.securityFilterChainBuilders.isEmpty(),
|
||||
() -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "
|
||||
+ "Typically this is done by exposing a SecurityFilterChain bean "
|
||||
+ "or by adding a @Configuration that extends WebSecurityConfigurerAdapter. "
|
||||
+ "Typically this is done by exposing a SecurityFilterChain bean. "
|
||||
+ "More advanced users can invoke " + WebSecurity.class.getSimpleName()
|
||||
+ ".addSecurityFilterChainBuilder directly");
|
||||
int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
|
||||
|
||||
+25
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,48 +26,56 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;
|
||||
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
/**
|
||||
* Add this annotation to an {@code @Configuration} class to have the Spring Security
|
||||
* configuration defined in any {@link WebSecurityConfigurer} or more likely by extending
|
||||
* the {@link WebSecurityConfigurerAdapter} base class and overriding individual methods:
|
||||
* configuration defined in any {@link WebSecurityConfigurer} or more likely by exposing a
|
||||
* {@link SecurityFilterChain} bean:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableWebSecurity
|
||||
* public class MyWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
* public class MyWebSecurityConfiguration {
|
||||
*
|
||||
* @Override
|
||||
* public void configure(WebSecurity web) throws Exception {
|
||||
* web.ignoring()
|
||||
* @Bean
|
||||
* public WebSecurityCustomizer webSecurityCustomizer() {
|
||||
* return (web) -> web.ignoring()
|
||||
* // Spring Security should completely ignore URLs starting with /resources/
|
||||
* .antMatchers("/resources/**");
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* protected void configure(HttpSecurity http) throws Exception {
|
||||
* @Bean
|
||||
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
* http.authorizeRequests().antMatchers("/public/**").permitAll().anyRequest()
|
||||
* .hasRole("USER").and()
|
||||
* // Possibly more configuration ...
|
||||
* .formLogin() // enable form based log in
|
||||
* // set permitAll for all URLs associated with Form Login
|
||||
* .permitAll();
|
||||
* return http.build();
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
* auth
|
||||
* // enable in memory based authentication with a user named "user" and "admin"
|
||||
* .inMemoryAuthentication().withUser("user").password("password").roles("USER")
|
||||
* .and().withUser("admin").password("password").roles("USER", "ADMIN");
|
||||
* @Bean
|
||||
* public UserDetailsService userDetailsService() {
|
||||
* UserDetails user = User.withDefaultPasswordEncoder()
|
||||
* .username("user")
|
||||
* .password("password")
|
||||
* .roles("USER")
|
||||
* .build();
|
||||
* UserDetails admin = User.withDefaultPasswordEncoder()
|
||||
* .username("admin")
|
||||
* .password("password")
|
||||
* .roles("ADMIN", "USER")
|
||||
* .build();
|
||||
* return new InMemoryUserDetailsManager(user, admin);
|
||||
* }
|
||||
*
|
||||
* // Possibly more overridden methods ...
|
||||
* // Possibly more bean methods ...
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @see WebSecurityConfigurer
|
||||
* @see WebSecurityConfigurerAdapter
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
|
||||
+4
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -54,10 +54,9 @@ import org.springframework.util.Assert;
|
||||
/**
|
||||
* Uses a {@link WebSecurity} to create the {@link FilterChainProxy} that performs the web
|
||||
* based security for Spring Security. It then exports the necessary beans. Customizations
|
||||
* can be made to {@link WebSecurity} by extending {@link WebSecurityConfigurerAdapter}
|
||||
* and exposing it as a {@link Configuration} or implementing
|
||||
* {@link WebSecurityConfigurer} and exposing it as a {@link Configuration}. This
|
||||
* configuration is imported when using {@link EnableWebSecurity}.
|
||||
* can be made to {@link WebSecurity} by implementing {@link WebSecurityConfigurer} and
|
||||
* exposing it as a {@link Configuration} or exposing a {@link WebSecurityCustomizer}
|
||||
* bean. This configuration is imported when using {@link EnableWebSecurity}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Keesun Baik
|
||||
|
||||
+5
-5
@@ -25,7 +25,7 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.security.authentication.AuthenticationDetailsSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.openid.OpenIDLoginConfigurer;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.PortMapper;
|
||||
@@ -307,14 +307,14 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
|
||||
/**
|
||||
* <p>
|
||||
* Specifies the URL to send users to if login is required. If used with
|
||||
* {@link WebSecurityConfigurerAdapter} a default login page will be generated when
|
||||
* this attribute is not specified.
|
||||
* {@link EnableWebSecurity} a default login page will be generated when this
|
||||
* attribute is not specified.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a URL is specified or this is not being used in conjunction with
|
||||
* {@link WebSecurityConfigurerAdapter}, users are required to process the specified
|
||||
* URL to generate a login page.
|
||||
* {@link EnableWebSecurity}, users are required to process the specified URL to
|
||||
* generate a login page.
|
||||
* </p>
|
||||
*/
|
||||
protected T loginPage(String loginPage) {
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,7 +22,7 @@ import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;
|
||||
@@ -30,7 +30,7 @@ import org.springframework.security.web.csrf.CsrfToken;
|
||||
|
||||
/**
|
||||
* Adds a Filter that will generate a login page if one is not specified otherwise when
|
||||
* using {@link WebSecurityConfigurerAdapter}.
|
||||
* using {@link EnableWebSecurity}.
|
||||
*
|
||||
* <p>
|
||||
* By default an
|
||||
@@ -64,7 +64,7 @@ import org.springframework.security.web.csrf.CsrfToken;
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
* @see WebSecurityConfigurerAdapter
|
||||
* @see EnableWebSecurity
|
||||
*/
|
||||
public final class DefaultLoginPageConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
extends AbstractHttpConfigurer<DefaultLoginPageConfigurer<H>, H> {
|
||||
|
||||
+7
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +18,7 @@ package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.ForwardAuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.ForwardAuthenticationSuccessHandler;
|
||||
@@ -84,15 +84,15 @@ public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
|
||||
/**
|
||||
* <p>
|
||||
* Specifies the URL to send users to if login is required. If used with
|
||||
* {@link WebSecurityConfigurerAdapter} a default login page will be generated when
|
||||
* this attribute is not specified.
|
||||
* {@link EnableWebSecurity} a default login page will be generated when this
|
||||
* attribute is not specified.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a URL is specified or this is not being used in conjunction with
|
||||
* {@link WebSecurityConfigurerAdapter}, users are required to process the specified
|
||||
* URL to generate a login page. In general, the login page should create a form that
|
||||
* submits a request with the following requirements to work with
|
||||
* {@link EnableWebSecurity}, users are required to process the specified URL to
|
||||
* generate a login page. In general, the login page should create a form that submits
|
||||
* a request with the following requirements to work with
|
||||
* {@link UsernamePasswordAuthenticationFilter}:
|
||||
* </p>
|
||||
*
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,7 +26,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.web.header.HeaderWriter;
|
||||
import org.springframework.security.web.header.HeaderWriterFilter;
|
||||
import org.springframework.security.web.header.writers.CacheControlHeadersWriter;
|
||||
@@ -50,7 +50,7 @@ import org.springframework.util.Assert;
|
||||
/**
|
||||
* <p>
|
||||
* Adds the Security HTTP headers to the response. Security HTTP headers is activated by
|
||||
* default when using {@link WebSecurityConfigurerAdapter}'s default constructor.
|
||||
* default when using {@link EnableWebSecurity}'s default constructor.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
|
||||
+4
-9
@@ -22,10 +22,8 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.RememberMeAuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
@@ -150,13 +148,10 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
|
||||
/**
|
||||
* Specifies the {@link UserDetailsService} used to look up the {@link UserDetails}
|
||||
* when a remember me token is valid. The default is to use the
|
||||
* {@link UserDetailsService} found by invoking
|
||||
* {@link HttpSecurity#getSharedObject(Class)} which is set when using
|
||||
* {@link WebSecurityConfigurerAdapter#configure(AuthenticationManagerBuilder)}. When
|
||||
* using a {@link org.springframework.security.web.SecurityFilterChain} bean, the
|
||||
* default is to look for a {@link UserDetailsService} bean. Alternatively, one can
|
||||
* populate {@link #rememberMeServices(RememberMeServices)}.
|
||||
* when a remember me token is valid. When using a
|
||||
* {@link org.springframework.security.web.SecurityFilterChain} bean, the default is
|
||||
* to look for a {@link UserDetailsService} bean. Alternatively, one can populate
|
||||
* {@link #rememberMeServices(RememberMeServices)}.
|
||||
* @param userDetailsService the {@link UserDetailsService} to configure
|
||||
* @return the {@link RememberMeConfigurer} for further customization
|
||||
* @see AbstractRememberMeServices
|
||||
|
||||
+1
-6
@@ -24,13 +24,11 @@ import org.springframework.security.authentication.AuthenticationDetailsSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
||||
@@ -144,10 +142,7 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>>
|
||||
|
||||
/**
|
||||
* Specifies the {@link AuthenticationUserDetailsService} to use. If not specified,
|
||||
* the shared {@link UserDetailsService} will be used to create a
|
||||
* {@link UserDetailsByNameServiceWrapper}. If a {@link SecurityFilterChain} bean is
|
||||
* used instead of the {@link WebSecurityConfigurerAdapter}, then the
|
||||
* {@link UserDetailsService} bean will be used by default.
|
||||
* then the {@link UserDetailsService} bean will be used by default.
|
||||
* @param authenticationUserDetailsService the
|
||||
* {@link AuthenticationUserDetailsService} to use
|
||||
* @return the {@link X509Configurer} for further customizations
|
||||
|
||||
+17
@@ -34,6 +34,7 @@ import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequest
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -171,6 +172,8 @@ public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
|
||||
private AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;
|
||||
|
||||
private RedirectStrategy authorizationRedirectStrategy;
|
||||
|
||||
private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;
|
||||
|
||||
private AuthorizationCodeGrantConfigurer() {
|
||||
@@ -202,6 +205,17 @@ public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the redirect strategy for Authorization Endpoint redirect URI.
|
||||
* @param authorizationRedirectStrategy the redirect strategy
|
||||
* @return the {@link AuthorizationCodeGrantConfigurer} for further configuration
|
||||
*/
|
||||
public AuthorizationCodeGrantConfigurer authorizationRedirectStrategy(
|
||||
RedirectStrategy authorizationRedirectStrategy) {
|
||||
this.authorizationRedirectStrategy = authorizationRedirectStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the client used for requesting the access token credential from the Token
|
||||
* Endpoint.
|
||||
@@ -247,6 +261,9 @@ public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
authorizationRequestRedirectFilter
|
||||
.setAuthorizationRequestRepository(this.authorizationRequestRepository);
|
||||
}
|
||||
if (this.authorizationRedirectStrategy != null) {
|
||||
authorizationRequestRedirectFilter.setAuthorizationRedirectStrategy(this.authorizationRedirectStrategy);
|
||||
}
|
||||
RequestCache requestCache = builder.getSharedObject(RequestCache.class);
|
||||
if (requestCache != null) {
|
||||
authorizationRequestRedirectFilter.setRequestCache(requestCache);
|
||||
|
||||
+18
@@ -68,6 +68,7 @@ import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||
@@ -368,6 +369,10 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
authorizationRequestFilter
|
||||
.setAuthorizationRequestRepository(this.authorizationEndpointConfig.authorizationRequestRepository);
|
||||
}
|
||||
if (this.authorizationEndpointConfig.authorizationRedirectStrategy != null) {
|
||||
authorizationRequestFilter
|
||||
.setAuthorizationRedirectStrategy(this.authorizationEndpointConfig.authorizationRedirectStrategy);
|
||||
}
|
||||
RequestCache requestCache = http.getSharedObject(RequestCache.class);
|
||||
if (requestCache != null) {
|
||||
authorizationRequestFilter.setRequestCache(requestCache);
|
||||
@@ -540,6 +545,8 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
|
||||
private AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;
|
||||
|
||||
private RedirectStrategy authorizationRedirectStrategy;
|
||||
|
||||
private AuthorizationEndpointConfig() {
|
||||
}
|
||||
|
||||
@@ -582,6 +589,17 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the redirect strategy for Authorization Endpoint redirect URI.
|
||||
* @param authorizationRedirectStrategy the redirect strategy
|
||||
* @return the {@link AuthorizationEndpointConfig} for further configuration
|
||||
*/
|
||||
public AuthorizationEndpointConfig authorizationRedirectStrategy(
|
||||
RedirectStrategy authorizationRedirectStrategy) {
|
||||
this.authorizationRedirectStrategy = authorizationRedirectStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link OAuth2LoginConfigurer} for further configuration.
|
||||
* @return the {@link OAuth2LoginConfigurer}
|
||||
|
||||
+18
-18
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -31,7 +31,7 @@ import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.RememberMeConfigurer;
|
||||
@@ -61,29 +61,29 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
* <h2>Example Configuration</h2>
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* @Configuration
|
||||
* @EnableWebSecurity
|
||||
* public class OpenIDLoginConfig extends WebSecurityConfigurerAdapter {
|
||||
* public class OpenIDLoginConfig {
|
||||
*
|
||||
* @Override
|
||||
* protected void configure(HttpSecurity http) {
|
||||
* @Bean
|
||||
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
* http
|
||||
* .authorizeRequests()
|
||||
* .antMatchers("/**").hasRole("USER")
|
||||
* .and()
|
||||
* .openidLogin()
|
||||
* .permitAll();
|
||||
* return http.build();
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* protected void configure(AuthenticationManagerBuilder auth)(
|
||||
* AuthenticationManagerBuilder auth) throws Exception {
|
||||
* auth
|
||||
* .inMemoryAuthentication()
|
||||
* .withUser("https://www.google.com/accounts/o8/id?id=lmkCn9xzPdsxVwG7pjYMuDgNNdASFmobNkcRPaWU")
|
||||
* .password("password")
|
||||
* .roles("USER");
|
||||
* @Bean
|
||||
* public UserDetailsService userDetailsService() {
|
||||
* UserDetails user = User.withDefaultPasswordEncoder()
|
||||
* .username("https://www.google.com/accounts/o8/id?id=lmkCn9xzPdsxVwG7pjYMuDgNNdASFmobNkcRPaWU")
|
||||
* .password("password")
|
||||
* .roles("USER")
|
||||
* .build();
|
||||
* return new InMemoryUserDetailsManager(user);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
@@ -229,14 +229,14 @@ public final class OpenIDLoginConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
/**
|
||||
* <p>
|
||||
* Specifies the URL to send users to if login is required. If used with
|
||||
* {@link WebSecurityConfigurerAdapter} a default login page will be generated when
|
||||
* this attribute is not specified.
|
||||
* {@link EnableWebSecurity} a default login page will be generated when this
|
||||
* attribute is not specified.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a URL is specified or this is not being used in conjunction with
|
||||
* {@link WebSecurityConfigurerAdapter}, users are required to process the specified
|
||||
* URL to generate a login page.
|
||||
* {@link EnableWebSecurity}, users are required to process the specified URL to
|
||||
* generate a login page.
|
||||
* </p>
|
||||
*
|
||||
* <ul>
|
||||
|
||||
+23
-2
@@ -47,10 +47,16 @@ import org.springframework.security.saml2.provider.service.web.Saml2Authenticati
|
||||
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;
|
||||
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -252,8 +258,7 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
this.updateAuthenticationDefaults();
|
||||
this.updateAccessDefaults(http);
|
||||
String loginUrl = providerUrlMap.entrySet().iterator().next().getKey();
|
||||
final LoginUrlAuthenticationEntryPoint entryPoint = new LoginUrlAuthenticationEntryPoint(loginUrl);
|
||||
registerAuthenticationEntryPoint(http, entryPoint);
|
||||
registerAuthenticationEntryPoint(http, getLoginEntryPoint(http, loginUrl));
|
||||
}
|
||||
else {
|
||||
super.init(http);
|
||||
@@ -295,6 +300,22 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
return this.relyingPartyRegistrationRepository;
|
||||
}
|
||||
|
||||
private AuthenticationEntryPoint getLoginEntryPoint(B http, String providerLoginPage) {
|
||||
RequestMatcher loginPageMatcher = new AntPathRequestMatcher(this.getLoginPage());
|
||||
RequestMatcher faviconMatcher = new AntPathRequestMatcher("/favicon.ico");
|
||||
RequestMatcher defaultEntryPointMatcher = this.getAuthenticationEntryPointMatcher(http);
|
||||
RequestMatcher defaultLoginPageMatcher = new AndRequestMatcher(
|
||||
new OrRequestMatcher(loginPageMatcher, faviconMatcher), defaultEntryPointMatcher);
|
||||
RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
|
||||
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
|
||||
LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints = new LinkedHashMap<>();
|
||||
entryPoints.put(new AndRequestMatcher(notXRequestedWith, new NegatedRequestMatcher(defaultLoginPageMatcher)),
|
||||
new LoginUrlAuthenticationEntryPoint(providerLoginPage));
|
||||
DelegatingAuthenticationEntryPoint loginEntryPoint = new DelegatingAuthenticationEntryPoint(entryPoints);
|
||||
loginEntryPoint.setDefaultEntryPoint(this.getAuthenticationEntryPoint());
|
||||
return loginEntryPoint;
|
||||
}
|
||||
|
||||
private void setAuthenticationRequestRepository(B http,
|
||||
Saml2WebSsoAuthenticationFilter saml2WebSsoAuthenticationFilter) {
|
||||
saml2WebSsoAuthenticationFilter.setAuthenticationRequestRepository(getAuthenticationRequestRepository(http));
|
||||
|
||||
+14
@@ -44,6 +44,8 @@ final class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private static final String ATT_AUTHORIZATION_REQUEST_RESOLVER_REF = "authorization-request-resolver-ref";
|
||||
|
||||
private static final String ATT_AUTHORIZATION_REDIRECT_STRATEGY_REF = "authorization-redirect-strategy-ref";
|
||||
|
||||
private static final String ATT_ACCESS_TOKEN_RESPONSE_CLIENT_REF = "access-token-response-client-ref";
|
||||
|
||||
private final BeanReference requestCache;
|
||||
@@ -87,6 +89,7 @@ final class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser {
|
||||
}
|
||||
BeanMetadataElement authorizationRequestRepository = getAuthorizationRequestRepository(
|
||||
authorizationCodeGrantElt);
|
||||
BeanMetadataElement authorizationRedirectStrategy = getAuthorizationRedirectStrategy(authorizationCodeGrantElt);
|
||||
BeanDefinitionBuilder authorizationRequestRedirectFilterBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(OAuth2AuthorizationRequestRedirectFilter.class);
|
||||
String authorizationRequestResolverRef = (authorizationCodeGrantElt != null)
|
||||
@@ -99,6 +102,7 @@ final class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser {
|
||||
}
|
||||
this.authorizationRequestRedirectFilter = authorizationRequestRedirectFilterBuilder
|
||||
.addPropertyValue("authorizationRequestRepository", authorizationRequestRepository)
|
||||
.addPropertyValue("authorizationRedirectStrategy", authorizationRedirectStrategy)
|
||||
.addPropertyValue("requestCache", this.requestCache).getBeanDefinition();
|
||||
BeanDefinitionBuilder authorizationCodeGrantFilterBldr = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(OAuth2AuthorizationCodeGrantFilter.class)
|
||||
@@ -130,6 +134,16 @@ final class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser {
|
||||
.getBeanDefinition();
|
||||
}
|
||||
|
||||
private BeanMetadataElement getAuthorizationRedirectStrategy(Element element) {
|
||||
String authorizationRedirectStrategyRef = (element != null)
|
||||
? element.getAttribute(ATT_AUTHORIZATION_REDIRECT_STRATEGY_REF) : null;
|
||||
if (StringUtils.hasText(authorizationRedirectStrategyRef)) {
|
||||
return new RuntimeBeanReference(authorizationRedirectStrategyRef);
|
||||
}
|
||||
return BeanDefinitionBuilder.rootBeanDefinition("org.springframework.security.web.DefaultRedirectStrategy")
|
||||
.getBeanDefinition();
|
||||
}
|
||||
|
||||
private BeanMetadataElement getAccessTokenResponseClient(Element element) {
|
||||
String accessTokenResponseClientRef = (element != null)
|
||||
? element.getAttribute(ATT_ACCESS_TOKEN_RESPONSE_CLIENT_REF) : null;
|
||||
|
||||
+12
@@ -87,6 +87,8 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private static final String ATT_AUTHORIZATION_REQUEST_RESOLVER_REF = "authorization-request-resolver-ref";
|
||||
|
||||
private static final String ATT_AUTHORIZATION_REDIRECT_STRATEGY_REF = "authorization-redirect-strategy-ref";
|
||||
|
||||
private static final String ATT_ACCESS_TOKEN_RESPONSE_CLIENT_REF = "access-token-response-client-ref";
|
||||
|
||||
private static final String ATT_USER_AUTHORITIES_MAPPER_REF = "user-authorities-mapper-ref";
|
||||
@@ -203,6 +205,7 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser {
|
||||
}
|
||||
oauth2AuthorizationRequestRedirectFilterBuilder
|
||||
.addPropertyValue("authorizationRequestRepository", authorizationRequestRepository)
|
||||
.addPropertyValue("authorizationRedirectStrategy", getAuthorizationRedirectStrategy(element))
|
||||
.addPropertyValue("requestCache", this.requestCache);
|
||||
this.oauth2AuthorizationRequestRedirectFilter = oauth2AuthorizationRequestRedirectFilterBuilder
|
||||
.getBeanDefinition();
|
||||
@@ -267,6 +270,15 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser {
|
||||
.getBeanDefinition();
|
||||
}
|
||||
|
||||
private BeanMetadataElement getAuthorizationRedirectStrategy(Element element) {
|
||||
String authorizationRedirectStrategyRef = element.getAttribute(ATT_AUTHORIZATION_REDIRECT_STRATEGY_REF);
|
||||
if (StringUtils.hasText(authorizationRedirectStrategyRef)) {
|
||||
return new RuntimeBeanReference(authorizationRedirectStrategyRef);
|
||||
}
|
||||
return BeanDefinitionBuilder.rootBeanDefinition("org.springframework.security.web.DefaultRedirectStrategy")
|
||||
.getBeanDefinition();
|
||||
}
|
||||
|
||||
private BeanDefinition getOidcAuthProvider(Element element, BeanMetadataElement accessTokenResponseClient,
|
||||
String userAuthoritiesMapperRef) {
|
||||
boolean oidcAuthenticationProviderEnabled = ClassUtils
|
||||
|
||||
+46
@@ -102,12 +102,14 @@ import org.springframework.security.oauth2.server.resource.web.server.ServerBear
|
||||
import org.springframework.security.web.PortMapper;
|
||||
import org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor;
|
||||
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
|
||||
import org.springframework.security.web.server.DefaultServerRedirectStrategy;
|
||||
import org.springframework.security.web.server.DelegatingServerAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.server.DelegatingServerAuthenticationEntryPoint.DelegateEntry;
|
||||
import org.springframework.security.web.server.ExchangeMatcherRedirectWebFilter;
|
||||
import org.springframework.security.web.server.MatcherSecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.server.ServerRedirectStrategy;
|
||||
import org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilter;
|
||||
import org.springframework.security.web.server.authentication.AuthenticationConverterServerWebExchangeMatcher;
|
||||
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
|
||||
@@ -3375,6 +3377,8 @@ public class ServerHttpSecurity {
|
||||
|
||||
private ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver;
|
||||
|
||||
private ServerRedirectStrategy authorizationRedirectStrategy;
|
||||
|
||||
private ServerWebExchangeMatcher authenticationMatcher;
|
||||
|
||||
private ServerAuthenticationSuccessHandler authenticationSuccessHandler;
|
||||
@@ -3547,6 +3551,16 @@ public class ServerHttpSecurity {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the redirect strategy for Authorization Endpoint redirect URI.
|
||||
* @param authorizationRedirectStrategy the redirect strategy
|
||||
* @return the {@link OAuth2LoginSpec} for further configuration
|
||||
*/
|
||||
public OAuth2LoginSpec authorizationRedirectStrategy(ServerRedirectStrategy authorizationRedirectStrategy) {
|
||||
this.authorizationRedirectStrategy = authorizationRedirectStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link ServerWebExchangeMatcher matcher} used for determining if the
|
||||
* request is an authentication request.
|
||||
@@ -3581,7 +3595,9 @@ public class ServerHttpSecurity {
|
||||
OAuth2AuthorizationRequestRedirectWebFilter oauthRedirectFilter = getRedirectWebFilter();
|
||||
ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = getAuthorizationRequestRepository();
|
||||
oauthRedirectFilter.setAuthorizationRequestRepository(authorizationRequestRepository);
|
||||
oauthRedirectFilter.setAuthorizationRedirectStrategy(getAuthorizationRedirectStrategy());
|
||||
oauthRedirectFilter.setRequestCache(http.requestCache.requestCache);
|
||||
|
||||
ReactiveAuthenticationManager manager = getAuthenticationManager();
|
||||
AuthenticationWebFilter authenticationFilter = new OAuth2LoginAuthenticationWebFilter(manager,
|
||||
authorizedClientRepository);
|
||||
@@ -3591,6 +3607,7 @@ public class ServerHttpSecurity {
|
||||
authenticationFilter.setAuthenticationSuccessHandler(getAuthenticationSuccessHandler(http));
|
||||
authenticationFilter.setAuthenticationFailureHandler(getAuthenticationFailureHandler());
|
||||
authenticationFilter.setSecurityContextRepository(this.securityContextRepository);
|
||||
|
||||
setDefaultEntryPoints(http);
|
||||
http.addFilterAt(oauthRedirectFilter, SecurityWebFiltersOrder.HTTP_BASIC);
|
||||
http.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.AUTHENTICATION);
|
||||
@@ -3737,6 +3754,13 @@ public class ServerHttpSecurity {
|
||||
return this.authorizationRequestRepository;
|
||||
}
|
||||
|
||||
private ServerRedirectStrategy getAuthorizationRedirectStrategy() {
|
||||
if (this.authorizationRedirectStrategy == null) {
|
||||
this.authorizationRedirectStrategy = new DefaultServerRedirectStrategy();
|
||||
}
|
||||
return this.authorizationRedirectStrategy;
|
||||
}
|
||||
|
||||
private ReactiveOAuth2AuthorizedClientService getAuthorizedClientService() {
|
||||
ReactiveOAuth2AuthorizedClientService bean = getBeanOrNull(ReactiveOAuth2AuthorizedClientService.class);
|
||||
if (bean != null) {
|
||||
@@ -3759,6 +3783,8 @@ public class ServerHttpSecurity {
|
||||
|
||||
private ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;
|
||||
|
||||
private ServerRedirectStrategy authorizationRedirectStrategy;
|
||||
|
||||
private OAuth2ClientSpec() {
|
||||
}
|
||||
|
||||
@@ -3851,6 +3877,23 @@ public class ServerHttpSecurity {
|
||||
return this.authorizationRequestRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the redirect strategy for Authorization Endpoint redirect URI.
|
||||
* @param authorizationRedirectStrategy the redirect strategy
|
||||
* @return the {@link OAuth2ClientSpec} for further configuration
|
||||
*/
|
||||
public OAuth2ClientSpec authorizationRedirectStrategy(ServerRedirectStrategy authorizationRedirectStrategy) {
|
||||
this.authorizationRedirectStrategy = authorizationRedirectStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
private ServerRedirectStrategy getAuthorizationRedirectStrategy() {
|
||||
if (this.authorizationRedirectStrategy == null) {
|
||||
this.authorizationRedirectStrategy = new DefaultServerRedirectStrategy();
|
||||
}
|
||||
return this.authorizationRedirectStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows method chaining to continue configuring the {@link ServerHttpSecurity}
|
||||
* @return the {@link ServerHttpSecurity} to continue configuring
|
||||
@@ -3870,12 +3913,15 @@ public class ServerHttpSecurity {
|
||||
if (http.requestCache != null) {
|
||||
codeGrantWebFilter.setRequestCache(http.requestCache.requestCache);
|
||||
}
|
||||
|
||||
OAuth2AuthorizationRequestRedirectWebFilter oauthRedirectFilter = new OAuth2AuthorizationRequestRedirectWebFilter(
|
||||
clientRegistrationRepository);
|
||||
oauthRedirectFilter.setAuthorizationRequestRepository(getAuthorizationRequestRepository());
|
||||
oauthRedirectFilter.setAuthorizationRedirectStrategy(getAuthorizationRedirectStrategy());
|
||||
if (http.requestCache != null) {
|
||||
oauthRedirectFilter.setRequestCache(http.requestCache.requestCache);
|
||||
}
|
||||
|
||||
http.addFilterAt(codeGrantWebFilter, SecurityWebFiltersOrder.OAUTH2_AUTHORIZATION_CODE);
|
||||
http.addFilterAt(oauthRedirectFilter, SecurityWebFiltersOrder.HTTP_BASIC);
|
||||
}
|
||||
|
||||
+4
@@ -22,6 +22,7 @@ import org.springframework.security.oauth2.client.registration.ReactiveClientReg
|
||||
import org.springframework.security.oauth2.client.web.server.ServerAuthorizationRequestRepository
|
||||
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest
|
||||
import org.springframework.security.web.server.ServerRedirectStrategy
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter
|
||||
import org.springframework.web.server.ServerWebExchange
|
||||
|
||||
@@ -37,6 +38,7 @@ import org.springframework.web.server.ServerWebExchange
|
||||
* @property clientRegistrationRepository the repository of client registrations.
|
||||
* @property authorizedClientRepository the repository for authorized client(s).
|
||||
* @property authorizationRequestRepository the repository to use for storing [OAuth2AuthorizationRequest]s.
|
||||
* @property authorizationRedirectStrategy the redirect strategy for Authorization Endpoint redirect URI.
|
||||
*/
|
||||
@ServerSecurityMarker
|
||||
class ServerOAuth2ClientDsl {
|
||||
@@ -45,6 +47,7 @@ class ServerOAuth2ClientDsl {
|
||||
var clientRegistrationRepository: ReactiveClientRegistrationRepository? = null
|
||||
var authorizedClientRepository: ServerOAuth2AuthorizedClientRepository? = null
|
||||
var authorizationRequestRepository: ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest>? = null
|
||||
var authorizationRedirectStrategy: ServerRedirectStrategy? = null
|
||||
|
||||
internal fun get(): (ServerHttpSecurity.OAuth2ClientSpec) -> Unit {
|
||||
return { oauth2Client ->
|
||||
@@ -53,6 +56,7 @@ class ServerOAuth2ClientDsl {
|
||||
clientRegistrationRepository?.also { oauth2Client.clientRegistrationRepository(clientRegistrationRepository) }
|
||||
authorizedClientRepository?.also { oauth2Client.authorizedClientRepository(authorizedClientRepository) }
|
||||
authorizationRequestRepository?.also { oauth2Client.authorizationRequestRepository(authorizationRequestRepository) }
|
||||
authorizationRedirectStrategy?.also { oauth2Client.authorizationRedirectStrategy(authorizationRedirectStrategy) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -24,6 +24,7 @@ import org.springframework.security.oauth2.client.web.server.ServerAuthorization
|
||||
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizationRequestResolver
|
||||
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest
|
||||
import org.springframework.security.web.server.ServerRedirectStrategy
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler
|
||||
@@ -49,6 +50,7 @@ import org.springframework.web.server.ServerWebExchange
|
||||
* @property authorizedClientRepository the repository for authorized client(s).
|
||||
* @property authorizationRequestRepository the repository to use for storing [OAuth2AuthorizationRequest]s.
|
||||
* @property authorizationRequestResolver the resolver used for resolving [OAuth2AuthorizationRequest]s.
|
||||
* @property authorizationRedirectStrategy the redirect strategy for Authorization Endpoint redirect URI.
|
||||
* @property authenticationMatcher the [ServerWebExchangeMatcher] used for determining if the request is an
|
||||
* authentication request.
|
||||
*/
|
||||
@@ -64,6 +66,7 @@ class ServerOAuth2LoginDsl {
|
||||
var authorizedClientRepository: ServerOAuth2AuthorizedClientRepository? = null
|
||||
var authorizationRequestRepository: ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest>? = null
|
||||
var authorizationRequestResolver: ServerOAuth2AuthorizationRequestResolver? = null
|
||||
var authorizationRedirectStrategy: ServerRedirectStrategy? = null
|
||||
var authenticationMatcher: ServerWebExchangeMatcher? = null
|
||||
|
||||
internal fun get(): (ServerHttpSecurity.OAuth2LoginSpec) -> Unit {
|
||||
@@ -78,6 +81,7 @@ class ServerOAuth2LoginDsl {
|
||||
authorizedClientRepository?.also { oauth2Login.authorizedClientRepository(authorizedClientRepository) }
|
||||
authorizationRequestRepository?.also { oauth2Login.authorizationRequestRepository(authorizationRequestRepository) }
|
||||
authorizationRequestResolver?.also { oauth2Login.authorizationRequestResolver(authorizationRequestResolver) }
|
||||
authorizationRedirectStrategy?.also { oauth2Login.authorizationRedirectStrategy(authorizationRedirectStrategy) }
|
||||
authenticationMatcher?.also { oauth2Login.authenticationMatcher(authenticationMatcher) }
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -23,6 +23,7 @@ import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCo
|
||||
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest
|
||||
import org.springframework.security.web.RedirectStrategy
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure OAuth 2.0 Authorization Code Grant.
|
||||
@@ -31,6 +32,7 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequ
|
||||
* @since 5.3
|
||||
* @property authorizationRequestResolver the resolver used for resolving [OAuth2AuthorizationRequest]'s.
|
||||
* @property authorizationRequestRepository the repository used for storing [OAuth2AuthorizationRequest]'s.
|
||||
* @property authorizationRedirectStrategy the redirect strategy for Authorization Endpoint redirect URI.
|
||||
* @property accessTokenResponseClient the client used for requesting the access token credential
|
||||
* from the Token Endpoint.
|
||||
*/
|
||||
@@ -38,12 +40,14 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequ
|
||||
class AuthorizationCodeGrantDsl {
|
||||
var authorizationRequestResolver: OAuth2AuthorizationRequestResolver? = null
|
||||
var authorizationRequestRepository: AuthorizationRequestRepository<OAuth2AuthorizationRequest>? = null
|
||||
var authorizationRedirectStrategy: RedirectStrategy? = null
|
||||
var accessTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest>? = null
|
||||
|
||||
internal fun get(): (OAuth2ClientConfigurer<HttpSecurity>.AuthorizationCodeGrantConfigurer) -> Unit {
|
||||
return { authorizationCodeGrant ->
|
||||
authorizationRequestResolver?.also { authorizationCodeGrant.authorizationRequestResolver(authorizationRequestResolver) }
|
||||
authorizationRequestRepository?.also { authorizationCodeGrant.authorizationRequestRepository(authorizationRequestRepository) }
|
||||
authorizationRedirectStrategy?.also { authorizationCodeGrant.authorizationRedirectStrategy(authorizationRedirectStrategy) }
|
||||
accessTokenResponseClient?.also { authorizationCodeGrant.accessTokenResponseClient(accessTokenResponseClient) }
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -21,6 +21,7 @@ import org.springframework.security.config.annotation.web.configurers.oauth2.cli
|
||||
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest
|
||||
import org.springframework.security.web.RedirectStrategy
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure the Authorization Server's Authorization Endpoint using
|
||||
@@ -31,18 +32,21 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequ
|
||||
* @property baseUri the base URI used for authorization requests.
|
||||
* @property authorizationRequestResolver the resolver used for resolving [OAuth2AuthorizationRequest]'s.
|
||||
* @property authorizationRequestRepository the repository used for storing [OAuth2AuthorizationRequest]'s.
|
||||
* @property authorizationRedirectStrategy the redirect strategy for Authorization Endpoint redirect URI.
|
||||
*/
|
||||
@OAuth2LoginSecurityMarker
|
||||
class AuthorizationEndpointDsl {
|
||||
var baseUri: String? = null
|
||||
var authorizationRequestResolver: OAuth2AuthorizationRequestResolver? = null
|
||||
var authorizationRequestRepository: AuthorizationRequestRepository<OAuth2AuthorizationRequest>? = null
|
||||
var authorizationRedirectStrategy: RedirectStrategy? = null
|
||||
|
||||
internal fun get(): (OAuth2LoginConfigurer<HttpSecurity>.AuthorizationEndpointConfig) -> Unit {
|
||||
return { authorizationEndpoint ->
|
||||
baseUri?.also { authorizationEndpoint.baseUri(baseUri) }
|
||||
authorizationRequestResolver?.also { authorizationEndpoint.authorizationRequestResolver(authorizationRequestResolver) }
|
||||
authorizationRequestRepository?.also { authorizationEndpoint.authorizationRequestRepository(authorizationRequestRepository) }
|
||||
authorizationRedirectStrategy?.also { authorizationEndpoint.authorizationRedirectStrategy(authorizationRedirectStrategy) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,6 +500,9 @@ oauth2-login.attlist &=
|
||||
oauth2-login.attlist &=
|
||||
## Reference to the OAuth2AuthorizationRequestResolver
|
||||
attribute authorization-request-resolver-ref {xsd:token}?
|
||||
oauth2-login.attlist &=
|
||||
## Reference to the authorization RedirectStrategy
|
||||
attribute authorization-redirect-strategy-ref {xsd:token}?
|
||||
oauth2-login.attlist &=
|
||||
## Reference to the OAuth2AccessTokenResponseClient
|
||||
attribute access-token-response-client-ref {xsd:token}?
|
||||
@@ -547,6 +550,9 @@ authorization-code-grant =
|
||||
authorization-code-grant.attlist &=
|
||||
## Reference to the AuthorizationRequestRepository
|
||||
attribute authorization-request-repository-ref {xsd:token}?
|
||||
authorization-code-grant.attlist &=
|
||||
## Reference to the authorization RedirectStrategy
|
||||
attribute authorization-redirect-strategy-ref {xsd:token}?
|
||||
authorization-code-grant.attlist &=
|
||||
## Reference to the OAuth2AuthorizationRequestResolver
|
||||
attribute authorization-request-resolver-ref {xsd:token}?
|
||||
|
||||
@@ -1651,6 +1651,12 @@
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="authorization-redirect-strategy-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Reference to the authorization RedirectStrategy
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="access-token-response-client-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Reference to the OAuth2AccessTokenResponseClient
|
||||
@@ -1754,6 +1760,12 @@
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="authorization-redirect-strategy-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Reference to the authorization RedirectStrategy
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="authorization-request-resolver-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Reference to the OAuth2AuthorizationRequestResolver
|
||||
|
||||
+20
@@ -59,6 +59,8 @@ import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.security.web.DefaultRedirectStrategy;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
@@ -69,6 +71,7 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -96,6 +99,8 @@ public class OAuth2ClientConfigurerTests {
|
||||
|
||||
private static OAuth2AuthorizationRequestResolver authorizationRequestResolver;
|
||||
|
||||
private static RedirectStrategy authorizationRedirectStrategy;
|
||||
|
||||
private static OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;
|
||||
|
||||
private static RequestCache requestCache;
|
||||
@@ -131,6 +136,7 @@ public class OAuth2ClientConfigurerTests {
|
||||
authorizedClientService);
|
||||
authorizationRequestResolver = new DefaultOAuth2AuthorizationRequestResolver(clientRegistrationRepository,
|
||||
"/oauth2/authorization");
|
||||
authorizationRedirectStrategy = new DefaultRedirectStrategy();
|
||||
OAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken("access-token-1234")
|
||||
.tokenType(OAuth2AccessToken.TokenType.BEARER).expiresIn(300).build();
|
||||
accessTokenResponseClient = mock(OAuth2AccessTokenResponseClient.class);
|
||||
@@ -262,6 +268,19 @@ public class OAuth2ClientConfigurerTests {
|
||||
verify(authorizationRequestResolver).resolve(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenCustomAuthorizationRedirectStrategySetThenAuthorizationRedirectStrategyUsed()
|
||||
throws Exception {
|
||||
authorizationRedirectStrategy = mock(RedirectStrategy.class);
|
||||
this.spring.register(OAuth2ClientConfig.class).autowire();
|
||||
// @formatter:off
|
||||
this.mockMvc.perform(get("/oauth2/authorization/registration-1"))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn();
|
||||
// @formatter:on
|
||||
verify(authorizationRedirectStrategy).sendRedirect(any(), any(), anyString());
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@EnableWebMvc
|
||||
static class OAuth2ClientConfig extends WebSecurityConfigurerAdapter {
|
||||
@@ -279,6 +298,7 @@ public class OAuth2ClientConfigurerTests {
|
||||
.oauth2Client()
|
||||
.authorizationCodeGrant()
|
||||
.authorizationRequestResolver(authorizationRequestResolver)
|
||||
.authorizationRedirectStrategy(authorizationRedirectStrategy)
|
||||
.accessTokenResponseClient(accessTokenResponseClient);
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
+82
@@ -90,6 +90,7 @@ import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
|
||||
import org.springframework.security.oauth2.jwt.TestJwts;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
|
||||
import org.springframework.security.web.context.HttpRequestResponseHolder;
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
@@ -101,7 +102,9 @@ import org.springframework.web.context.support.AnnotationConfigWebApplicationCon
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -382,6 +385,32 @@ public class OAuth2LoginConfigurerTests {
|
||||
"https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=clientId&scope=openid+profile+email&state=state&redirect_uri=http%3A%2F%2Flocalhost%2Flogin%2Foauth2%2Fcode%2Fgoogle&custom-param1=custom-value1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oauth2LoginWithAuthorizationRedirectStrategyThenCustomAuthorizationRedirectStrategyUsed()
|
||||
throws Exception {
|
||||
loadConfig(OAuth2LoginConfigCustomAuthorizationRedirectStrategy.class);
|
||||
RedirectStrategy redirectStrategy = this.context
|
||||
.getBean(OAuth2LoginConfigCustomAuthorizationRedirectStrategy.class).redirectStrategy;
|
||||
String requestUri = "/oauth2/authorization/google";
|
||||
this.request = new MockHttpServletRequest("GET", requestUri);
|
||||
this.request.setServletPath(requestUri);
|
||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);
|
||||
then(redirectStrategy).should().sendRedirect(any(), any(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenOauth2LoginWithCustomAuthorizationRedirectStrategyThenCustomAuthorizationRedirectStrategyUsed()
|
||||
throws Exception {
|
||||
loadConfig(OAuth2LoginConfigCustomAuthorizationRedirectStrategyInLambda.class);
|
||||
RedirectStrategy redirectStrategy = this.context
|
||||
.getBean(OAuth2LoginConfigCustomAuthorizationRedirectStrategyInLambda.class).redirectStrategy;
|
||||
String requestUri = "/oauth2/authorization/google";
|
||||
this.request = new MockHttpServletRequest("GET", requestUri);
|
||||
this.request.setServletPath(requestUri);
|
||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);
|
||||
then(redirectStrategy).should().sendRedirect(any(), any(), anyString());
|
||||
}
|
||||
|
||||
// gh-5347
|
||||
@Test
|
||||
public void oauth2LoginWithOneClientConfiguredThenRedirectForAuthorization() throws Exception {
|
||||
@@ -883,6 +912,59 @@ public class OAuth2LoginConfigurerTests {
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class OAuth2LoginConfigCustomAuthorizationRedirectStrategy extends CommonWebSecurityConfigurerAdapter {
|
||||
|
||||
private final ClientRegistrationRepository clientRegistrationRepository = new InMemoryClientRegistrationRepository(
|
||||
GOOGLE_CLIENT_REGISTRATION);
|
||||
|
||||
RedirectStrategy redirectStrategy = mock(RedirectStrategy.class);
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.oauth2Login((oauth2Login) ->
|
||||
oauth2Login
|
||||
.clientRegistrationRepository(this.clientRegistrationRepository)
|
||||
.authorizationEndpoint((authorizationEndpoint) ->
|
||||
authorizationEndpoint
|
||||
.authorizationRedirectStrategy(this.redirectStrategy)
|
||||
)
|
||||
);
|
||||
// @formatter:on
|
||||
super.configure(http);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class OAuth2LoginConfigCustomAuthorizationRedirectStrategyInLambda
|
||||
extends CommonLambdaWebSecurityConfigurerAdapter {
|
||||
|
||||
private final ClientRegistrationRepository clientRegistrationRepository = new InMemoryClientRegistrationRepository(
|
||||
GOOGLE_CLIENT_REGISTRATION);
|
||||
|
||||
RedirectStrategy redirectStrategy = mock(RedirectStrategy.class);
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.oauth2Login((oauth2Login) ->
|
||||
oauth2Login
|
||||
.clientRegistrationRepository(this.clientRegistrationRepository)
|
||||
.authorizationEndpoint((authorizationEndpoint) ->
|
||||
authorizationEndpoint
|
||||
.authorizationRedirectStrategy(this.redirectStrategy)
|
||||
)
|
||||
);
|
||||
// @formatter:on
|
||||
super.configure(http);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class OAuth2LoginConfigMultipleClients extends CommonWebSecurityConfigurerAdapter {
|
||||
|
||||
|
||||
+16
-4
@@ -43,6 +43,7 @@ import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
@@ -82,6 +83,7 @@ import org.springframework.security.saml2.provider.service.authentication.Saml2A
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken;
|
||||
import org.springframework.security.saml2.provider.service.authentication.TestOpenSamlObjects;
|
||||
import org.springframework.security.saml2.provider.service.authentication.TestSaml2AuthenticationRequestContexts;
|
||||
import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository;
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
|
||||
import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;
|
||||
@@ -113,10 +115,10 @@ import org.springframework.web.util.UriComponentsBuilder;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
@@ -411,6 +413,16 @@ public class Saml2LoginConfigurerTests {
|
||||
verify(authenticationConverter).convert(any(HttpServletRequest.class));
|
||||
}
|
||||
|
||||
// gh-11657
|
||||
@Test
|
||||
public void getFaviconWhenDefaultConfigurationThenDoesNotSaveAuthnRequest() throws Exception {
|
||||
this.spring.register(Saml2LoginConfig.class).autowire();
|
||||
this.mvc.perform(get("/favicon.ico").accept(MediaType.TEXT_HTML)).andExpect(status().isFound())
|
||||
.andExpect(redirectedUrl("http://localhost/login"));
|
||||
this.mvc.perform(get("/").accept(MediaType.TEXT_HTML)).andExpect(status().isFound())
|
||||
.andExpect(redirectedUrl("http://localhost/saml2/authenticate/registration-id"));
|
||||
}
|
||||
|
||||
private void validateSaml2WebSsoAuthenticationFilterConfiguration() {
|
||||
// get the OpenSamlAuthenticationProvider
|
||||
Saml2WebSsoAuthenticationFilter filter = getSaml2SsoFilter(this.springSecurityFilterChain);
|
||||
@@ -429,6 +441,8 @@ public class Saml2LoginConfigurerTests {
|
||||
|
||||
private void performSaml2Login(String expected) throws IOException, ServletException {
|
||||
// setup authentication parameters
|
||||
this.request.setRequestURI("/login/saml2/sso/registration-id");
|
||||
this.request.setServletPath("/login/saml2/sso/registration-id");
|
||||
this.request.setParameter("SAMLResponse",
|
||||
Base64.getEncoder().encodeToString("saml2-xml-response-object".getBytes()));
|
||||
// perform test
|
||||
@@ -821,9 +835,7 @@ public class Saml2LoginConfigurerTests {
|
||||
.assertingPartyDetails((party) -> party.verificationX509Credentials(
|
||||
(c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential())))
|
||||
.build();
|
||||
RelyingPartyRegistrationRepository repository = mock(RelyingPartyRegistrationRepository.class);
|
||||
given(repository.findByRegistrationId(anyString())).willReturn(registration);
|
||||
return repository;
|
||||
return spy(new InMemoryRelyingPartyRegistrationRepository(registration));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+15
@@ -44,6 +44,7 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;
|
||||
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
@@ -55,6 +56,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
@@ -90,6 +92,9 @@ public class OAuth2ClientBeanDefinitionParserTests {
|
||||
@Autowired(required = false)
|
||||
private OAuth2AuthorizationRequestResolver authorizationRequestResolver;
|
||||
|
||||
@Autowired(required = false)
|
||||
private RedirectStrategy authorizationRedirectStrategy;
|
||||
|
||||
@Autowired(required = false)
|
||||
private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;
|
||||
|
||||
@@ -148,6 +153,16 @@ public class OAuth2ClientBeanDefinitionParserTests {
|
||||
verify(this.authorizationRequestResolver).resolve(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenCustomAuthorizationRedirectStrategyThenCalled() throws Exception {
|
||||
this.spring.configLocations(xml("CustomAuthorizationRedirectStrategy")).autowire();
|
||||
// @formatter:off
|
||||
this.mvc.perform(get("/oauth2/authorization/google"))
|
||||
.andExpect(status().isOk());
|
||||
// @formatter:on
|
||||
verify(this.authorizationRedirectStrategy).sendRedirect(any(), any(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenAuthorizationResponseMatchThenProcess() throws Exception {
|
||||
this.spring.configLocations(xml("CustomConfiguration")).autowire();
|
||||
|
||||
+16
@@ -64,6 +64,7 @@ import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
|
||||
import org.springframework.security.oauth2.jwt.TestJwts;
|
||||
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
@@ -78,6 +79,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.times;
|
||||
@@ -118,6 +120,9 @@ public class OAuth2LoginBeanDefinitionParserTests {
|
||||
@Autowired(required = false)
|
||||
private OAuth2AuthorizationRequestResolver authorizationRequestResolver;
|
||||
|
||||
@Autowired(required = false)
|
||||
private RedirectStrategy authorizationRedirectStrategy;
|
||||
|
||||
@Autowired(required = false)
|
||||
private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;
|
||||
|
||||
@@ -378,6 +383,17 @@ public class OAuth2LoginBeanDefinitionParserTests {
|
||||
verify(this.authorizationRequestResolver).resolve(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenCustomAuthorizationRedirectStrategyThenCalled() throws Exception {
|
||||
this.spring.configLocations(this.xml("SingleClientRegistration-WithCustomAuthorizationRedirectStrategy"))
|
||||
.autowire();
|
||||
// @formatter:off
|
||||
this.mvc.perform(get("/oauth2/authorization/google-login"))
|
||||
.andExpect(status().isOk());
|
||||
// @formatter:on
|
||||
verify(this.authorizationRedirectStrategy).sendRedirect(any(), any(), anyString());
|
||||
}
|
||||
|
||||
// gh-5347
|
||||
@Test
|
||||
public void requestWhenMultiClientRegistrationThenRedirectDefaultLoginPage() throws Exception {
|
||||
|
||||
+89
@@ -39,14 +39,18 @@ import org.springframework.security.config.annotation.web.reactive.ServerHttpSec
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
|
||||
import org.springframework.security.oauth2.client.web.server.OAuth2AuthorizationRequestRedirectWebFilter;
|
||||
import org.springframework.security.oauth2.client.web.server.ServerAuthorizationRequestRepository;
|
||||
import org.springframework.security.oauth2.client.web.server.authentication.OAuth2LoginAuthenticationWebFilter;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;
|
||||
import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
|
||||
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
|
||||
import org.springframework.security.web.server.DefaultServerRedirectStrategy;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.server.ServerRedirectStrategy;
|
||||
import org.springframework.security.web.server.WebFilterChainProxy;
|
||||
import org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilterTests;
|
||||
import org.springframework.security.web.server.authentication.HttpBasicServerAuthenticationEntryPoint;
|
||||
@@ -76,6 +80,7 @@ import org.springframework.web.server.WebFilterChain;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
@@ -531,6 +536,90 @@ public class ServerHttpSecurityTests {
|
||||
verify(authorizationRequestRepository).removeAuthorizationRequest(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUseDefaultAuthorizationRedirectStrategyForOAuth2Login() {
|
||||
ReactiveClientRegistrationRepository clientRegistrationRepository = mock(
|
||||
ReactiveClientRegistrationRepository.class);
|
||||
given(clientRegistrationRepository.findByRegistrationId(anyString()))
|
||||
.willReturn(Mono.just(TestClientRegistrations.clientRegistration().build()));
|
||||
|
||||
SecurityWebFilterChain securityFilterChain = this.http.oauth2Login()
|
||||
.clientRegistrationRepository(clientRegistrationRepository).and().build();
|
||||
|
||||
WebTestClient client = WebTestClientBuilder.bindToWebFilters(securityFilterChain).build();
|
||||
client.get().uri("/oauth2/authorization/registration-id").exchange().expectStatus().is3xxRedirection();
|
||||
|
||||
OAuth2AuthorizationRequestRedirectWebFilter filter = getWebFilter(securityFilterChain,
|
||||
OAuth2AuthorizationRequestRedirectWebFilter.class).get();
|
||||
assertThat(ReflectionTestUtils.getField(filter, "authorizationRedirectStrategy"))
|
||||
.isInstanceOf(DefaultServerRedirectStrategy.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldConfigureAuthorizationRedirectStrategyForOAuth2Login() {
|
||||
ServerRedirectStrategy authorizationRedirectStrategy = mock(ServerRedirectStrategy.class);
|
||||
ReactiveClientRegistrationRepository clientRegistrationRepository = mock(
|
||||
ReactiveClientRegistrationRepository.class);
|
||||
given(clientRegistrationRepository.findByRegistrationId(anyString()))
|
||||
.willReturn(Mono.just(TestClientRegistrations.clientRegistration().build()));
|
||||
given(authorizationRedirectStrategy.sendRedirect(any(), any())).willReturn(Mono.empty());
|
||||
|
||||
SecurityWebFilterChain securityFilterChain = this.http.oauth2Login()
|
||||
.clientRegistrationRepository(clientRegistrationRepository)
|
||||
.authorizationRedirectStrategy(authorizationRedirectStrategy).and().build();
|
||||
|
||||
WebTestClient client = WebTestClientBuilder.bindToWebFilters(securityFilterChain).build();
|
||||
client.get().uri("/oauth2/authorization/registration-id").exchange();
|
||||
verify(authorizationRedirectStrategy).sendRedirect(any(), any());
|
||||
|
||||
OAuth2AuthorizationRequestRedirectWebFilter filter = getWebFilter(securityFilterChain,
|
||||
OAuth2AuthorizationRequestRedirectWebFilter.class).get();
|
||||
assertThat(ReflectionTestUtils.getField(filter, "authorizationRedirectStrategy"))
|
||||
.isSameAs(authorizationRedirectStrategy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUseDefaultAuthorizationRedirectStrategyForOAuth2Client() {
|
||||
ReactiveClientRegistrationRepository clientRegistrationRepository = mock(
|
||||
ReactiveClientRegistrationRepository.class);
|
||||
given(clientRegistrationRepository.findByRegistrationId(anyString()))
|
||||
.willReturn(Mono.just(TestClientRegistrations.clientRegistration().build()));
|
||||
|
||||
SecurityWebFilterChain securityFilterChain = this.http.oauth2Client()
|
||||
.clientRegistrationRepository(clientRegistrationRepository).and().build();
|
||||
|
||||
WebTestClient client = WebTestClientBuilder.bindToWebFilters(securityFilterChain).build();
|
||||
client.get().uri("/oauth2/authorization/registration-id").exchange().expectStatus().is3xxRedirection();
|
||||
|
||||
OAuth2AuthorizationRequestRedirectWebFilter filter = getWebFilter(securityFilterChain,
|
||||
OAuth2AuthorizationRequestRedirectWebFilter.class).get();
|
||||
assertThat(ReflectionTestUtils.getField(filter, "authorizationRedirectStrategy"))
|
||||
.isInstanceOf(DefaultServerRedirectStrategy.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldConfigureAuthorizationRedirectStrategyForOAuth2Client() {
|
||||
ServerRedirectStrategy authorizationRedirectStrategy = mock(ServerRedirectStrategy.class);
|
||||
ReactiveClientRegistrationRepository clientRegistrationRepository = mock(
|
||||
ReactiveClientRegistrationRepository.class);
|
||||
given(clientRegistrationRepository.findByRegistrationId(anyString()))
|
||||
.willReturn(Mono.just(TestClientRegistrations.clientRegistration().build()));
|
||||
given(authorizationRedirectStrategy.sendRedirect(any(), any())).willReturn(Mono.empty());
|
||||
|
||||
SecurityWebFilterChain securityFilterChain = this.http.oauth2Client()
|
||||
.clientRegistrationRepository(clientRegistrationRepository)
|
||||
.authorizationRedirectStrategy(authorizationRedirectStrategy).and().build();
|
||||
|
||||
WebTestClient client = WebTestClientBuilder.bindToWebFilters(securityFilterChain).build();
|
||||
client.get().uri("/oauth2/authorization/registration-id").exchange();
|
||||
verify(authorizationRedirectStrategy).sendRedirect(any(), any());
|
||||
|
||||
OAuth2AuthorizationRequestRedirectWebFilter filter = getWebFilter(securityFilterChain,
|
||||
OAuth2AuthorizationRequestRedirectWebFilter.class).get();
|
||||
assertThat(ReflectionTestUtils.getField(filter, "authorizationRedirectStrategy"))
|
||||
.isSameAs(authorizationRedirectStrategy);
|
||||
}
|
||||
|
||||
private boolean isX509Filter(WebFilter filter) {
|
||||
try {
|
||||
Object converter = ReflectionTestUtils.getField(filter, "authenticationConverter");
|
||||
|
||||
+37
@@ -37,7 +37,9 @@ import org.springframework.security.oauth2.client.web.server.ServerAuthorization
|
||||
import org.springframework.security.oauth2.client.web.server.WebSessionOAuth2ServerAuthorizationRequestRepository
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames
|
||||
import org.springframework.security.web.server.DefaultServerRedirectStrategy
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain
|
||||
import org.springframework.security.web.server.ServerRedirectStrategy
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter
|
||||
import org.springframework.test.web.reactive.server.WebTestClient
|
||||
import org.springframework.web.reactive.config.EnableWebFlux
|
||||
@@ -128,6 +130,41 @@ class ServerOAuth2ClientDslTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `OAuth2 client when authorization redirect strategy configured then custom redirect strategy used`() {
|
||||
this.spring.register(AuthorizationRedirectStrategyConfig::class.java, ClientConfig::class.java).autowire()
|
||||
mockkObject(AuthorizationRedirectStrategyConfig.AUTHORIZATION_REDIRECT_STRATEGY)
|
||||
every {
|
||||
AuthorizationRedirectStrategyConfig.AUTHORIZATION_REDIRECT_STRATEGY.sendRedirect(any(), any())
|
||||
} returns Mono.empty()
|
||||
|
||||
this.client.get()
|
||||
.uri("/oauth2/authorization/google")
|
||||
.exchange()
|
||||
|
||||
verify(exactly = 1) {
|
||||
AuthorizationRedirectStrategyConfig.AUTHORIZATION_REDIRECT_STRATEGY.sendRedirect(any(), any())
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebFluxSecurity
|
||||
@EnableWebFlux
|
||||
open class AuthorizationRedirectStrategyConfig {
|
||||
|
||||
companion object {
|
||||
val AUTHORIZATION_REDIRECT_STRATEGY : ServerRedirectStrategy = DefaultServerRedirectStrategy()
|
||||
}
|
||||
|
||||
@Bean
|
||||
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||
return http {
|
||||
oauth2Client {
|
||||
authorizationRedirectStrategy = AUTHORIZATION_REDIRECT_STRATEGY
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `OAuth2 client when authentication converter configured then custom converter used`() {
|
||||
this.spring.register(AuthenticationConverterConfig::class.java, ClientConfig::class.java).autowire()
|
||||
|
||||
+34
@@ -34,7 +34,9 @@ import org.springframework.security.oauth2.client.registration.ReactiveClientReg
|
||||
import org.springframework.security.oauth2.client.web.server.ServerAuthorizationRequestRepository
|
||||
import org.springframework.security.oauth2.client.web.server.WebSessionOAuth2ServerAuthorizationRequestRepository
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest
|
||||
import org.springframework.security.web.server.DefaultServerRedirectStrategy
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain
|
||||
import org.springframework.security.web.server.ServerRedirectStrategy
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter
|
||||
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher
|
||||
import org.springframework.test.web.reactive.server.WebTestClient
|
||||
@@ -139,6 +141,38 @@ class ServerOAuth2LoginDslTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `OAuth2 login when authorization redirect strategy configured then custom redirect strategy used`() {
|
||||
this.spring.register(AuthorizationRedirectStrategyConfig::class.java, ClientConfig::class.java).autowire()
|
||||
mockkObject(AuthorizationRedirectStrategyConfig.AUTHORIZATION_REDIRECT_STRATEGY)
|
||||
every {
|
||||
AuthorizationRedirectStrategyConfig.AUTHORIZATION_REDIRECT_STRATEGY.sendRedirect(any(), any())
|
||||
} returns Mono.empty()
|
||||
this.client.get()
|
||||
.uri("/oauth2/authorization/google")
|
||||
.exchange()
|
||||
|
||||
verify(exactly = 1) { AuthorizationRedirectStrategyConfig.AUTHORIZATION_REDIRECT_STRATEGY.sendRedirect(any(), any()) }
|
||||
}
|
||||
|
||||
@EnableWebFluxSecurity
|
||||
@EnableWebFlux
|
||||
open class AuthorizationRedirectStrategyConfig {
|
||||
|
||||
companion object {
|
||||
val AUTHORIZATION_REDIRECT_STRATEGY : ServerRedirectStrategy = DefaultServerRedirectStrategy()
|
||||
}
|
||||
|
||||
@Bean
|
||||
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||
return http {
|
||||
oauth2Login {
|
||||
authorizationRedirectStrategy = AUTHORIZATION_REDIRECT_STRATEGY
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `OAuth2 login when authentication matcher configured then custom matcher used`() {
|
||||
this.spring.register(AuthenticationMatcherConfig::class.java, ClientConfig::class.java).autowire()
|
||||
|
||||
+37
@@ -43,6 +43,9 @@ import org.springframework.security.oauth2.core.OAuth2AccessToken
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames
|
||||
import org.springframework.security.web.DefaultRedirectStrategy
|
||||
import org.springframework.security.web.RedirectStrategy
|
||||
import org.springframework.security.web.SecurityFilterChain
|
||||
import org.springframework.test.web.servlet.MockMvc
|
||||
import org.springframework.test.web.servlet.get
|
||||
|
||||
@@ -101,6 +104,40 @@ class AuthorizationCodeGrantDslTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `oauth2Client when custom authorization redirect strategy then redirect strategy used`() {
|
||||
this.spring.register(RedirectStrategyConfig::class.java, ClientConfig::class.java).autowire()
|
||||
mockkObject(RedirectStrategyConfig.REDIRECT_STRATEGY)
|
||||
every { RedirectStrategyConfig.REDIRECT_STRATEGY.sendRedirect(any(), any(), any()) }
|
||||
|
||||
this.mockMvc.get("/oauth2/authorization/registrationId")
|
||||
|
||||
verify(exactly = 1) { RedirectStrategyConfig.REDIRECT_STRATEGY.sendRedirect(any(), any(), any()) }
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
open class RedirectStrategyConfig {
|
||||
|
||||
companion object {
|
||||
val REDIRECT_STRATEGY: RedirectStrategy = DefaultRedirectStrategy()
|
||||
}
|
||||
|
||||
@Bean
|
||||
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
http {
|
||||
oauth2Client {
|
||||
authorizationCodeGrant {
|
||||
authorizationRedirectStrategy = REDIRECT_STRATEGY
|
||||
}
|
||||
}
|
||||
authorizeRequests {
|
||||
authorize(anyRequest, authenticated)
|
||||
}
|
||||
}
|
||||
return http.build()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `oauth2Client when custom access token response client then client used`() {
|
||||
this.spring.register(AuthorizedClientConfig::class.java, ClientConfig::class.java).autowire()
|
||||
|
||||
+34
@@ -38,6 +38,9 @@ import org.springframework.security.oauth2.client.web.AuthorizationRequestReposi
|
||||
import org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest
|
||||
import org.springframework.security.web.DefaultRedirectStrategy
|
||||
import org.springframework.security.web.RedirectStrategy
|
||||
import org.springframework.security.web.SecurityFilterChain
|
||||
import org.springframework.test.web.servlet.MockMvc
|
||||
import org.springframework.test.web.servlet.get
|
||||
|
||||
@@ -121,6 +124,37 @@ class AuthorizationEndpointDslTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `oauth2Login when custom authorization redirect strategy then redirect strategy used`() {
|
||||
this.spring.register(RedirectStrategyConfig::class.java, ClientConfig::class.java).autowire()
|
||||
mockkObject(RedirectStrategyConfig.REDIRECT_STRATEGY)
|
||||
every { RedirectStrategyConfig.REDIRECT_STRATEGY.sendRedirect(any(), any(), any()) }
|
||||
|
||||
this.mockMvc.get("/oauth2/authorization/google")
|
||||
|
||||
verify(exactly = 1) { RedirectStrategyConfig.REDIRECT_STRATEGY.sendRedirect(any(), any(), any()) }
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
open class RedirectStrategyConfig {
|
||||
|
||||
companion object {
|
||||
val REDIRECT_STRATEGY: RedirectStrategy = DefaultRedirectStrategy()
|
||||
}
|
||||
|
||||
@Bean
|
||||
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
http {
|
||||
oauth2Login {
|
||||
authorizationEndpoint {
|
||||
authorizationRedirectStrategy = REDIRECT_STRATEGY
|
||||
}
|
||||
}
|
||||
}
|
||||
return http.build()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `oauth2Login when custom authorization uri repository then uri used`() {
|
||||
this.spring.register(AuthorizationUriConfig::class.java, ClientConfig::class.java).autowire()
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2002-2022 the original author or authors.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ https://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://www.springframework.org/schema/security"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/security
|
||||
https://www.springframework.org/schema/security/spring-security.xsd
|
||||
http://www.springframework.org/schema/beans
|
||||
https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<http auto-config="true">
|
||||
<oauth2-client>
|
||||
<authorization-code-grant
|
||||
authorization-redirect-strategy-ref="authorizationRedirectStrategy"/>
|
||||
</oauth2-client>
|
||||
</http>
|
||||
|
||||
<b:bean id="authorizationRedirectStrategy" class="org.mockito.Mockito" factory-method="mock">
|
||||
<b:constructor-arg value="org.springframework.security.web.RedirectStrategy"/>
|
||||
</b:bean>
|
||||
|
||||
<client-registrations>
|
||||
<client-registration registration-id="google"
|
||||
client-id="google-client-id"
|
||||
client-secret="google-client-secret"
|
||||
redirect-uri="http://localhost/callback/google"
|
||||
scope="scope1,scope2"
|
||||
provider-id="google"/>
|
||||
</client-registrations>
|
||||
|
||||
<b:import resource="userservice.xml"/>
|
||||
</b:beans>
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2002-2022 the original author or authors.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ https://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://www.springframework.org/schema/security"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/security
|
||||
https://www.springframework.org/schema/security/spring-security.xsd
|
||||
http://www.springframework.org/schema/beans
|
||||
https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<http auto-config="true">
|
||||
<intercept-url pattern="/**" access="authenticated"/>
|
||||
<oauth2-login authorization-redirect-strategy-ref="authorizationRedirectStrategy"/>
|
||||
</http>
|
||||
|
||||
<b:bean id="authorizationRedirectStrategy" class="org.mockito.Mockito" factory-method="mock">
|
||||
<b:constructor-arg value="org.springframework.security.web.RedirectStrategy"/>
|
||||
</b:bean>
|
||||
|
||||
<b:import resource="../oauth2/client/google-registration.xml"/>
|
||||
<b:import resource="userservice.xml"/>
|
||||
</b:beans>
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
'display_version': '5.8.0-M1'
|
||||
'display_version': '5.8.0-M2'
|
||||
'name': 'ROOT'
|
||||
'prerelease': 'true'
|
||||
'version': '5.8.0-M1'
|
||||
'version': '5.8.0-M2'
|
||||
|
||||
@@ -495,7 +495,7 @@ XML Configuration requires the `NoOpPasswordEncoder` bean name to be `passwordEn
|
||||
|
||||
Most applications that allow a user to specify a password also require a feature for updating that password.
|
||||
|
||||
https://w3c.github.io/webappsec-change-password-url/[A Well-Know URL for Changing Passwords] indicates a mechanism by which password managers can discover the password update endpoint for a given application.
|
||||
https://w3c.github.io/webappsec-change-password-url/[A Well-Known URL for Changing Passwords] indicates a mechanism by which password managers can discover the password update endpoint for a given application.
|
||||
|
||||
You can configure Spring Security to provide this discovery endpoint.
|
||||
For example, if the change password endpoint in your application is `/change-password`, then you can configure Spring Security like so:
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
The basic setup looks like this:
|
||||
|
||||
[source,java]
|
||||
====
|
||||
.Java
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration(classes = HelloWebfluxMethodApplication.class)
|
||||
@@ -19,9 +21,35 @@ public class HelloWebfluxMethodApplicationTests {
|
||||
// add Spring Security test Support
|
||||
.apply(springSecurity())
|
||||
.configureClient()
|
||||
.filter(basicAuthentication())
|
||||
.filter(basicAuthentication("user", "password"))
|
||||
.build();
|
||||
}
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
.Kotlin
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@ExtendWith(SpringExtension::class)
|
||||
@ContextConfiguration(classes = [HelloWebfluxMethodApplication::class])
|
||||
class HelloWebfluxMethodApplicationTests {
|
||||
@Autowired
|
||||
lateinit var context: ApplicationContext
|
||||
|
||||
lateinit var rest: WebTestClient
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
this.rest = WebTestClient
|
||||
.bindToApplicationContext(this.context)
|
||||
// add Spring Security test Support
|
||||
.apply(springSecurity())
|
||||
.configureClient()
|
||||
.filter(basicAuthentication("user", "password"))
|
||||
.build()
|
||||
}
|
||||
// ...
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
@@ -984,6 +984,11 @@ Reference to the `AuthorizationRequestRepository`.
|
||||
Reference to the `OAuth2AuthorizationRequestResolver`.
|
||||
|
||||
|
||||
[[nsa-oauth2-login-authorization-redirect-strategy-ref]]
|
||||
* **authorization-redirect-strategy-ref**
|
||||
Reference to the authorization `RedirectStrategy`.
|
||||
|
||||
|
||||
[[nsa-oauth2-login-access-token-response-client-ref]]
|
||||
* **access-token-response-client-ref**
|
||||
Reference to the `OAuth2AccessTokenResponseClient`.
|
||||
@@ -1084,6 +1089,11 @@ Configures xref:servlet/oauth2/client/authorization-grants.adoc#oauth2Client-aut
|
||||
Reference to the `AuthorizationRequestRepository`.
|
||||
|
||||
|
||||
[[nsa-authorization-code-grant-authorization-redirect-strategy-ref]]
|
||||
* **authorization-redirect-strategy-ref**
|
||||
Reference to the authorization `RedirectStrategy`.
|
||||
|
||||
|
||||
[[nsa-authorization-code-grant-authorization-request-resolver-ref]]
|
||||
* **authorization-request-resolver-ref**
|
||||
Reference to the `OAuth2AuthorizationRequestResolver`.
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ springJavaformatVersion=0.0.34
|
||||
springBootVersion=2.4.2
|
||||
springFrameworkVersion=5.3.22
|
||||
openSamlVersion=3.4.6
|
||||
version=5.8.0-M1
|
||||
version=5.8.0-M2
|
||||
kotlinVersion=1.7.10
|
||||
samplesBranch=5.8.x
|
||||
org.gradle.jvmargs=-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError
|
||||
|
||||
+10
-1
@@ -95,7 +95,7 @@ public class OAuth2AuthorizationRequestRedirectFilter extends OncePerRequestFilt
|
||||
|
||||
private final ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();
|
||||
|
||||
private final RedirectStrategy authorizationRedirectStrategy = new DefaultRedirectStrategy();
|
||||
private RedirectStrategy authorizationRedirectStrategy = new DefaultRedirectStrategy();
|
||||
|
||||
private OAuth2AuthorizationRequestResolver authorizationRequestResolver;
|
||||
|
||||
@@ -139,6 +139,15 @@ public class OAuth2AuthorizationRequestRedirectFilter extends OncePerRequestFilt
|
||||
this.authorizationRequestResolver = authorizationRequestResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the redirect strategy for Authorization Endpoint redirect URI.
|
||||
* @param authorizationRedirectStrategy the redirect strategy
|
||||
*/
|
||||
public void setAuthorizationRedirectStrategy(RedirectStrategy authorizationRedirectStrategy) {
|
||||
Assert.notNull(authorizationRedirectStrategy, "authorizationRedirectStrategy cannot be null");
|
||||
this.authorizationRedirectStrategy = authorizationRedirectStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the repository used for storing {@link OAuth2AuthorizationRequest}'s.
|
||||
* @param authorizationRequestRepository the repository used for storing
|
||||
|
||||
+10
-1
@@ -75,7 +75,7 @@ import org.springframework.web.util.UriComponentsBuilder;
|
||||
*/
|
||||
public class OAuth2AuthorizationRequestRedirectWebFilter implements WebFilter {
|
||||
|
||||
private final ServerRedirectStrategy authorizationRedirectStrategy = new DefaultServerRedirectStrategy();
|
||||
private ServerRedirectStrategy authorizationRedirectStrategy = new DefaultServerRedirectStrategy();
|
||||
|
||||
private final ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver;
|
||||
|
||||
@@ -105,6 +105,15 @@ public class OAuth2AuthorizationRequestRedirectWebFilter implements WebFilter {
|
||||
this.authorizationRequestResolver = authorizationRequestResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the redirect strategy for Authorization Endpoint redirect URI.
|
||||
* @param authorizationRedirectStrategy the redirect strategy
|
||||
*/
|
||||
public void setAuthorizationRedirectStrategy(ServerRedirectStrategy authorizationRedirectStrategy) {
|
||||
Assert.notNull(authorizationRedirectStrategy, "authorizationRedirectStrategy cannot be null");
|
||||
this.authorizationRedirectStrategy = authorizationRedirectStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the repository used for storing {@link OAuth2AuthorizationRequest}'s.
|
||||
* @param authorizationRequestRepository the repository used for storing
|
||||
|
||||
+36
@@ -17,6 +17,7 @@
|
||||
package org.springframework.security.oauth2.client.web;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -30,7 +31,9 @@ import javax.servlet.http.HttpServletResponse;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.oauth2.client.ClientAuthorizationRequiredException;
|
||||
@@ -40,6 +43,7 @@ import org.springframework.security.oauth2.client.registration.InMemoryClientReg
|
||||
import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
@@ -116,6 +120,11 @@ public class OAuth2AuthorizationRequestRedirectFilterTests {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthorizationRequestRepository(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAuthorizationRedirectStrategyWhenAuthorizationRedirectStrategyIsNullThenThrowIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthorizationRedirectStrategy(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setRequestCacheWhenRequestCacheIsNullThenThrowIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.filter.setRequestCache(null));
|
||||
@@ -333,4 +342,31 @@ public class OAuth2AuthorizationRequestRedirectFilterTests {
|
||||
+ "login_hint=user@provider\\.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doFilterWhenCustomAuthorizationRedirectStrategySetThenCustomAuthorizationRedirectStrategyUsed()
|
||||
throws Exception {
|
||||
String requestUri = OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/"
|
||||
+ this.registration1.getRegistrationId();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
|
||||
request.setServletPath(requestUri);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterChain filterChain = mock(FilterChain.class);
|
||||
RedirectStrategy customRedirectStrategy = (httpRequest, httpResponse, url) -> {
|
||||
String redirectUrl = httpResponse.encodeRedirectURL(url);
|
||||
httpResponse.setStatus(HttpStatus.OK.value());
|
||||
httpResponse.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE);
|
||||
httpResponse.getWriter().write(redirectUrl);
|
||||
httpResponse.getWriter().flush();
|
||||
};
|
||||
this.filter.setAuthorizationRedirectStrategy(customRedirectStrategy);
|
||||
this.filter.doFilter(request, response, filterChain);
|
||||
verifyZeroInteractions(filterChain);
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
|
||||
assertThat(response.getContentType()).isEqualTo(MediaType.TEXT_PLAIN_VALUE);
|
||||
assertThat(response.getContentAsString(StandardCharsets.UTF_8))
|
||||
.matches("https://example.com/login/oauth/authorize\\?" + "response_type=code&client_id=client-id&"
|
||||
+ "scope=read:user&state=.{15,}&"
|
||||
+ "redirect_uri=http://localhost/login/oauth2/code/registration-id");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+55
@@ -17,6 +17,7 @@
|
||||
package org.springframework.security.oauth2.client.web.server;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -24,13 +25,20 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.security.oauth2.client.ClientAuthorizationRequiredException;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.web.server.ServerRedirectStrategy;
|
||||
import org.springframework.security.web.server.savedrequest.ServerRequestCache;
|
||||
import org.springframework.test.web.reactive.server.FluxExchangeResult;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
@@ -81,6 +89,11 @@ public class OAuth2AuthorizationRequestRedirectWebFilterTests {
|
||||
.isThrownBy(() -> new OAuth2AuthorizationRequestRedirectWebFilter(this.clientRepository));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setterWhenAuthorizationRedirectStrategyNullThenIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthorizationRedirectStrategy(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterWhenDoesNotMatchThenClientRegistrationRepositoryNotSubscribed() {
|
||||
// @formatter:off
|
||||
@@ -195,4 +208,46 @@ public class OAuth2AuthorizationRequestRedirectWebFilterTests {
|
||||
verifyNoInteractions(this.requestCache);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterWhenCustomRedirectStrategySetThenRedirectUriInResponseBody() {
|
||||
given(this.clientRepository.findByRegistrationId(this.registration.getRegistrationId()))
|
||||
.willReturn(Mono.just(this.registration));
|
||||
given(this.authzRequestRepository.saveAuthorizationRequest(any(), any())).willReturn(Mono.empty());
|
||||
ServerRedirectStrategy customRedirectStrategy = (exchange, location) -> {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
response.setStatusCode(HttpStatus.OK);
|
||||
response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
|
||||
DataBuffer buffer = exchange.getResponse().bufferFactory()
|
||||
.wrap(location.toASCIIString().getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
return exchange.getResponse().writeWith(Flux.just(buffer));
|
||||
};
|
||||
this.filter.setAuthorizationRedirectStrategy(customRedirectStrategy);
|
||||
this.filter.setRequestCache(this.requestCache);
|
||||
|
||||
FluxExchangeResult<String> result = this.client.get()
|
||||
.uri("https://example.com/oauth2/authorization/registration-id").exchange().expectHeader()
|
||||
.contentType(MediaType.TEXT_PLAIN).expectStatus().isOk().returnResult(String.class);
|
||||
|
||||
// @formatter:off
|
||||
StepVerifier.create(result.getResponseBody())
|
||||
.assertNext((uri) -> {
|
||||
URI location = URI.create(uri);
|
||||
|
||||
assertThat(location)
|
||||
.hasScheme("https")
|
||||
.hasHost("example.com")
|
||||
.hasPath("/login/oauth/authorize")
|
||||
.hasParameter("response_type", "code")
|
||||
.hasParameter("client_id", "client-id")
|
||||
.hasParameter("scope", "read:user")
|
||||
.hasParameter("state")
|
||||
.hasParameter("redirect_uri", "https://example.com/login/oauth2/code/registration-id");
|
||||
})
|
||||
.verifyComplete();
|
||||
// @formatter:on
|
||||
|
||||
verifyNoInteractions(this.requestCache);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
@@ -236,6 +236,8 @@ public class Saml2WebSsoAuthenticationRequestFilter extends OncePerRequestFilter
|
||||
StringBuilder html = new StringBuilder();
|
||||
html.append("<!DOCTYPE html>\n");
|
||||
html.append("<html>\n").append(" <head>\n");
|
||||
html.append(" <meta http-equiv=\"Content-Security-Policy\" ")
|
||||
.append("content=\"script-src 'sha256-ePniVEkSivX/c7XWBGafqh8tSpiRrKiqYeqbG7N1TOE='\">\n");
|
||||
html.append(" <meta charset=\"utf-8\" />\n");
|
||||
html.append(" </head>\n");
|
||||
html.append(" <body onload=\"document.forms[0].submit()\">\n");
|
||||
|
||||
+2
@@ -215,6 +215,8 @@ public final class Saml2LogoutRequestFilter extends OncePerRequestFilter {
|
||||
StringBuilder html = new StringBuilder();
|
||||
html.append("<!DOCTYPE html>\n");
|
||||
html.append("<html>\n").append(" <head>\n");
|
||||
html.append(" <meta http-equiv=\"Content-Security-Policy\" ")
|
||||
.append("content=\"script-src 'sha256-ePniVEkSivX/c7XWBGafqh8tSpiRrKiqYeqbG7N1TOE='\">\n");
|
||||
html.append(" <meta charset=\"utf-8\" />\n");
|
||||
html.append(" </head>\n");
|
||||
html.append(" <body onload=\"document.forms[0].submit()\">\n");
|
||||
|
||||
+2
@@ -119,6 +119,8 @@ public final class Saml2RelyingPartyInitiatedLogoutSuccessHandler implements Log
|
||||
StringBuilder html = new StringBuilder();
|
||||
html.append("<!DOCTYPE html>\n");
|
||||
html.append("<html>\n").append(" <head>\n");
|
||||
html.append(" <meta http-equiv=\"Content-Security-Policy\" ")
|
||||
.append("content=\"script-src 'sha256-ePniVEkSivX/c7XWBGafqh8tSpiRrKiqYeqbG7N1TOE='\">\n");
|
||||
html.append(" <meta charset=\"utf-8\" />\n");
|
||||
html.append(" </head>\n");
|
||||
html.append(" <body onload=\"document.forms[0].submit()\">\n");
|
||||
|
||||
+1
@@ -244,6 +244,7 @@ public class OpenSamlAuthenticationProviderTests {
|
||||
expected.put("age", Collections.singletonList(21));
|
||||
expected.put("website", Collections.singletonList("https://johndoe.com/"));
|
||||
expected.put("registered", Collections.singletonList(true));
|
||||
expected.put("role", Arrays.asList("RoleTwo"));
|
||||
Instant registeredDate = Instant.ofEpochMilli(DateTime.parse("1970-01-01T00:00:00Z").getMillis());
|
||||
expected.put("registeredDate", Collections.singletonList(registeredDate));
|
||||
assertThat((String) principal.getFirstAttribute("name")).isEqualTo("John Doe");
|
||||
|
||||
+4
-3
@@ -23,7 +23,6 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
@@ -92,6 +91,8 @@ import org.springframework.security.saml2.provider.service.registration.RelyingP
|
||||
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -645,7 +646,7 @@ public final class OpenSaml4AuthenticationProvider implements AuthenticationProv
|
||||
}
|
||||
|
||||
private static Map<String, List<Object>> getAssertionAttributes(Assertion assertion) {
|
||||
Map<String, List<Object>> attributeMap = new LinkedHashMap<>();
|
||||
MultiValueMap<String, Object> attributeMap = new LinkedMultiValueMap<>();
|
||||
for (AttributeStatement attributeStatement : assertion.getAttributeStatements()) {
|
||||
for (Attribute attribute : attributeStatement.getAttributes()) {
|
||||
List<Object> attributeValues = new ArrayList<>();
|
||||
@@ -655,7 +656,7 @@ public final class OpenSaml4AuthenticationProvider implements AuthenticationProv
|
||||
attributeValues.add(attributeValue);
|
||||
}
|
||||
}
|
||||
attributeMap.put(attribute.getName(), attributeValues);
|
||||
attributeMap.addAll(attribute.getName(), attributeValues);
|
||||
}
|
||||
}
|
||||
return attributeMap;
|
||||
|
||||
+1
@@ -343,6 +343,7 @@ public class OpenSaml4AuthenticationProviderTests {
|
||||
expected.put("registered", Collections.singletonList(true));
|
||||
Instant registeredDate = Instant.parse("1970-01-01T00:00:00Z");
|
||||
expected.put("registeredDate", Collections.singletonList(registeredDate));
|
||||
expected.put("role", Arrays.asList("RoleOne", "RoleTwo")); // gh-11042
|
||||
assertThat((String) principal.getFirstAttribute("name")).isEqualTo("John Doe");
|
||||
assertThat(principal.getAttributes()).isEqualTo(expected);
|
||||
assertThat(principal.getSessionIndexes()).contains("session-index");
|
||||
|
||||
+12
@@ -327,6 +327,18 @@ public final class TestOpenSamlObjects {
|
||||
name.setValue("John Doe");
|
||||
nameAttr.getAttributeValues().add(name);
|
||||
attrStmt1.getAttributes().add(nameAttr);
|
||||
Attribute roleOneAttr = attributeBuilder.buildObject(); // gh-11042
|
||||
roleOneAttr.setName("role");
|
||||
XSString roleOne = new XSStringBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME);
|
||||
roleOne.setValue("RoleOne");
|
||||
roleOneAttr.getAttributeValues().add(roleOne);
|
||||
attrStmt1.getAttributes().add(roleOneAttr);
|
||||
Attribute roleTwoAttr = attributeBuilder.buildObject(); // gh-11042
|
||||
roleTwoAttr.setName("role");
|
||||
XSString roleTwo = new XSStringBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME);
|
||||
roleTwo.setValue("RoleTwo");
|
||||
roleTwoAttr.getAttributeValues().add(roleTwo);
|
||||
attrStmt1.getAttributes().add(roleTwoAttr);
|
||||
Attribute ageAttr = attributeBuilder.buildObject();
|
||||
ageAttr.setName("age");
|
||||
XSInteger age = new XSIntegerBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSInteger.TYPE_NAME);
|
||||
|
||||
+3
-1
@@ -198,7 +198,9 @@ public class Saml2WebSsoAuthenticationRequestFilterTests {
|
||||
given(this.factory.createPostAuthenticationRequest(any())).willReturn(request);
|
||||
this.filter.doFilterInternal(this.request, this.response, this.filterChain);
|
||||
assertThat(this.response.getHeader("Location")).isNull();
|
||||
assertThat(this.response.getContentAsString())
|
||||
assertThat(this.response.getContentAsString()).contains(
|
||||
"<meta http-equiv=\"Content-Security-Policy\" content=\"script-src 'sha256-ePniVEkSivX/c7XWBGafqh8tSpiRrKiqYeqbG7N1TOE='\">")
|
||||
.contains("<body onload=\"document.forms[0].submit()\">")
|
||||
.contains("<form action=\"https://sso-url.example.com/IDP/SSO\" method=\"post\">")
|
||||
.contains("<input type=\"hidden\" name=\"SAMLRequest\"")
|
||||
.contains("value=\"" + relayStateEncoded + "\"");
|
||||
|
||||
+3
@@ -117,6 +117,9 @@ public class Saml2LogoutRequestFilterTests {
|
||||
String content = response.getContentAsString();
|
||||
assertThat(content).contains(Saml2ParameterNames.SAML_RESPONSE);
|
||||
assertThat(content).contains(registration.getAssertingPartyDetails().getSingleLogoutServiceResponseLocation());
|
||||
assertThat(content).contains(
|
||||
"<meta http-equiv=\"Content-Security-Policy\" content=\"script-src 'sha256-ePniVEkSivX/c7XWBGafqh8tSpiRrKiqYeqbG7N1TOE='\">");
|
||||
assertThat(content).contains("<body onload=\"document.forms[0].submit()\">");
|
||||
verify(this.securityContextHolderStrategy).getContext();
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -97,6 +97,9 @@ public class Saml2RelyingPartyInitiatedLogoutSuccessHandlerTests {
|
||||
String content = response.getContentAsString();
|
||||
assertThat(content).contains(Saml2ParameterNames.SAML_REQUEST);
|
||||
assertThat(content).contains(registration.getAssertingPartyDetails().getSingleLogoutServiceLocation());
|
||||
assertThat(content).contains(
|
||||
"<meta http-equiv=\"Content-Security-Policy\" content=\"script-src 'sha256-ePniVEkSivX/c7XWBGafqh8tSpiRrKiqYeqbG7N1TOE='\">");
|
||||
assertThat(content).contains("<body onload=\"document.forms[0].submit()\">");
|
||||
}
|
||||
|
||||
private Saml2Authentication authentication(RelyingPartyRegistration registration) {
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.springframework.security.web.firewall.HttpFirewall;
|
||||
import org.springframework.security.web.firewall.RequestRejectedException;
|
||||
import org.springframework.security.web.firewall.RequestRejectedHandler;
|
||||
import org.springframework.security.web.firewall.StrictHttpFirewall;
|
||||
import org.springframework.security.web.util.ThrowableAnalyzer;
|
||||
import org.springframework.security.web.util.UrlUtils;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -158,6 +159,8 @@ public class FilterChainProxy extends GenericFilterBean {
|
||||
|
||||
private RequestRejectedHandler requestRejectedHandler = new DefaultRequestRejectedHandler();
|
||||
|
||||
private ThrowableAnalyzer throwableAnalyzer = new ThrowableAnalyzer();
|
||||
|
||||
public FilterChainProxy() {
|
||||
}
|
||||
|
||||
@@ -186,8 +189,15 @@ public class FilterChainProxy extends GenericFilterBean {
|
||||
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
|
||||
doFilterInternal(request, response, chain);
|
||||
}
|
||||
catch (RequestRejectedException ex) {
|
||||
this.requestRejectedHandler.handle((HttpServletRequest) request, (HttpServletResponse) response, ex);
|
||||
catch (Exception ex) {
|
||||
Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);
|
||||
Throwable requestRejectedException = this.throwableAnalyzer
|
||||
.getFirstThrowableOfType(RequestRejectedException.class, causeChain);
|
||||
if (!(requestRejectedException instanceof RequestRejectedException)) {
|
||||
throw ex;
|
||||
}
|
||||
this.requestRejectedHandler.handle((HttpServletRequest) request, (HttpServletResponse) response,
|
||||
(RequestRejectedException) requestRejectedException);
|
||||
}
|
||||
finally {
|
||||
this.securityContextHolderStrategy.clearContext();
|
||||
|
||||
+12
-4
@@ -66,18 +66,26 @@ public final class RequestAttributeSecurityContextRepository implements Security
|
||||
|
||||
@Override
|
||||
public boolean containsContext(HttpServletRequest request) {
|
||||
return loadContext(request).get() != null;
|
||||
return getContext(request) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
|
||||
SecurityContext context = loadContext(requestResponseHolder.getRequest()).get();
|
||||
return (context != null) ? context : SecurityContextHolder.createEmptyContext();
|
||||
return getContextOrEmpty(requestResponseHolder.getRequest());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<SecurityContext> loadContext(HttpServletRequest request) {
|
||||
return () -> (SecurityContext) request.getAttribute(this.requestAttributeName);
|
||||
return () -> getContextOrEmpty(request);
|
||||
}
|
||||
|
||||
private SecurityContext getContextOrEmpty(HttpServletRequest request) {
|
||||
SecurityContext context = getContext(request);
|
||||
return (context != null) ? context : SecurityContextHolder.createEmptyContext();
|
||||
}
|
||||
|
||||
private SecurityContext getContext(HttpServletRequest request) {
|
||||
return (SecurityContext) request.getAttribute(this.requestAttributeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -50,6 +50,7 @@ import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.willAnswer;
|
||||
import static org.mockito.BDDMockito.willThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
@@ -262,4 +263,18 @@ public class FilterChainProxyTests {
|
||||
verify(rjh).handle(eq(this.request), eq(this.response), eq((requestRejectedException)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestRejectedHandlerIsCalledIfFirewallThrowsWrappedRequestRejectedException() throws Exception {
|
||||
HttpFirewall fw = mock(HttpFirewall.class);
|
||||
RequestRejectedHandler rjh = mock(RequestRejectedHandler.class);
|
||||
this.fcp.setFirewall(fw);
|
||||
this.fcp.setRequestRejectedHandler(rjh);
|
||||
RequestRejectedException requestRejectedException = new RequestRejectedException("Contains illegal chars");
|
||||
ServletException servletException = new ServletException(requestRejectedException);
|
||||
given(fw.getFirewalledRequest(this.request)).willReturn(mock(FirewalledRequest.class));
|
||||
willThrow(servletException).given(this.chain).doFilter(any(), any());
|
||||
this.fcp.doFilter(this.request, this.response, this.chain);
|
||||
verify(rjh).handle(eq(this.request), eq(this.response), eq((requestRejectedException)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+15
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.security.web.context;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
@@ -67,4 +69,17 @@ class RequestAttributeSecurityContextRepositoryTests {
|
||||
assertThat(this.repository.containsContext(this.request)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void loadDeferredContextWhenNotPresentThenEmptyContext() {
|
||||
Supplier<SecurityContext> deferredContext = this.repository.loadContext(this.request);
|
||||
assertThat(deferredContext.get()).isEqualTo(SecurityContextHolder.createEmptyContext());
|
||||
}
|
||||
|
||||
@Test
|
||||
void loadContextWhenNotPresentThenEmptyContext() {
|
||||
SecurityContext context = this.repository
|
||||
.loadContext(new HttpRequestResponseHolder(this.request, this.response));
|
||||
assertThat(context).isEqualTo(SecurityContextHolder.createEmptyContext());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user