1
0
mirror of synced 2026-07-10 21:30:04 +00:00

Compare commits

..

32 Commits

Author SHA1 Message Date
github-actions[bot] 644901fed5 Release 6.4.13 2025-11-17 17:05:12 +00:00
Rob Winch 09805317e6 Update Spring Data 2024.1.12
Closes gh-18179
2025-11-17 10:27:01 -06:00
Rob Winch 4ab933803f Update to Reactor 2024.0.12
This aligns with Spring Framework's version of Reactor

Closes gh-18178
2025-11-17 10:26:09 -06:00
Rob Winch d2b1cb572f Update to Spring Framework 6.2.13
Closes gh-18177
2025-11-17 10:25:21 -06:00
Rob Winch d0166004aa Bump com.fasterxml.jackson:jackson-bom from 2.18.4.1 to 2.18.5 2025-11-17 09:33:40 -06:00
Rob Winch 9f96fbcda0 Bump org.hibernate.orm:hibernate-core from 6.6.33.Final to 6.6.34.Final 2025-11-17 09:33:37 -06:00
Rob Winch ccffb48fd1 Bump org-aspectj from 1.9.24 to 1.9.25 2025-11-17 09:33:35 -06:00
Rob Winch d0fcdebe88 Bump io.spring.gradle:spring-security-release-plugin from 1.0.11 to 1.0.13 2025-11-17 09:33:33 -06:00
dependabot[bot] f997e22d9d Bump io.micrometer:micrometer-observation from 1.14.12 to 1.14.13
Bumps [io.micrometer:micrometer-observation](https://github.com/micrometer-metrics/micrometer) from 1.14.12 to 1.14.13.
- [Release notes](https://github.com/micrometer-metrics/micrometer/releases)
- [Commits](https://github.com/micrometer-metrics/micrometer/compare/v1.14.12...v1.14.13)

---
updated-dependencies:
- dependency-name: io.micrometer:micrometer-observation
  dependency-version: 1.14.13
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-07 03:14:59 +00:00
dependabot[bot] ff908c4d7c Bump io.spring.gradle:spring-security-release-plugin
Bumps [io.spring.gradle:spring-security-release-plugin](https://github.com/spring-io/spring-security-release-tools) from 1.0.11 to 1.0.13.
- [Release notes](https://github.com/spring-io/spring-security-release-tools/releases)
- [Commits](https://github.com/spring-io/spring-security-release-tools/compare/v1.0.11...v1.0.13)

---
updated-dependencies:
- dependency-name: io.spring.gradle:spring-security-release-plugin
  dependency-version: 1.0.13
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-05 03:15:17 +00:00
dependabot[bot] 521f533fc4 Bump org-aspectj from 1.9.24 to 1.9.25
Bumps `org-aspectj` from 1.9.24 to 1.9.25.

Updates `org.aspectj:aspectjrt` from 1.9.24 to 1.9.25
- [Release notes](https://github.com/eclipse/org.aspectj/releases)
- [Commits](https://github.com/eclipse/org.aspectj/commits)

Updates `org.aspectj:aspectjweaver` from 1.9.24 to 1.9.25
- [Release notes](https://github.com/eclipse/org.aspectj/releases)
- [Commits](https://github.com/eclipse/org.aspectj/commits)

---
updated-dependencies:
- dependency-name: org.aspectj:aspectjrt
  dependency-version: 1.9.25
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.aspectj:aspectjweaver
  dependency-version: 1.9.25
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-05 03:14:41 +00:00
Josh Cummings 532d0bef14 Add Test to Confirm 72-byte BCrypt Password Limit
Closes gh-18133
2025-11-04 14:04:02 -07:00
Daniel Garnier-Moiroux fed6df5167 Default WebAuthnConfigurer#rpName to rpId
In WebAuthn L3 spec, PublicKeyCredentialEntity.name is deprecated:

> This member is deprecated because many clients do not display it,
> but it remains a required dictionary member for backwards compatibility.
> Relying Parties MAY, as a safe default, set this equal to the RP ID.

Source: https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialentity

Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
2025-11-04 11:16:22 -06:00
Daniel Garnier-Moiroux 4feeb0f843 Docs: document effects of disabling CORS configurer
Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
2025-11-04 10:23:46 -06:00
dependabot[bot] 8b0689cbb8 Bump org.hibernate.orm:hibernate-core from 6.6.33.Final to 6.6.34.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.33.Final to 6.6.34.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.34/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.33...6.6.34)

---
updated-dependencies:
- dependency-name: org.hibernate.orm:hibernate-core
  dependency-version: 6.6.34.Final
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-28 03:15:58 +00:00
dependabot[bot] 28e158d1cb Bump com.fasterxml.jackson:jackson-bom from 2.18.4.1 to 2.18.5
Bumps [com.fasterxml.jackson:jackson-bom](https://github.com/FasterXML/jackson-bom) from 2.18.4.1 to 2.18.5.
- [Commits](https://github.com/FasterXML/jackson-bom/compare/jackson-bom-2.18.4.1...jackson-bom-2.18.5)

---
updated-dependencies:
- dependency-name: com.fasterxml.jackson:jackson-bom
  dependency-version: 2.18.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-28 03:15:38 +00:00
Rob Winch 82f87cf2b6 Next Development Version 2025-10-20 16:55:17 -05:00
github-actions[bot] dc5aed9b5f Release 6.4.12 2025-10-20 17:17:37 +00:00
Rob Winch f19c9c8625 Bump ch.qos.logback:logback-classic from 1.5.19 to 1.5.20 2025-10-20 09:14:31 -05:00
dependabot[bot] 217a29e6ba Bump org.springframework.data:spring-data-bom
Bumps [org.springframework.data:spring-data-bom](https://github.com/spring-projects/spring-data-bom) from 2024.1.10 to 2024.1.11.
- [Release notes](https://github.com/spring-projects/spring-data-bom/releases)
- [Commits](https://github.com/spring-projects/spring-data-bom/compare/2024.1.10...2024.1.11)

---
updated-dependencies:
- dependency-name: org.springframework.data:spring-data-bom
  dependency-version: 2024.1.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-20 03:12:54 +00:00
dependabot[bot] b2d6380633 Bump ch.qos.logback:logback-classic from 1.5.19 to 1.5.20
Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.5.19 to 1.5.20.
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](https://github.com/qos-ch/logback/compare/v_1.5.19...v_1.5.20)

---
updated-dependencies:
- dependency-name: ch.qos.logback:logback-classic
  dependency-version: 1.5.20
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-20 03:12:45 +00:00
dependabot[bot] 43c53c3b78 Bump org.springframework:spring-framework-bom from 6.2.11 to 6.2.12
Bumps [org.springframework:spring-framework-bom](https://github.com/spring-projects/spring-framework) from 6.2.11 to 6.2.12.
- [Release notes](https://github.com/spring-projects/spring-framework/releases)
- [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12)

---
updated-dependencies:
- dependency-name: org.springframework:spring-framework-bom
  dependency-version: 6.2.12
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-17 13:48:50 -06:00
dependabot[bot] cbad2ff5ca Bump org.springframework.ldap:spring-ldap-core from 3.2.14 to 3.2.15
Bumps [org.springframework.ldap:spring-ldap-core](https://github.com/spring-projects/spring-ldap) from 3.2.14 to 3.2.15.
- [Release notes](https://github.com/spring-projects/spring-ldap/releases)
- [Changelog](https://github.com/spring-projects/spring-ldap/blob/main/changelog.txt)
- [Commits](https://github.com/spring-projects/spring-ldap/compare/3.2.14...3.2.15)

---
updated-dependencies:
- dependency-name: org.springframework.ldap:spring-ldap-core
  dependency-version: 3.2.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-17 13:47:56 -06:00
Rob Winch b75f2582c4 Bump org.hibernate.orm:hibernate-core from 6.6.31.Final to 6.6.33.Final 2025-10-16 12:51:41 -05:00
dependabot[bot] 978459bd1d Bump io.micrometer:micrometer-observation from 1.14.11 to 1.14.12
Bumps [io.micrometer:micrometer-observation](https://github.com/micrometer-metrics/micrometer) from 1.14.11 to 1.14.12.
- [Release notes](https://github.com/micrometer-metrics/micrometer/releases)
- [Commits](https://github.com/micrometer-metrics/micrometer/compare/v1.14.11...v1.14.12)

---
updated-dependencies:
- dependency-name: io.micrometer:micrometer-observation
  dependency-version: 1.14.12
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-14 03:15:43 +00:00
dependabot[bot] 7cc9d2849e Bump org.hibernate.orm:hibernate-core from 6.6.31.Final to 6.6.33.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.31.Final to 6.6.33.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.33/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.31...6.6.33)

---
updated-dependencies:
- dependency-name: org.hibernate.orm:hibernate-core
  dependency-version: 6.6.33.Final
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-13 03:16:24 +00:00
Rob Winch f57c9ffcbb Bump ch.qos.logback:logback-classic from 1.5.18 to 1.5.19 2025-10-06 09:10:34 -05:00
dependabot[bot] dd7f809564 Bump org.hibernate.orm:hibernate-core from 6.6.29.Final to 6.6.31.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.29.Final to 6.6.31.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.31/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.29...6.6.31)

---
updated-dependencies:
- dependency-name: org.hibernate.orm:hibernate-core
  dependency-version: 6.6.31.Final
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-06 03:13:36 +00:00
dependabot[bot] dc5962af16 Bump ch.qos.logback:logback-classic from 1.5.18 to 1.5.19
Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.5.18 to 1.5.19.
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](https://github.com/qos-ch/logback/compare/v_1.5.18...v_1.5.19)

---
updated-dependencies:
- dependency-name: ch.qos.logback:logback-classic
  dependency-version: 1.5.19
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-01 03:20:55 +00:00
dependabot[bot] ae4b2e50e2 Bump org.gretty:gretty from 4.1.7 to 4.1.10
Bumps [org.gretty:gretty](https://github.com/gretty-gradle-plugin/gretty) from 4.1.7 to 4.1.10.
- [Release notes](https://github.com/gretty-gradle-plugin/gretty/releases)
- [Changelog](https://github.com/gretty-gradle-plugin/gretty/blob/master/changes.md)
- [Commits](https://github.com/gretty-gradle-plugin/gretty/compare/v4.1.7...v4.1.10)

---
updated-dependencies:
- dependency-name: org.gretty:gretty
  dependency-version: 4.1.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-22 03:14:46 +00:00
github-actions[bot] b007219055 Next development version 2025-09-17 15:23:57 +00:00
github-actions[bot] c43f1f85ef Release 6.4.11 2025-09-17 14:56:44 +00:00
804 changed files with 3915 additions and 30182 deletions
-2
View File
@@ -1,2 +0,0 @@
require:
members: false
+8 -31
View File
@@ -4,32 +4,6 @@ registries:
type: maven-repository
url: https://repo.spring.io/milestone
updates:
- package-ecosystem: gradle
target-branch: 6.4.x
directory: /
schedule:
interval: daily
time: '03:00'
timezone: Etc/UTC
labels:
- 'type: dependency-upgrade'
registries:
- spring-milestones
ignore:
- dependency-name: com.nimbusds:nimbus-jose-jwt
- dependency-name: org.python:jython
- dependency-name: org.apache.directory.server:*
- dependency-name: org.apache.directory.shared:*
- dependency-name: org.junit:junit-bom
update-types:
- version-update:semver-major
- dependency-name: org.mockito:mockito-bom
update-types:
- version-update:semver-major
- dependency-name: '*'
update-types:
- version-update:semver-major
- version-update:semver-minor
- package-ecosystem: gradle
target-branch: 6.3.x
directory: /
@@ -45,7 +19,6 @@ updates:
- dependency-name: com.nimbusds:nimbus-jose-jwt
- dependency-name: org.python:jython
- dependency-name: org.apache.directory.server:*
- dependency-name: org.apache.directory.shared:*
- dependency-name: org.junit:junit-bom
update-types:
- version-update:semver-major
@@ -71,7 +44,6 @@ updates:
- dependency-name: com.nimbusds:nimbus-jose-jwt
- dependency-name: org.python:jython
- dependency-name: org.apache.directory.server:*
- dependency-name: org.apache.directory.shared:*
- dependency-name: org.junit:junit-bom
update-types:
- version-update:semver-major
@@ -85,7 +57,6 @@ updates:
- dependency-name: '*'
update-types:
- version-update:semver-major
- version-update:semver-minor
- package-ecosystem: github-actions
target-branch: 6.3.x
@@ -95,8 +66,14 @@ updates:
labels:
- 'type: task'
- 'in: build'
ignore:
- dependency-name: sjohnr/*
- package-ecosystem: github-actions
target-branch: main
directory: /
schedule:
interval: weekly
labels:
- 'type: task'
- 'in: build'
- package-ecosystem: github-actions
target-branch: docs-build
directory: /
@@ -26,7 +26,7 @@ jobs:
java-version: '17'
distribution: 'temurin'
- name: Set up Gradle
uses: gradle/gradle-build-action@v2
uses: gradle/gradle-build-action@v3
- name: Upgrade Wrappers
run: ./gradlew clean upgradeGradleWrapperAll --continue -Porg.gradle.java.installations.auto-download=false
env:
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
strategy:
matrix:
# List of active maintenance branches.
branch: [ main, 6.4.x, 6.3.x ]
branch: [ main, 6.3.x, 6.2.x, 5.8.x ]
runs-on: ubuntu-latest
steps:
- name: Checkout
+25 -14
View File
@@ -79,7 +79,7 @@ See https://github.com/spring-projects/spring-security/tree/main#building-from-s
The wiki pages https://github.com/spring-projects/spring-framework/wiki/Code-Style[Code Style] and https://github.com/spring-projects/spring-framework/wiki/IntelliJ-IDEA-Editor-Settings[IntelliJ IDEA Editor Settings] define the source file coding standards we use along with some IDEA editor settings we customize.
To format the code as well as check the style, run `./gradlew format && ./gradlew check`.
To format the code as well as check the style, run `./gradlew format check`.
[[submit-a-pull-request]]
=== Submit a Pull Request
@@ -89,30 +89,41 @@ We are excited for your pull request! :heart:
Please do your best to follow these steps.
Don't worry if you don't get them all correct the first time, we will help you.
1. [[sign-cla]] All commits must include a __Signed-off-by__ trailer at the end of each commit message to indicate that the contributor agrees to the Developer Certificate of Origin.
For additional details, please refer to the blog post https://spring.io/blog/2025/01/06/hello-dco-goodbye-cla-simplifying-contributions-to-spring[Hello DCO, Goodbye CLA: Simplifying Contributions to Spring].
2. [[create-an-issue-list]] Must you https://github.com/spring-projects/spring-security/issues/new/choose[create an issue] first? No, but it is recommended for features and larger bug fixes. It's easier discuss with the team first to determine the right fix or enhancement.
[[sign-cla]]
1. If you have not previously done so, please sign the https://cla.spring.io/sign/spring[Contributor License Agreement].
You will be reminded automatically when you submit the PR.
[[create-an-issue]]
1. Must you https://github.com/spring-projects/spring-security/issues/new/choose[create an issue] first? No, but it is recommended for features and larger bug fixes. It's easier discuss with the team first to determine the right fix or enhancement.
For typos and straightforward bug fixes, starting with a pull request is encouraged.
Please include a description for context and motivation.
Note that the team may close your pull request if it's not a fit for the project.
3. [[choose-a-branch]] Always check out the branch indicated in the milestone and submit pull requests against it (for example, for milestone `5.8.3` use the `5.8.x` branch).
[[choose-a-branch]]
1. Always check out the branch indicated in the milestone and submit pull requests against it (for example, for milestone `5.8.3` use the `5.8.x` branch).
If there is no milestone, choose `main`.
Once merged, the fix will be forwarded-ported to applicable branches including `main`.
4. [[create-a-local-branch]] Create a local branch
[[create-a-local-branch]]
1. Create a local branch
If this is for an issue, consider a branch name with the issue number, like `gh-22276`.
5. [[write-tests]] Add documentation and JUnit Tests for your changes.
6. [[update-copyright]] In all files you edited, if the copyright header is of the form 2002-20xx, update the final copyright year to the current year.
7. [[add-since]] If on `main`, add `@since` JavaDoc attributes to new public APIs that your PR adds
8. [[change-rnc]] If you are updating the XSD, please instead update the RNC file and then run `./gradlew :spring-security-config:rncToXsd`.
9. [[format-code]] For each commit, build the code using `./gradlew format && ./gradlew check`.
[[write-tests]]
1. Add documentation and JUnit Tests for your changes.
[[update-copyright]]
1. In all files you edited, if the copyright header is of the form 2002-20xx, update the final copyright year to the current year.
[[add-since]]
1. If on `main`, add `@since` JavaDoc attributes to new public APIs that your PR adds
[[change-rnc]]
1. If you are updating the XSD, please instead update the RNC file and then run `./gradlew :spring-security-config:rncToXsd`.
[[format-code]]
1. For each commit, build the code using `./gradlew format check`.
This command ensures the code meets most of <<code-style,the style guide>>; a notable exception is import order.
10. [[commit-atomically]] Choose the granularity of your commits consciously and squash commits that represent
[[commit-atomically]]
1. Choose the granularity of your commits consciously and squash commits that represent
multiple edits or corrections of the same logical change.
See https://git-scm.com/book/en/Git-Tools-Rewriting-History[Rewriting History section of Pro Git] for an overview of streamlining the commit history.
11. [[format-commit-messages]] Format commit messages using 55 characters for the subject line, 72 characters per line
[[format-commit-messages]]
1. Format commit messages using 55 characters for the subject line, 72 characters per line
for the description, followed by the issue fixed, for example, `Closes gh-22276`.
See the https://git-scm.com/book/en/Distributed-Git-Contributing-to-a-Project#Commit-Guidelines[Commit Guidelines section of Pro Git] for best practices around commit messages, and use `git log` to see some examples.
Favor imperative tense over present tense (use "Fix" instead of "Fixes"); avoid past tense (use "Fix" instead of "Fixed").
Present tense is preferred.
+
[indent=0]
----
@@ -96,11 +96,7 @@ import org.springframework.util.StringUtils;
* All comparisons and prefixes are case sensitive.
*
* @author Ben Alex
* @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security
* annotations may also prove useful, for example
* {@code @PreAuthorize("hasPermission(#id, ObjectsReturnType.class, read)")}
*/
@Deprecated
public class AclEntryVoter extends AbstractAclVoter {
private static final Log logger = LogFactory.getLog(AclEntryVoter.class);
@@ -20,7 +20,6 @@ import java.util.List;
import org.springframework.security.access.AfterInvocationProvider;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.acls.AclPermissionEvaluator;
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
import org.springframework.security.acls.domain.SidRetrievalStrategyImpl;
import org.springframework.security.acls.model.Acl;
@@ -40,11 +39,7 @@ import org.springframework.util.ObjectUtils;
* services.
*
* @author Ben Alex
* @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security
* annotations may also prove useful, for example
* {@code @PostAuthorize("hasPermission(filterObject, read)")}
*/
@Deprecated
public abstract class AbstractAclProvider implements AfterInvocationProvider {
protected final AclService aclService;
@@ -26,7 +26,6 @@ import org.springframework.core.log.LogMessage;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.AuthorizationServiceException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.acls.AclPermissionEvaluator;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.core.Authentication;
@@ -63,11 +62,7 @@ import org.springframework.security.core.Authentication;
*
* @author Ben Alex
* @author Paulo Neves
* @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security
* annotations may also prove useful, for example
* {@code @PostFilter("hasPermission(filterObject, read)")}
*/
@Deprecated
public class AclEntryAfterInvocationCollectionFilteringProvider extends AbstractAclProvider {
protected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationCollectionFilteringProvider.class);
@@ -27,7 +27,6 @@ import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.acls.AclPermissionEvaluator;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.core.Authentication;
@@ -60,12 +59,7 @@ import org.springframework.security.core.SpringSecurityMessageSource;
* granted and <code>null</code> will be returned.
* <p>
* All comparisons and prefixes are case sensitive.
*
* @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security
* annotations may also prove useful, for example
* {@code @PostAuthorize("hasPermission(filterObject, read)")}
*/
@Deprecated
public class AclEntryAfterInvocationProvider extends AbstractAclProvider implements MessageSourceAware {
protected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationProvider.class);
@@ -32,9 +32,7 @@ import org.springframework.core.log.LogMessage;
*
* @author Ben Alex
* @author Paulo Neves
* @deprecated please see {@code PostFilter}
*/
@Deprecated
class ArrayFilterer<T> implements Filterer<T> {
protected static final Log logger = LogFactory.getLog(ArrayFilterer.class);
@@ -31,9 +31,7 @@ import org.springframework.core.log.LogMessage;
*
* @author Ben Alex
* @author Paulo Neves
* @deprecated please see {@code PostFilter}
*/
@Deprecated
class CollectionFilterer<T> implements Filterer<T> {
protected static final Log logger = LogFactory.getLog(CollectionFilterer.class);
@@ -23,9 +23,7 @@ import java.util.Iterator;
*
* @author Ben Alex
* @author Paulo Neves
* @deprecated please use {@code PreFilter} and {@code @PostFilter} instead
*/
@Deprecated
interface Filterer<T> extends Iterable<T> {
/**
@@ -100,8 +100,8 @@ public class JdbcAclService implements AclService {
@Override
public List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {
Object[] args = { parentIdentity.getIdentifier().toString(), parentIdentity.getType() };
List<ObjectIdentity> objects = this.jdbcOperations.query(this.findChildrenSql,
(rs, rowNum) -> mapObjectIdentityRow(rs), args);
List<ObjectIdentity> objects = this.jdbcOperations.query(this.findChildrenSql, args,
(rs, rowNum) -> mapObjectIdentityRow(rs));
return (!objects.isEmpty()) ? objects : null;
}
@@ -190,7 +190,8 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
* @return the primary key or null if not found
*/
protected Long createOrRetrieveClassPrimaryKey(String type, boolean allowCreate, Class idType) {
List<Long> classIds = this.jdbcOperations.queryForList(this.selectClassPrimaryKey, Long.class, type);
List<Long> classIds = this.jdbcOperations.queryForList(this.selectClassPrimaryKey, new Object[] { type },
Long.class);
if (!classIds.isEmpty()) {
return classIds.get(0);
@@ -241,8 +242,8 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
* @return the primary key or null if not found
*/
protected Long createOrRetrieveSidPrimaryKey(String sidName, boolean sidIsPrincipal, boolean allowCreate) {
List<Long> sidIds = this.jdbcOperations.queryForList(this.selectSidPrimaryKey, Long.class, sidIsPrincipal,
sidName);
List<Long> sidIds = this.jdbcOperations.queryForList(this.selectSidPrimaryKey,
new Object[] { sidIsPrincipal, sidName }, Long.class);
if (!sidIds.isEmpty()) {
return sidIds.get(0);
}
@@ -109,7 +109,7 @@ public class JdbcAclServiceTests {
List<ObjectIdentity> result = new ArrayList<>();
result.add(new ObjectIdentityImpl(Object.class, "5577"));
Object[] args = { "1", "org.springframework.security.acls.jdbc.JdbcAclServiceTests$MockLongIdDomainObject" };
given(this.jdbcOperations.query(anyString(), any(RowMapper.class), eq(args))).willReturn(result);
given(this.jdbcOperations.query(anyString(), eq(args), any(RowMapper.class))).willReturn(result);
ObjectIdentity objectIdentity = new ObjectIdentityImpl(MockLongIdDomainObject.class, 1L);
List<ObjectIdentity> objectIdentities = this.aclService.findChildren(objectIdentity);
assertThat(objectIdentities).hasSize(1);
@@ -51,7 +51,6 @@ import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
@@ -216,8 +215,6 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
public CasAuthenticationFilter() {
super("/login/cas");
RequestMatcher processUri = PathPatternRequestMatcher.withDefaults().matcher("/login/cas");
setRequiresAuthenticationRequestMatcher(processUri);
setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler());
setSecurityContextRepository(this.securityContextRepository);
}
@@ -322,18 +319,6 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
super.setAuthenticationFailureHandler(new CasAuthenticationFailureHandler(failureHandler));
}
/**
* Use this {@code RequestMatcher} to match proxy receptor requests. Without setting
* this matcher, {@link CasAuthenticationFilter} will not capture any proxy receptor
* requets.
* @param proxyReceptorMatcher the {@link RequestMatcher} to use
* @since 6.5
*/
public final void setProxyReceptorMatcher(RequestMatcher proxyReceptorMatcher) {
Assert.notNull(proxyReceptorMatcher, "proxyReceptorMatcher cannot be null");
this.proxyReceptorMatcher = proxyReceptorMatcher;
}
public final void setProxyReceptorUrl(final String proxyReceptorUrl) {
this.proxyReceptorMatcher = new AntPathRequestMatcher("/**" + proxyReceptorUrl);
}
@@ -43,7 +43,6 @@ import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
@@ -79,7 +78,7 @@ public class CasAuthenticationFilterTests {
@Test
public void testNormalOperation() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/login/cas");
MockHttpServletRequest request = new MockHttpServletRequest();
request.setServletPath("/login/cas");
request.addParameter("ticket", "ST-0-ER94xMJmn6pha35CQRoZ");
CasAuthenticationFilter filter = new CasAuthenticationFilter();
@@ -104,7 +103,7 @@ public class CasAuthenticationFilterTests {
String url = "/login/cas";
CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setFilterProcessesUrl(url);
MockHttpServletRequest request = new MockHttpServletRequest("POST", url);
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
request.setServletPath(url);
assertThat(filter.requiresAuthentication(request, response)).isTrue();
@@ -133,11 +132,10 @@ public class CasAuthenticationFilterTests {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setFilterProcessesUrl(url);
filter.setServiceProperties(properties);
MockHttpServletRequest request = new MockHttpServletRequest("POST", url);
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
request.setServletPath(url);
assertThat(filter.requiresAuthentication(request, response)).isTrue();
request = new MockHttpServletRequest("POST", "/other");
request.setServletPath("/other");
assertThat(filter.requiresAuthentication(request, response)).isFalse();
request.setParameter(properties.getArtifactParameter(), "value");
@@ -172,7 +170,7 @@ public class CasAuthenticationFilterTests {
given(manager.authenticate(any(Authentication.class))).willReturn(authentication);
ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setAuthenticateAllArtifacts(true);
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/authenticate");
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("ticket", "ST-1-123");
request.setServletPath("/authenticate");
MockHttpServletResponse response = new MockHttpServletResponse();
@@ -268,20 +266,4 @@ public class CasAuthenticationFilterTests {
verify(securityContextRepository).setContext(any(SecurityContext.class));
}
@Test
public void requiresAuthenticationWhenProxyRequestMatcherThenMatches() {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/pgtCallback");
MockHttpServletResponse response = new MockHttpServletResponse();
request.setServletPath("/pgtCallback");
assertThat(filter.requiresAuthentication(request, response)).isFalse();
filter.setProxyReceptorMatcher(PathPatternRequestMatcher.withDefaults().matcher(request.getServletPath()));
assertThat(filter.requiresAuthentication(request, response)).isFalse();
filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));
assertThat(filter.requiresAuthentication(request, response)).isTrue();
request.setRequestURI("/other");
request.setServletPath("/other");
assertThat(filter.requiresAuthentication(request, response)).isFalse();
}
}
@@ -96,7 +96,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
pc.getReaderContext()
.fatal("You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or "
+ "spring-security-3.1.xsd schema or spring-security-3.2.xsd schema or spring-security-4.0.xsd schema "
+ "with Spring Security 6.5. Please update your schema declarations to the 6.5 schema.",
+ "with Spring Security 6.4. Please update your schema declarations to the 6.4 schema.",
element);
}
String name = pc.getDelegate().getLocalName(element);
@@ -221,7 +221,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
private boolean matchesVersionInternal(Element element) {
String schemaLocation = element.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
return schemaLocation.matches("(?m).*spring-security-6\\.5.*.xsd.*")
return schemaLocation.matches("(?m).*spring-security-6\\.4.*.xsd.*")
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
|| !schemaLocation.matches("(?m).*spring-security.*");
}
@@ -67,7 +67,9 @@ public class AuthenticationManagerBuilder
/**
* Creates a new instance
* @param objectPostProcessor the {@link ObjectPostProcessor} instance to use.
* @param objectPostProcessor the
* {@link org.springframework.security.config.annotation.ObjectPostProcessor} instance
* to use.
*/
public AuthenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
super(objectPostProcessor, true);
@@ -95,10 +95,14 @@ class InitializeUserDetailsBeanManagerConfigurer extends GlobalAuthenticationCon
PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);
CompromisedPasswordChecker passwordChecker = getBeanOrNull(CompromisedPasswordChecker.class);
DaoAuthenticationProvider provider = new DaoAuthenticationProvider(userDetailsService);
DaoAuthenticationProvider provider;
if (passwordEncoder != null) {
provider.setPasswordEncoder(passwordEncoder);
provider = new DaoAuthenticationProvider(passwordEncoder);
}
else {
provider = new DaoAuthenticationProvider();
}
provider.setUserDetailsService(userDetailsService);
if (passwordManager != null) {
provider.setUserDetailsPasswordService(passwordManager);
}
@@ -25,6 +25,7 @@ import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
import org.springframework.security.config.annotation.web.configurers.ChannelSecurityConfigurer;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
@@ -132,7 +133,7 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
/**
* Adds an {@link ObjectPostProcessor} for this class.
* @param objectPostProcessor
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
* @return the {@link ChannelSecurityConfigurer} for further customizations
*/
public LdapAuthenticationProviderConfigurer<B> withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
@@ -36,7 +36,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
public abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, C extends AbstractDaoAuthenticationConfigurer<B, C, U>, U extends UserDetailsService>
extends UserDetailsAwareConfigurer<B, U> {
private DaoAuthenticationProvider provider;
private DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
private final U userDetailsService;
@@ -46,7 +46,7 @@ public abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderMana
*/
AbstractDaoAuthenticationConfigurer(U userDetailsService) {
this.userDetailsService = userDetailsService;
this.provider = new DaoAuthenticationProvider(userDetailsService);
this.provider.setUserDetailsService(userDetailsService);
if (userDetailsService instanceof UserDetailsPasswordService) {
this.provider.setUserDetailsPasswordService((UserDetailsPasswordService) userDetailsService);
}
@@ -17,7 +17,6 @@
package org.springframework.security.config.annotation.method.configuration;
import java.util.ArrayList;
import java.util.List;
import org.aopalliance.intercept.MethodInterceptor;
@@ -32,7 +31,6 @@ import org.springframework.security.aot.hint.SecurityHintsRegistrar;
import org.springframework.security.authorization.AuthorizationProxyFactory;
import org.springframework.security.authorization.method.AuthorizationAdvisor;
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory.TargetVisitor;
import org.springframework.security.authorization.method.AuthorizeReturnObjectMethodInterceptor;
import org.springframework.security.config.Customizer;
@@ -42,23 +40,21 @@ final class AuthorizationProxyConfiguration implements AopInfrastructureBean {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static AuthorizationAdvisorProxyFactory authorizationProxyFactory(
ObjectProvider<AuthorizationAdvisor> authorizationAdvisors, ObjectProvider<TargetVisitor> targetVisitors,
ObjectProvider<Customizer<AuthorizationAdvisorProxyFactory>> customizers) {
List<AuthorizationAdvisor> advisors = new ArrayList<>();
authorizationAdvisors.forEach(advisors::add);
List<TargetVisitor> visitors = new ArrayList<>();
targetVisitors.orderedStream().forEach(visitors::add);
visitors.add(TargetVisitor.defaults());
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory(advisors);
factory.setTargetVisitor(TargetVisitor.of(visitors.toArray(TargetVisitor[]::new)));
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory(new ArrayList<>());
customizers.forEach((c) -> c.customize(factory));
return factory;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static MethodInterceptor authorizeReturnObjectMethodInterceptor() {
return new AuthorizeReturnObjectMethodInterceptor();
static MethodInterceptor authorizeReturnObjectMethodInterceptor(ObjectProvider<AuthorizationAdvisor> provider,
AuthorizationAdvisorProxyFactory authorizationProxyFactory) {
provider.forEach(authorizationProxyFactory::addAdvisor);
AuthorizeReturnObjectMethodInterceptor interceptor = new AuthorizeReturnObjectMethodInterceptor(
authorizationProxyFactory);
authorizationProxyFactory.addAdvisor(interceptor);
return interceptor;
}
@Bean
@@ -1,73 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.method.configuration;
import java.util.Map;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.core.Ordered;
import org.springframework.http.HttpEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
@Configuration
class AuthorizationProxyWebConfiguration {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
AuthorizationAdvisorProxyFactory.TargetVisitor webTargetVisitor() {
return new WebTargetVisitor();
}
static class WebTargetVisitor implements AuthorizationAdvisorProxyFactory.TargetVisitor, Ordered {
private static final int DEFAULT_ORDER = 100;
@Override
public Object visit(AuthorizationAdvisorProxyFactory proxyFactory, Object target) {
if (target instanceof ResponseEntity<?> entity) {
return new ResponseEntity<>(proxyFactory.proxy(entity.getBody()), entity.getHeaders(),
entity.getStatusCode());
}
if (target instanceof HttpEntity<?> entity) {
return new HttpEntity<>(proxyFactory.proxy(entity.getBody()), entity.getHeaders());
}
if (target instanceof ModelAndView mav) {
View view = mav.getView();
String viewName = mav.getViewName();
Map<String, Object> model = (Map<String, Object>) proxyFactory.proxy(mav.getModel());
ModelAndView proxied = (view != null) ? new ModelAndView(view, model)
: new ModelAndView(viewName, model);
proxied.setStatus(mav.getStatus());
return proxied;
}
return null;
}
@Override
public int getOrder() {
return DEFAULT_ORDER;
}
}
}
@@ -0,0 +1,72 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.method.configuration;
import java.util.function.Supplier;
import io.micrometer.observation.ObservationRegistry;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.AuthorizationResult;
import org.springframework.security.authorization.ObservationAuthorizationManager;
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
import org.springframework.security.authorization.method.MethodInvocationResult;
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedHandler;
import org.springframework.security.core.Authentication;
import org.springframework.util.function.SingletonSupplier;
final class DeferringObservationAuthorizationManager<T>
implements AuthorizationManager<T>, MethodAuthorizationDeniedHandler {
private final Supplier<AuthorizationManager<T>> delegate;
private MethodAuthorizationDeniedHandler handler = new ThrowingMethodAuthorizationDeniedHandler();
DeferringObservationAuthorizationManager(ObjectProvider<ObservationRegistry> provider,
AuthorizationManager<T> delegate) {
this.delegate = SingletonSupplier.of(() -> {
ObservationRegistry registry = provider.getIfAvailable(() -> ObservationRegistry.NOOP);
if (registry.isNoop()) {
return delegate;
}
return new ObservationAuthorizationManager<>(registry, delegate);
});
if (delegate instanceof MethodAuthorizationDeniedHandler h) {
this.handler = h;
}
}
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, T object) {
return this.delegate.get().check(authentication, object);
}
@Override
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
return this.handler.handleDeniedInvocation(methodInvocation, authorizationResult);
}
@Override
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
AuthorizationResult authorizationResult) {
return this.handler.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);
}
}
@@ -0,0 +1,73 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.method.configuration;
import java.util.function.Supplier;
import io.micrometer.observation.ObservationRegistry;
import org.aopalliance.intercept.MethodInvocation;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationResult;
import org.springframework.security.authorization.ObservationReactiveAuthorizationManager;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
import org.springframework.security.authorization.method.MethodInvocationResult;
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedHandler;
import org.springframework.security.core.Authentication;
import org.springframework.util.function.SingletonSupplier;
final class DeferringObservationReactiveAuthorizationManager<T>
implements ReactiveAuthorizationManager<T>, MethodAuthorizationDeniedHandler {
private final Supplier<ReactiveAuthorizationManager<T>> delegate;
private MethodAuthorizationDeniedHandler handler = new ThrowingMethodAuthorizationDeniedHandler();
DeferringObservationReactiveAuthorizationManager(ObjectProvider<ObservationRegistry> provider,
ReactiveAuthorizationManager<T> delegate) {
this.delegate = SingletonSupplier.of(() -> {
ObservationRegistry registry = provider.getIfAvailable(() -> ObservationRegistry.NOOP);
if (registry.isNoop()) {
return delegate;
}
return new ObservationReactiveAuthorizationManager<>(registry, delegate);
});
if (delegate instanceof MethodAuthorizationDeniedHandler h) {
this.handler = h;
}
}
@Override
public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, T object) {
return this.delegate.get().check(authentication, object);
}
@Override
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
return this.handler.handleDeniedInvocation(methodInvocation, authorizationResult);
}
@Override
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
AuthorizationResult authorizationResult) {
return this.handler.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);
}
}
@@ -41,9 +41,6 @@ final class MethodSecuritySelector implements ImportSelector {
private static final boolean isDataPresent = ClassUtils
.isPresent("org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar", null);
private static final boolean isWebPresent = ClassUtils
.isPresent("org.springframework.web.servlet.DispatcherServlet", null);
private static final boolean isObservabilityPresent = ClassUtils
.isPresent("io.micrometer.observation.ObservationRegistry", null);
@@ -70,9 +67,6 @@ final class MethodSecuritySelector implements ImportSelector {
if (isDataPresent) {
imports.add(AuthorizationProxyDataConfiguration.class.getName());
}
if (isWebPresent) {
imports.add(AuthorizationProxyWebConfiguration.class.getName());
}
if (isObservabilityPresent) {
imports.add(MethodObservationConfiguration.class.getName());
}
@@ -33,16 +33,12 @@ public enum PayloadInterceptorOrder implements Ordered {
/**
* Where basic authentication is placed.
* @see RSocketSecurity#basicAuthentication(Customizer)
* @deprecated please see {@link PayloadInterceptorOrder#AUTHENTICATION}
*/
@Deprecated
BASIC_AUTHENTICATION,
/**
* Where JWT based authentication is performed.
* @see RSocketSecurity#jwt(Customizer)
* @deprecated please see {@link PayloadInterceptorOrder#AUTHENTICATION}
*/
@Deprecated
JWT_AUTHENTICATION,
/**
* A generic placeholder for other types of authentication.
@@ -40,7 +40,7 @@ import org.springframework.core.ResolvableType;
import org.springframework.http.HttpMethod;
import org.springframework.lang.Nullable;
import org.springframework.security.config.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.ServletRegistrationsSupport.RegistrationMapping;
import org.springframework.security.config.annotation.web.configurers.AbstractConfigAttributeRequestMatcherRegistry;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
@@ -169,7 +169,7 @@ public abstract class AbstractRequestMatcherRegistry<C> {
/**
* Associates a list of {@link RequestMatcher} instances with the
* {@link AbstractRequestMatcherRegistry}
* {@link AbstractConfigAttributeRequestMatcherRegistry}
* @param requestMatchers the {@link RequestMatcher} instances
* @return the object that is chained after creating the {@link RequestMatcher}
*/
@@ -218,14 +218,9 @@ public abstract class AbstractRequestMatcherRegistry<C> {
}
List<RequestMatcher> matchers = new ArrayList<>();
for (String pattern : patterns) {
if (RequestMatcherFactory.usesPathPatterns()) {
matchers.add(RequestMatcherFactory.matcher(method, pattern));
}
else {
AntPathRequestMatcher ant = new AntPathRequestMatcher(pattern, (method != null) ? method.name() : null);
MvcRequestMatcher mvc = createMvcMatchers(method, pattern).get(0);
matchers.add(new DeferredRequestMatcher((c) -> resolve(ant, mvc, c), mvc, ant));
}
AntPathRequestMatcher ant = new AntPathRequestMatcher(pattern, (method != null) ? method.name() : null);
MvcRequestMatcher mvc = createMvcMatchers(method, pattern).get(0);
matchers.add(new DeferredRequestMatcher((c) -> resolve(ant, mvc, c), mvc, ant));
}
return requestMatchers(matchers.toArray(new RequestMatcher[0]));
}
@@ -240,41 +235,111 @@ public abstract class AbstractRequestMatcherRegistry<C> {
}
private RequestMatcher resolve(AntPathRequestMatcher ant, MvcRequestMatcher mvc, ServletContext servletContext) {
ServletRegistrationsSupport registrations = new ServletRegistrationsSupport(servletContext);
Collection<RegistrationMapping> mappings = registrations.mappings();
if (mappings.isEmpty()) {
Map<String, ? extends ServletRegistration> registrations = mappableServletRegistrations(servletContext);
if (registrations.isEmpty()) {
return new DispatcherServletDelegatingRequestMatcher(ant, mvc, new MockMvcRequestMatcher());
}
Collection<RegistrationMapping> dispatcherServletMappings = registrations.dispatcherServletMappings();
if (dispatcherServletMappings.isEmpty()) {
if (!hasDispatcherServlet(registrations)) {
return new DispatcherServletDelegatingRequestMatcher(ant, mvc, new MockMvcRequestMatcher());
}
if (dispatcherServletMappings.size() > 1) {
String errorMessage = computeErrorMessage(servletContext.getServletRegistrations().values());
throw new IllegalArgumentException(errorMessage);
}
RegistrationMapping dispatcherServlet = dispatcherServletMappings.iterator().next();
if (mappings.size() > 1 && !dispatcherServlet.isDefault()) {
String errorMessage = computeErrorMessage(servletContext.getServletRegistrations().values());
throw new IllegalArgumentException(errorMessage);
}
if (dispatcherServlet.isDefault()) {
if (mappings.size() == 1) {
ServletRegistration dispatcherServlet = requireOneRootDispatcherServlet(registrations);
if (dispatcherServlet != null) {
if (registrations.size() == 1) {
return mvc;
}
return new DispatcherServletDelegatingRequestMatcher(ant, mvc);
return new DispatcherServletDelegatingRequestMatcher(ant, mvc, servletContext);
}
dispatcherServlet = requireOnlyPathMappedDispatcherServlet(registrations);
if (dispatcherServlet != null) {
String mapping = dispatcherServlet.getMappings().iterator().next();
mvc.setServletPath(mapping.substring(0, mapping.length() - 2));
return mvc;
}
String errorMessage = computeErrorMessage(registrations.values());
throw new IllegalArgumentException(errorMessage);
}
private Map<String, ? extends ServletRegistration> mappableServletRegistrations(ServletContext servletContext) {
Map<String, ServletRegistration> mappable = new LinkedHashMap<>();
for (Map.Entry<String, ? extends ServletRegistration> entry : servletContext.getServletRegistrations()
.entrySet()) {
if (!entry.getValue().getMappings().isEmpty()) {
mappable.put(entry.getKey(), entry.getValue());
}
}
return mappable;
}
private boolean hasDispatcherServlet(Map<String, ? extends ServletRegistration> registrations) {
if (registrations == null) {
return false;
}
for (ServletRegistration registration : registrations.values()) {
if (isDispatcherServlet(registration)) {
return true;
}
}
return false;
}
private ServletRegistration requireOneRootDispatcherServlet(
Map<String, ? extends ServletRegistration> registrations) {
ServletRegistration rootDispatcherServlet = null;
for (ServletRegistration registration : registrations.values()) {
if (!isDispatcherServlet(registration)) {
continue;
}
if (registration.getMappings().size() > 1) {
return null;
}
if (!"/".equals(registration.getMappings().iterator().next())) {
return null;
}
rootDispatcherServlet = registration;
}
return rootDispatcherServlet;
}
private ServletRegistration requireOnlyPathMappedDispatcherServlet(
Map<String, ? extends ServletRegistration> registrations) {
ServletRegistration pathDispatcherServlet = null;
for (ServletRegistration registration : registrations.values()) {
if (!isDispatcherServlet(registration)) {
return null;
}
if (registration.getMappings().size() > 1) {
return null;
}
String mapping = registration.getMappings().iterator().next();
if (!mapping.startsWith("/") || !mapping.endsWith("/*")) {
return null;
}
if (pathDispatcherServlet != null) {
return null;
}
pathDispatcherServlet = registration;
}
return pathDispatcherServlet;
}
private boolean isDispatcherServlet(ServletRegistration registration) {
Class<?> dispatcherServlet = ClassUtils.resolveClassName("org.springframework.web.servlet.DispatcherServlet",
null);
try {
Class<?> clazz = Class.forName(registration.getClassName());
return dispatcherServlet.isAssignableFrom(clazz);
}
catch (ClassNotFoundException ex) {
return false;
}
return mvc;
}
private static String computeErrorMessage(Collection<? extends ServletRegistration> registrations) {
String template = """
This method cannot decide whether these patterns are Spring MVC patterns or not. \
This is because there is more than one mappable servlet in your servlet context: %s.
To address this, please create one PathPatternRequestMatcher.Builder#servletPath for each servlet that has \
authorized endpoints and use them to construct request matchers manually.
""";
String template = "This method cannot decide whether these patterns are Spring MVC patterns or not. "
+ "If this endpoint is a Spring MVC endpoint, please use requestMatchers(MvcRequestMatcher); "
+ "otherwise, please use requestMatchers(AntPathRequestMatcher).\n\n"
+ "This is because there is more than one mappable servlet in your servlet context: %s.\n\n"
+ "For each MvcRequestMatcher, call MvcRequestMatcher#setServletPath to indicate the servlet path.";
Map<String, Collection<String>> mappings = new LinkedHashMap<>();
for (ServletRegistration registration : registrations) {
mappings.put(registration.getClassName(), registration.getMappings());
@@ -453,12 +518,18 @@ public abstract class AbstractRequestMatcherRegistry<C> {
static class DispatcherServletRequestMatcher implements RequestMatcher {
private final ServletContext servletContext;
DispatcherServletRequestMatcher(ServletContext servletContext) {
this.servletContext = servletContext;
}
@Override
public boolean matches(HttpServletRequest request) {
String name = request.getHttpServletMapping().getServletName();
ServletRegistration registration = request.getServletContext().getServletRegistration(name);
ServletRegistration registration = this.servletContext.getServletRegistration(name);
Assert.notNull(registration,
() -> computeErrorMessage(request.getServletContext().getServletRegistrations().values()));
() -> computeErrorMessage(this.servletContext.getServletRegistrations().values()));
try {
Class<?> clazz = Class.forName(registration.getClassName());
return DispatcherServlet.class.isAssignableFrom(clazz);
@@ -478,8 +549,10 @@ public abstract class AbstractRequestMatcherRegistry<C> {
private final RequestMatcher dispatcherServlet;
DispatcherServletDelegatingRequestMatcher(AntPathRequestMatcher ant, MvcRequestMatcher mvc) {
this(ant, mvc, new OrRequestMatcher(new MockMvcRequestMatcher(), new DispatcherServletRequestMatcher()));
DispatcherServletDelegatingRequestMatcher(AntPathRequestMatcher ant, MvcRequestMatcher mvc,
ServletContext servletContext) {
this(ant, mvc, new OrRequestMatcher(new MockMvcRequestMatcher(),
new DispatcherServletRequestMatcher(servletContext)));
}
DispatcherServletDelegatingRequestMatcher(AntPathRequestMatcher ant, MvcRequestMatcher mvc,
@@ -135,7 +135,6 @@ public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>>
* <li>{@link DisableEncodeUrlFilter}</li>
* <li>{@link ForceEagerSessionCreationFilter}</li>
* <li>{@link ChannelProcessingFilter}</li>
* <li>{@link org.springframework.security.web.transport.HttpsRedirectFilter}</li>
* <li>{@link WebAsyncManagerIntegrationFilter}</li>
* <li>{@link SecurityContextHolderFilter}</li>
* <li>{@link SecurityContextPersistenceFilter}</li>
@@ -1,59 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpMethod;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
/**
* This utility exists only to facilitate applications opting into using path patterns in
* the HttpSecurity DSL. It is for internal use only.
*
* @deprecated
*/
@Deprecated(forRemoval = true)
public final class RequestMatcherFactory {
private static PathPatternRequestMatcher.Builder builder;
public static void setApplicationContext(ApplicationContext context) {
builder = context.getBeanProvider(PathPatternRequestMatcher.Builder.class).getIfUnique();
}
public static boolean usesPathPatterns() {
return builder != null;
}
public static RequestMatcher matcher(String path) {
return matcher(null, path);
}
public static RequestMatcher matcher(HttpMethod method, String path) {
if (builder != null) {
return builder.matcher(method, path);
}
return new AntPathRequestMatcher(path, (method != null) ? method.name() : null);
}
private RequestMatcherFactory() {
}
}
@@ -1,77 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletRegistration;
import org.springframework.util.ClassUtils;
class ServletRegistrationsSupport {
private final Collection<RegistrationMapping> registrations;
ServletRegistrationsSupport(ServletContext servletContext) {
Map<String, ? extends ServletRegistration> registrations = servletContext.getServletRegistrations();
Collection<RegistrationMapping> mappings = new ArrayList<>();
for (Map.Entry<String, ? extends ServletRegistration> entry : registrations.entrySet()) {
if (!entry.getValue().getMappings().isEmpty()) {
for (String mapping : entry.getValue().getMappings()) {
mappings.add(new RegistrationMapping(entry.getValue(), mapping));
}
}
}
this.registrations = mappings;
}
Collection<RegistrationMapping> dispatcherServletMappings() {
Collection<RegistrationMapping> mappings = new ArrayList<>();
for (RegistrationMapping registration : this.registrations) {
if (registration.isDispatcherServlet()) {
mappings.add(registration);
}
}
return mappings;
}
Collection<RegistrationMapping> mappings() {
return this.registrations;
}
record RegistrationMapping(ServletRegistration registration, String mapping) {
boolean isDispatcherServlet() {
Class<?> dispatcherServlet = ClassUtils
.resolveClassName("org.springframework.web.servlet.DispatcherServlet", null);
try {
Class<?> clazz = Class.forName(this.registration.getClassName());
return dispatcherServlet.isAssignableFrom(clazz);
}
catch (ClassNotFoundException ex) {
return false;
}
}
boolean isDefault() {
return "/".equals(this.mapping);
}
}
}
@@ -31,7 +31,6 @@ import org.springframework.security.web.authentication.AuthenticationFilter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.ott.GenerateOneTimeTokenFilter;
import org.springframework.security.web.authentication.ott.OneTimeTokenAuthenticationFilter;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;
@@ -54,7 +53,6 @@ import org.springframework.security.web.session.ConcurrentSessionFilter;
import org.springframework.security.web.session.DisableEncodeUrlFilter;
import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
import org.springframework.security.web.session.SessionManagementFilter;
import org.springframework.security.web.transport.HttpsRedirectFilter;
import org.springframework.web.filter.CorsFilter;
/**
@@ -79,7 +77,6 @@ final class FilterOrderRegistration {
put(DisableEncodeUrlFilter.class, order.next());
put(ForceEagerSessionCreationFilter.class, order.next());
put(ChannelProcessingFilter.class, order.next());
put(HttpsRedirectFilter.class, order.next());
order.next(); // gh-8105
put(WebAsyncManagerIntegrationFilter.class, order.next());
put(SecurityContextHolderFilter.class, order.next());
@@ -104,7 +101,6 @@ final class FilterOrderRegistration {
"org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter",
order.next());
put(UsernamePasswordAuthenticationFilter.class, order.next());
put(OneTimeTokenAuthenticationFilter.class, order.next());
order.next(); // gh-8105
put(DefaultResourcesFilter.class, order.next());
put(DefaultLoginPageGeneratingFilter.class, order.next());
@@ -45,7 +45,6 @@ import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
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.configurers.AnonymousConfigurer;
@@ -59,7 +58,6 @@ import org.springframework.security.config.annotation.web.configurers.Expression
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer;
import org.springframework.security.config.annotation.web.configurers.HttpsRedirectConfigurer;
import org.springframework.security.config.annotation.web.configurers.JeeConfigurer;
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
import org.springframework.security.config.annotation.web.configurers.PasswordManagementConfigurer;
@@ -3137,9 +3135,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* the {@link ChannelSecurityConfigurer.ChannelRequestMatcherRegistry}
* @return the {@link HttpSecurity} for further customizations
* @throws Exception
* @deprecated Use {@link #redirectToHttps}
*/
@Deprecated
public HttpSecurity requiresChannel(
Customizer<ChannelSecurityConfigurer<HttpSecurity>.ChannelRequestMatcherRegistry> requiresChannelCustomizer)
throws Exception {
@@ -3148,53 +3144,6 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
return HttpSecurity.this;
}
/**
* Configures channel security. In order for this configuration to be useful at least
* one mapping to a required channel must be provided.
*
* <h2>Example Configuration</h2>
*
* The example below demonstrates how to require HTTPS for every request. Only
* requiring HTTPS for some requests is supported, for example if you need to
* differentiate between local and production deployments.
*
* <pre>
* &#064;Configuration
* &#064;EnableWebSecurity
* public class RequireHttpsConfig {
*
* &#064;Bean
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
* http
* .authorizeHttpRequests((authorize) -&gt; authorize
* anyRequest().authenticated()
* )
* .formLogin(withDefaults())
* .redirectToHttps(withDefaults());
* return http.build();
* }
*
* &#064;Bean
* public UserDetailsService userDetailsService() {
* UserDetails user = User.withDefaultPasswordEncoder()
* .username(&quot;user&quot;)
* .password(&quot;password&quot;)
* .roles(&quot;USER&quot;)
* .build();
* return new InMemoryUserDetailsManager(user);
* }
* }
* </pre>
* @param httpsRedirectConfigurerCustomizer the {@link Customizer} to provide more
* options for the {@link HttpsRedirectConfigurer}
* @return the {@link HttpSecurity} for further customizations
*/
public HttpSecurity redirectToHttps(
Customizer<HttpsRedirectConfigurer<HttpSecurity>> httpsRedirectConfigurerCustomizer) throws Exception {
httpsRedirectConfigurerCustomizer.customize(getOrApply(new HttpsRedirectConfigurer<>()));
return HttpSecurity.this;
}
/**
* Configures HTTP Basic authentication.
*
@@ -3706,8 +3655,8 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* {@link #securityMatcher(String...)}, {@link #securityMatchers(Customizer)} and
* {@link #securityMatchers()}
* </p>
* @param requestMatcher the {@link RequestMatcher} to use, for example,
* {@code PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.GET, "/admin/**")}
* @param requestMatcher the {@link RequestMatcher} to use (i.e. new
* AntPathRequestMatcher("/admin/**","GET") )
* @return the {@link HttpSecurity} for further customizations
* @see #securityMatcher(String...)
*/
@@ -3735,17 +3684,11 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* @see MvcRequestMatcher
*/
public HttpSecurity securityMatcher(String... patterns) {
List<RequestMatcher> matchers = new ArrayList<>();
for (String pattern : patterns) {
if (RequestMatcherFactory.usesPathPatterns()) {
matchers.add(RequestMatcherFactory.matcher(pattern));
}
else {
RequestMatcher matcher = mvcPresent ? createMvcMatcher(pattern) : createAntMatcher(pattern);
matchers.add(matcher);
}
if (mvcPresent) {
this.requestMatcher = new OrRequestMatcher(createMvcMatchers(patterns));
return this;
}
this.requestMatcher = new OrRequestMatcher(matchers);
this.requestMatcher = new OrRequestMatcher(createAntMatchers(patterns));
return this;
}
@@ -3758,7 +3701,6 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* http
* // ...
* .webAuthn((webAuthn) -&gt; webAuthn
* .rpName("Spring Security Relying Party")
* .rpId("example.com")
* .allowedOrigins("https://example.com")
* );
@@ -3774,11 +3716,15 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
return HttpSecurity.this;
}
private RequestMatcher createAntMatcher(String pattern) {
return new AntPathRequestMatcher(pattern);
private List<RequestMatcher> createAntMatchers(String... patterns) {
List<RequestMatcher> matchers = new ArrayList<>(patterns.length);
for (String pattern : patterns) {
matchers.add(new AntPathRequestMatcher(pattern));
}
return matchers;
}
private RequestMatcher createMvcMatcher(String mvcPattern) {
private List<RequestMatcher> createMvcMatchers(String... mvcPatterns) {
ResolvableType type = ResolvableType.forClassWithGenerics(ObjectPostProcessor.class, Object.class);
ObjectProvider<ObjectPostProcessor<Object>> postProcessors = getContext().getBeanProvider(type);
ObjectPostProcessor<Object> opp = postProcessors.getObject();
@@ -3789,9 +3735,13 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
}
HandlerMappingIntrospector introspector = getContext().getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME,
HandlerMappingIntrospector.class);
MvcRequestMatcher matcher = new MvcRequestMatcher(introspector, mvcPattern);
opp.postProcess(matcher);
return matcher;
List<RequestMatcher> matchers = new ArrayList<>(mvcPatterns.length);
for (String mvcPattern : mvcPatterns) {
MvcRequestMatcher matcher = new MvcRequestMatcher(introspector, mvcPattern);
opp.postProcess(matcher);
matchers.add(matcher);
}
return matchers;
}
/**
@@ -35,9 +35,7 @@ import org.springframework.core.ResolvableType;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.SingleResultAuthorizationManager;
import org.springframework.security.config.ObjectPostProcessor;
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
import org.springframework.security.config.annotation.SecurityBuilder;
@@ -60,8 +58,6 @@ import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.access.intercept.AuthorizationFilter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager;
import org.springframework.security.web.debug.DebugFilter;
import org.springframework.security.web.firewall.CompositeRequestRejectedHandler;
import org.springframework.security.web.firewall.HttpFirewall;
@@ -235,8 +231,8 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
/**
* Set the {@link WebInvocationPrivilegeEvaluator} to be used. If this is not
* specified, then a {@link AuthorizationManagerWebInvocationPrivilegeEvaluator} will
* be created based on the list of {@link SecurityFilterChain}.
* specified, then a {@link RequestMatcherDelegatingWebInvocationPrivilegeEvaluator}
* will be created based on the list of {@link SecurityFilterChain}.
* @param privilegeEvaluator the {@link WebInvocationPrivilegeEvaluator} to use
* @return the {@link WebSecurity} for further customizations
*/
@@ -305,33 +301,36 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
+ ".addSecurityFilterChainBuilder directly");
int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
RequestMatcherDelegatingAuthorizationManager.Builder builder = RequestMatcherDelegatingAuthorizationManager
.builder();
boolean mappings = false;
List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries = new ArrayList<>();
for (RequestMatcher ignoredRequest : this.ignoredRequests) {
WebSecurity.this.logger.warn("You are asking Spring Security to ignore " + ignoredRequest
+ ". This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.");
SecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest);
securityFilterChains.add(securityFilterChain);
builder.add(ignoredRequest, SingleResultAuthorizationManager.permitAll());
mappings = true;
requestMatcherPrivilegeEvaluatorsEntries
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
}
DefaultSecurityFilterChain anyRequestFilterChain = null;
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
if (anyRequestFilterChain != null) {
String message = "A filter chain that matches any request [" + anyRequestFilterChain
+ "] has already been configured, which means that this filter chain [" + securityFilterChain
+ "] will never get invoked. Please use `HttpSecurity#securityMatcher` to ensure that there is only one filter chain configured for 'any request' and that the 'any request' filter chain is published last.";
throw new IllegalArgumentException(message);
}
if (securityFilterChain instanceof DefaultSecurityFilterChain defaultSecurityFilterChain) {
if (defaultSecurityFilterChain.getRequestMatcher() instanceof AnyRequestMatcher) {
anyRequestFilterChain = defaultSecurityFilterChain;
}
}
securityFilterChains.add(securityFilterChain);
mappings = addAuthorizationManager(securityFilterChain, builder) || mappings;
requestMatcherPrivilegeEvaluatorsEntries
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
}
if (this.privilegeEvaluator == null) {
AuthorizationManager<HttpServletRequest> authorizationManager = mappings ? builder.build()
: SingleResultAuthorizationManager.permitAll();
AuthorizationManagerWebInvocationPrivilegeEvaluator privilegeEvaluator = new AuthorizationManagerWebInvocationPrivilegeEvaluator(
authorizationManager);
privilegeEvaluator.setServletContext(this.servletContext);
if (this.privilegeEvaluatorRequestTransformer != null) {
privilegeEvaluator.setRequestTransformer(this.privilegeEvaluatorRequestTransformer);
}
this.privilegeEvaluator = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
List.of(new RequestMatcherEntry<>(AnyRequestMatcher.INSTANCE, List.of(privilegeEvaluator))));
requestMatcherPrivilegeEvaluatorsEntries);
}
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (this.httpFirewall != null) {
@@ -346,7 +345,6 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
new HttpStatusRequestRejectedHandler());
filterChainProxy.setRequestRejectedHandler(requestRejectedHandler);
}
filterChainProxy.setFilterChainValidator(new WebSecurityFilterChainValidator());
filterChainProxy.setFilterChainDecorator(getFilterChainDecorator());
filterChainProxy.afterPropertiesSet();
@@ -364,33 +362,30 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
return result;
}
private boolean addAuthorizationManager(SecurityFilterChain securityFilterChain,
RequestMatcherDelegatingAuthorizationManager.Builder builder) {
boolean mappings = false;
private RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> getRequestMatcherPrivilegeEvaluatorsEntry(
SecurityFilterChain securityFilterChain) {
List<WebInvocationPrivilegeEvaluator> privilegeEvaluators = new ArrayList<>();
for (Filter filter : securityFilterChain.getFilters()) {
if (filter instanceof FilterSecurityInterceptor securityInterceptor) {
DefaultWebInvocationPrivilegeEvaluator privilegeEvaluator = new DefaultWebInvocationPrivilegeEvaluator(
securityInterceptor);
privilegeEvaluator.setServletContext(this.servletContext);
AuthorizationManager<RequestAuthorizationContext> authorizationManager = (authentication, context) -> {
HttpServletRequest request = context.getRequest();
boolean result = privilegeEvaluator.isAllowed(request.getContextPath(), request.getRequestURI(),
request.getMethod(), authentication.get());
return new AuthorizationDecision(result);
};
builder.add(securityFilterChain::matches, authorizationManager);
mappings = true;
if (filter instanceof FilterSecurityInterceptor) {
DefaultWebInvocationPrivilegeEvaluator defaultWebInvocationPrivilegeEvaluator = new DefaultWebInvocationPrivilegeEvaluator(
(FilterSecurityInterceptor) filter);
defaultWebInvocationPrivilegeEvaluator.setServletContext(this.servletContext);
privilegeEvaluators.add(defaultWebInvocationPrivilegeEvaluator);
continue;
}
if (filter instanceof AuthorizationFilter authorization) {
AuthorizationManager<HttpServletRequest> authorizationManager = authorization.getAuthorizationManager();
builder.add(securityFilterChain::matches,
(authentication, context) -> (AuthorizationDecision) authorizationManager
.authorize(authentication, context.getRequest()));
mappings = true;
if (filter instanceof AuthorizationFilter) {
AuthorizationManager<HttpServletRequest> authorizationManager = ((AuthorizationFilter) filter)
.getAuthorizationManager();
AuthorizationManagerWebInvocationPrivilegeEvaluator evaluator = new AuthorizationManagerWebInvocationPrivilegeEvaluator(
authorizationManager);
evaluator.setServletContext(this.servletContext);
if (this.privilegeEvaluatorRequestTransformer != null) {
evaluator.setRequestTransformer(this.privilegeEvaluatorRequestTransformer);
}
privilegeEvaluators.add(evaluator);
}
}
return mappings;
return new RequestMatcherEntry<>(securityFilterChain::matches, privilegeEvaluators);
}
@Override
@@ -1,113 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.builders;
import java.util.List;
import jakarta.servlet.Filter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.UnreachableFilterChainException;
import org.springframework.security.web.access.intercept.AuthorizationFilter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
/**
* A filter chain validator for filter chains built by {@link WebSecurity}
*
* @author Josh Cummings
* @author Max Batischev
* @since 6.5
*/
final class WebSecurityFilterChainValidator implements FilterChainProxy.FilterChainValidator {
private final Log logger = LogFactory.getLog(getClass());
@Override
public void validate(FilterChainProxy filterChainProxy) {
List<SecurityFilterChain> chains = filterChainProxy.getFilterChains();
checkForAnyRequestRequestMatcher(chains);
checkForDuplicateMatchers(chains);
checkAuthorizationFilters(chains);
}
private void checkForAnyRequestRequestMatcher(List<SecurityFilterChain> chains) {
DefaultSecurityFilterChain anyRequestFilterChain = null;
for (SecurityFilterChain chain : chains) {
if (anyRequestFilterChain != null) {
String message = "A filter chain that matches any request [" + anyRequestFilterChain
+ "] has already been configured, which means that this filter chain [" + chain
+ "] will never get invoked. Please use `HttpSecurity#securityMatcher` to ensure that there is only one filter chain configured for 'any request' and that the 'any request' filter chain is published last.";
throw new UnreachableFilterChainException(message, anyRequestFilterChain, chain);
}
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
if (defaultChain.getRequestMatcher() instanceof AnyRequestMatcher) {
anyRequestFilterChain = defaultChain;
}
}
}
}
private void checkForDuplicateMatchers(List<SecurityFilterChain> chains) {
DefaultSecurityFilterChain filterChain = null;
for (SecurityFilterChain chain : chains) {
if (filterChain != null) {
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
if (defaultChain.getRequestMatcher().equals(filterChain.getRequestMatcher())) {
throw new UnreachableFilterChainException(
"The FilterChainProxy contains two filter chains using the" + " matcher "
+ defaultChain.getRequestMatcher(),
filterChain, defaultChain);
}
}
}
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
filterChain = defaultChain;
}
}
}
private void checkAuthorizationFilters(List<SecurityFilterChain> chains) {
Filter authorizationFilter = null;
Filter filterSecurityInterceptor = null;
for (SecurityFilterChain chain : chains) {
for (Filter filter : chain.getFilters()) {
if (filter instanceof AuthorizationFilter) {
authorizationFilter = filter;
}
if (filter instanceof FilterSecurityInterceptor) {
filterSecurityInterceptor = filter;
}
}
if (authorizationFilter != null && filterSecurityInterceptor != null) {
this.logger.warn(
"It is not recommended to use authorizeRequests in the configuration. Please only use authorizeHttpRequests");
}
if (filterSecurityInterceptor != null) {
this.logger.warn(
"Usage of authorizeRequests is deprecated. Please use authorizeHttpRequests in the configuration");
}
authorizationFilter = null;
filterSecurityInterceptor = null;
}
}
}
@@ -35,7 +35,6 @@ import org.springframework.security.config.annotation.authentication.configurati
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
import org.springframework.security.config.annotation.authentication.configurers.provisioning.JdbcUserDetailsManagerConfigurer;
import org.springframework.security.config.annotation.authentication.configurers.userdetails.DaoAuthenticationConfigurer;
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.DefaultLoginPageConfigurer;
@@ -105,7 +104,6 @@ class HttpSecurityConfiguration {
@Bean(HTTPSECURITY_BEAN_NAME)
@Scope("prototype")
HttpSecurity httpSecurity() throws Exception {
RequestMatcherFactory.setApplicationContext(this.context);
LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(this.context);
AuthenticationManagerBuilder authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(
this.objectPostProcessor, passwordEncoder);
@@ -34,8 +34,6 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -162,7 +160,7 @@ final class OAuth2ClientConfiguration {
* @since 6.2.0
*/
static final class OAuth2AuthorizedClientManagerRegistrar
implements ApplicationEventPublisherAware, BeanDefinitionRegistryPostProcessor, BeanFactoryAware {
implements BeanDefinitionRegistryPostProcessor, BeanFactoryAware {
static final String BEAN_NAME = "authorizedClientManagerRegistrar";
@@ -181,8 +179,6 @@ final class OAuth2ClientConfiguration {
private final AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
private ApplicationEventPublisher applicationEventPublisher;
private ListableBeanFactory beanFactory;
@Override
@@ -306,10 +302,6 @@ final class OAuth2ClientConfiguration {
authorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient);
}
if (this.applicationEventPublisher != null) {
authorizedClientProvider.setApplicationEventPublisher(this.applicationEventPublisher);
}
return authorizedClientProvider;
}
@@ -431,11 +423,6 @@ final class OAuth2ClientConfiguration {
return objectProvider.getIfAvailable();
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
}
}
@@ -78,8 +78,6 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
private static final String PATH_PATTERN_REQUEST_TRANSFORMER_BEAN_NAME = "pathPatternRequestTransformer";
private BeanResolver beanResolver;
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
@@ -121,6 +119,17 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
}
}
/**
* Used to ensure Spring MVC request matching is cached.
*
* Creates a {@link BeanDefinitionRegistryPostProcessor} that detects if a bean named
* HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME is defined. If so, it moves the
* AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME to another bean name
* and then adds a {@link CompositeFilter} that contains
* {@link HandlerMappingIntrospector#createCacheFilter()} and the original
* FilterChainProxy under the original Bean name.
* @return
*/
@Bean
static BeanDefinitionRegistryPostProcessor springSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor() {
return new BeanDefinitionRegistryPostProcessor() {
@@ -135,15 +144,12 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
}
String hmiRequestTransformerBeanName = HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME + "RequestTransformer";
if (!registry.containsBeanDefinition(PATH_PATTERN_REQUEST_TRANSFORMER_BEAN_NAME)
&& !registry.containsBeanDefinition(hmiRequestTransformerBeanName)) {
if (!registry.containsBeanDefinition(hmiRequestTransformerBeanName)) {
BeanDefinition hmiRequestTransformer = BeanDefinitionBuilder
.rootBeanDefinition(HandlerMappingIntrospectorRequestTransformer.class)
.addConstructorArgReference(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)
.getBeanDefinition();
registry.registerBeanDefinition(hmiRequestTransformerBeanName, hmiRequestTransformer);
}
if (!registry.containsBeanDefinition(hmiRequestTransformerBeanName)) {
BeanDefinition hmiRequestTransformer = BeanDefinitionBuilder
.rootBeanDefinition(HandlerMappingIntrospectorRequestTransformer.class)
.addConstructorArgReference(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)
.getBeanDefinition();
registry.registerBeanDefinition(hmiRequestTransformerBeanName, hmiRequestTransformer);
}
BeanDefinition filterChainProxy = registry
@@ -172,11 +178,7 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
/**
* {@link FactoryBean} to defer creation of
* {@link HandlerMappingIntrospector#createCacheFilter()}
*
* @deprecated see {@link WebSecurityConfiguration} for
* {@link org.springframework.web.util.pattern.PathPattern} replacement
*/
@Deprecated
static class HandlerMappingIntrospectorCacheFilterFactoryBean
implements ApplicationContextAware, FactoryBean<Filter> {
@@ -205,11 +207,7 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
* Extends {@link FilterChainProxy} to provide as much passivity as possible but
* delegates to {@link CompositeFilter} for
* {@link #doFilter(ServletRequest, ServletResponse, FilterChain)}.
*
* @deprecated see {@link WebSecurityConfiguration} for
* {@link org.springframework.web.util.pattern.PathPattern} replacement
*/
@Deprecated
static class CompositeFilterChainProxy extends FilterChainProxy {
/**
@@ -16,29 +16,16 @@
package org.springframework.security.config.annotation.web.configuration;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
@@ -58,18 +45,11 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor;
import org.springframework.security.context.DelegatingApplicationListener;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
import org.springframework.security.web.debug.DebugFilter;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.RequestRejectedHandler;
import org.springframework.web.filter.CompositeFilter;
import org.springframework.web.filter.ServletRequestPathFilter;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
/**
* Uses a {@link WebSecurity} to create the {@link FilterChainProxy} that performs the web
@@ -85,16 +65,23 @@ import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
* @see WebSecurity
*/
@Configuration(proxyBeanMethods = false)
public class WebSecurityConfiguration implements ImportAware {
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
private WebSecurity webSecurity;
private Boolean debugEnabled;
private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
private List<SecurityFilterChain> securityFilterChains = Collections.emptyList();
private List<WebSecurityCustomizer> webSecurityCustomizers = Collections.emptyList();
private ClassLoader beanClassLoader;
@Autowired(required = false)
private HttpSecurity httpSecurity;
@Bean
public static DelegatingApplicationListener delegatingApplicationListener() {
return new DelegatingApplicationListener();
@@ -112,15 +99,14 @@ public class WebSecurityConfiguration implements ImportAware {
* @throws Exception
*/
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain(ObjectProvider<HttpSecurity> provider) throws Exception {
public Filter springSecurityFilterChain() throws Exception {
boolean hasFilterChain = !this.securityFilterChains.isEmpty();
if (!hasFilterChain) {
this.webSecurity.addSecurityFilterChainBuilder(() -> {
HttpSecurity httpSecurity = provider.getObject();
httpSecurity.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated());
httpSecurity.formLogin(Customizer.withDefaults());
httpSecurity.httpBasic(Customizer.withDefaults());
return httpSecurity.build();
this.httpSecurity.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated());
this.httpSecurity.formLogin(Customizer.withDefaults());
this.httpSecurity.httpBasic(Customizer.withDefaults());
return this.httpSecurity.build();
});
}
for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
@@ -178,6 +164,7 @@ public class WebSecurityConfiguration implements ImportAware {
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
this.webSecurity.apply(webSecurityConfigurer);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}
@Autowired(required = false)
@@ -206,49 +193,9 @@ public class WebSecurityConfiguration implements ImportAware {
}
}
/**
* Used to ensure Spring MVC request matching is cached.
*
* Creates a {@link BeanDefinitionRegistryPostProcessor} that detects if a bean named
* HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME is defined. If so, it moves the
* AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME to another bean name
* and then adds a {@link CompositeFilter} that contains
* {@link HandlerMappingIntrospector#createCacheFilter()} and the original
* FilterChainProxy under the original Bean name.
* @return
*/
@Bean
static BeanDefinitionRegistryPostProcessor springSecurityPathPatternParserBeanDefinitionRegistryPostProcessor() {
return new BeanDefinitionRegistryPostProcessor() {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
BeanDefinition filterChainProxy = registry
.getBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
if (filterChainProxy.getResolvableType().isInstance(CompositeFilterChainProxy.class)) {
return;
}
BeanDefinitionBuilder pppCacheFilterBldr = BeanDefinitionBuilder
.rootBeanDefinition(ServletRequestPathFilter.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
ManagedList<BeanMetadataElement> filters = new ManagedList<>();
filters.add(pppCacheFilterBldr.getBeanDefinition());
filters.add(filterChainProxy);
BeanDefinitionBuilder compositeSpringSecurityFilterChainBldr = BeanDefinitionBuilder
.rootBeanDefinition(CompositeFilterChainProxy.class)
.addConstructorArgValue(filters);
registry.removeBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
registry.registerBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME,
compositeSpringSecurityFilterChainBldr.getBeanDefinition());
}
};
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
/**
@@ -284,121 +231,4 @@ public class WebSecurityConfiguration implements ImportAware {
}
/**
* Extends {@link FilterChainProxy} to provide as much passivity as possible but
* delegates to {@link CompositeFilter} for
* {@link #doFilter(ServletRequest, ServletResponse, FilterChain)}.
*/
static class CompositeFilterChainProxy extends FilterChainProxy {
/**
* Used for {@link #doFilter(ServletRequest, ServletResponse, FilterChain)}
*/
private final Filter doFilterDelegate;
private final FilterChainProxy springSecurityFilterChain;
/**
* Creates a new instance
* @param filters the Filters to delegate to. One of which must be
* FilterChainProxy.
*/
CompositeFilterChainProxy(List<? extends Filter> filters) {
this.doFilterDelegate = createDoFilterDelegate(filters);
this.springSecurityFilterChain = findFilterChainProxy(filters);
}
@Override
public void afterPropertiesSet() {
this.springSecurityFilterChain.afterPropertiesSet();
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
this.doFilterDelegate.doFilter(request, response, chain);
}
@Override
public List<Filter> getFilters(String url) {
return this.springSecurityFilterChain.getFilters(url);
}
@Override
public List<SecurityFilterChain> getFilterChains() {
return this.springSecurityFilterChain.getFilterChains();
}
@Override
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
this.springSecurityFilterChain.setSecurityContextHolderStrategy(securityContextHolderStrategy);
}
@Override
public void setFilterChainValidator(FilterChainValidator filterChainValidator) {
this.springSecurityFilterChain.setFilterChainValidator(filterChainValidator);
}
@Override
public void setFilterChainDecorator(FilterChainDecorator filterChainDecorator) {
this.springSecurityFilterChain.setFilterChainDecorator(filterChainDecorator);
}
@Override
public void setFirewall(HttpFirewall firewall) {
this.springSecurityFilterChain.setFirewall(firewall);
}
@Override
public void setRequestRejectedHandler(RequestRejectedHandler requestRejectedHandler) {
this.springSecurityFilterChain.setRequestRejectedHandler(requestRejectedHandler);
}
/**
* Used through reflection by Spring Security's Test support to lookup the
* FilterChainProxy Filters for a specific HttpServletRequest.
* @param request
* @return
*/
private List<? extends Filter> getFilters(HttpServletRequest request) {
List<SecurityFilterChain> filterChains = this.springSecurityFilterChain.getFilterChains();
for (SecurityFilterChain chain : filterChains) {
if (chain.matches(request)) {
return chain.getFilters();
}
}
return null;
}
/**
* Creates the Filter to delegate to for doFilter
* @param filters the Filters to delegate to.
* @return the Filter for doFilter
*/
private static Filter createDoFilterDelegate(List<? extends Filter> filters) {
CompositeFilter delegate = new CompositeFilter();
delegate.setFilters(filters);
return delegate;
}
/**
* Find the FilterChainProxy in a List of Filter
* @param filters
* @return non-null FilterChainProxy
* @throws IllegalStateException if the FilterChainProxy cannot be found
*/
private static FilterChainProxy findFilterChainProxy(List<? extends Filter> filters) {
for (Filter filter : filters) {
if (filter instanceof FilterChainProxy fcp) {
return fcp;
}
if (filter instanceof DebugFilter debugFilter) {
return debugFilter.getFilterChainProxy();
}
}
throw new IllegalStateException("Couldn't find FilterChainProxy in " + filters);
}
}
}
@@ -21,7 +21,6 @@ import java.util.Collections;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.context.ApplicationContext;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
@@ -29,7 +28,6 @@ import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.PortMapper;
import org.springframework.security.web.PortResolver;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
@@ -274,10 +272,6 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
if (portMapper != null) {
this.authenticationEntryPoint.setPortMapper(portMapper);
}
PortResolver portResolver = getBeanOrNull(http, PortResolver.class);
if (portResolver != null) {
this.authenticationEntryPoint.setPortResolver(portResolver);
}
RequestCache requestCache = http.getSharedObject(RequestCache.class);
if (requestCache != null) {
this.defaultSuccessHandler.setRequestCache(requestCache);
@@ -418,14 +412,6 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(loginPage);
}
private <C> C getBeanOrNull(B http, Class<C> clazz) {
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
if (context == null) {
return null;
}
return context.getBeanProvider(clazz).getIfUnique();
}
@SuppressWarnings("unchecked")
private T getSelf() {
return (T) this;
@@ -22,9 +22,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
import org.springframework.security.core.annotation.SecurityAnnotationScanner;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
@@ -38,14 +36,7 @@ import org.springframework.util.Assert;
* @see ChannelSecurityConfigurer
* @see UrlAuthorizationConfigurer
* @see ExpressionUrlAuthorizationConfigurer
* @deprecated In modern Spring Security APIs, each API manages its own configuration
* context. As such there is no direct replacement for this interface. In the case of
* method security, please see {@link SecurityAnnotationScanner} and
* {@link AuthorizationManager}. In the case of channel security, please see
* {@code HttpsRedirectFilter}. In the case of web security, please see
* {@link AuthorizationManager}.
*/
@Deprecated
public abstract class AbstractConfigAttributeRequestMatcherRegistry<C> extends AbstractRequestMatcherRegistry<C> {
private List<UrlMapping> urlMappings = new ArrayList<>();
@@ -31,7 +31,6 @@ import org.springframework.security.web.DefaultSecurityFilterChain;
* {@link HttpSecurity}.
*
* @author Rob Winch
* @author Ding Hao
*/
public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>, B extends HttpSecurityBuilder<B>>
extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {
@@ -71,8 +70,13 @@ public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T,
return this.securityContextHolderStrategy;
}
ApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);
this.securityContextHolderStrategy = context.getBeanProvider(SecurityContextHolderStrategy.class)
.getIfUnique(SecurityContextHolder::getContextHolderStrategy);
String[] names = context.getBeanNamesForType(SecurityContextHolderStrategy.class);
if (names.length == 1) {
this.securityContextHolderStrategy = context.getBean(SecurityContextHolderStrategy.class);
}
else {
this.securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy();
}
return this.securityContextHolderStrategy;
}
@@ -151,7 +151,6 @@ public abstract class AbstractInterceptUrlConfigurer<C extends AbstractIntercept
return securityInterceptor;
}
@Deprecated
public abstract class AbstractInterceptUrlRegistry<R extends AbstractInterceptUrlRegistry<R, T>, T>
extends AbstractConfigAttributeRequestMatcherRegistry<T> {
@@ -33,7 +33,6 @@ import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationEventPublisher;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.AuthorizationManagers;
import org.springframework.security.authorization.SingleResultAuthorizationManager;
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
import org.springframework.security.config.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
@@ -58,6 +57,9 @@ import org.springframework.util.function.SingletonSupplier;
public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<AuthorizeHttpRequestsConfigurer<H>, H> {
static final AuthorizationManager<RequestAuthorizationContext> permitAllAuthorizationManager = (a,
o) -> new AuthorizationDecision(true);
private final AuthorizationManagerRequestMatcherRegistry registry;
private final AuthorizationEventPublisher publisher;
@@ -285,7 +287,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
* customizations
*/
public AuthorizationManagerRequestMatcherRegistry permitAll() {
return access(SingleResultAuthorizationManager.permitAll());
return access(permitAllAuthorizationManager);
}
/**
@@ -294,7 +296,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
* customizations
*/
public AuthorizationManagerRequestMatcherRegistry denyAll() {
return access(SingleResultAuthorizationManager.denyAll());
return access((a, o) -> new AuthorizationDecision(false));
}
/**
@@ -78,9 +78,7 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
* @author Rob Winch
* @author Onur Kagan Ozcan
* @since 3.2
* @deprecated please use {@link HttpsRedirectConfigurer} instead
*/
@Deprecated
public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<ChannelSecurityConfigurer<H>, H> {
@@ -149,10 +147,6 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>>
return this.REGISTRY;
}
/**
* @deprecated no replacement planned
*/
@Deprecated
public final class ChannelRequestMatcherRegistry
extends AbstractConfigAttributeRequestMatcherRegistry<RequiresChannelUrl> {
@@ -221,10 +215,6 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>>
}
/**
* @deprecated no replacement planned
*/
@Deprecated
public class RequiresChannelUrl {
protected List<? extends RequestMatcher> requestMatchers;
@@ -107,14 +107,15 @@ public class CorsConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHt
* @return
*/
private static CorsFilter getMvcCorsFilter(ApplicationContext context) {
if (context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
CorsConfigurationSource corsConfigurationSource = context
.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, CorsConfigurationSource.class);
return new CorsFilter(corsConfigurationSource);
if (!context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
throw new NoSuchBeanDefinitionException(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, "A Bean named "
+ HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME + " of type "
+ HandlerMappingIntrospector.class.getName()
+ " is required to use MvcRequestMatcher. Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext.");
}
throw new NoSuchBeanDefinitionException(CorsConfigurationSource.class,
"Failed to find a bean that implements `CorsConfigurationSource`. Please ensure that you are using "
+ "`@EnableWebMvc`, are publishing a `WebMvcConfigurer`, or are publishing a `CorsConfigurationSource` bean.");
HandlerMappingIntrospector mappingIntrospector = context.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME,
HandlerMappingIntrospector.class);
return new CorsFilter(mappingIntrospector);
}
}
@@ -33,6 +33,13 @@ 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 EnableWebSecurity}.
*
* <p>
* By default an
* {@link org.springframework.security.web.access.channel.InsecureChannelProcessor} and a
* {@link org.springframework.security.web.access.channel.SecureChannelProcessor} will be
* registered.
* </p>
*
* <h2>Security Filters</h2>
*
* The following Filters are conditionally populated
@@ -51,6 +58,8 @@ import org.springframework.security.web.csrf.CsrfToken;
* The following shared objects are used:
*
* <ul>
* <li>{@link org.springframework.security.web.PortMapper} is used to create the default
* {@link org.springframework.security.web.access.channel.ChannelProcessor} instances</li>
* <li>{@link FormLoginConfigurer} is used to determine if the
* {@link DefaultLoginPageConfigurer} should be added and how to configure it.</li>
* </ul>
@@ -202,7 +202,6 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
return "hasIpAddress('" + ipAddressExpression + "')";
}
@Deprecated
public final class ExpressionInterceptUrlRegistry extends
ExpressionUrlAuthorizationConfigurer<H>.AbstractInterceptUrlRegistry<ExpressionInterceptUrlRegistry, AuthorizedUrl> {
@@ -16,9 +16,7 @@
package org.springframework.security.config.annotation.web.configurers;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.AuthenticationEntryPoint;
@@ -28,6 +26,7 @@ import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
/**
@@ -235,7 +234,7 @@ public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
@Override
protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
return RequestMatcherFactory.matcher(HttpMethod.POST, loginProcessingUrl);
return new AntPathRequestMatcher(loginProcessingUrl, "POST");
}
/**
@@ -1,76 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.web.PortMapper;
import org.springframework.security.web.transport.HttpsRedirectFilter;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
/**
* Specifies for what requests the application should redirect to HTTPS. When this
* configurer is added, it redirects all HTTP requests by default to HTTPS.
*
* <h2>Security Filters</h2>
*
* The following Filters are populated
*
* <ul>
* <li>{@link HttpsRedirectFilter}</li>
* </ul>
*
* <h2>Shared Objects Created</h2>
*
* No shared objects are created.
*
* <h2>Shared Objects Used</h2>
*
* The following shared objects are used:
*
* <ul>
* <li>{@link PortMapper} is used to configure {@link HttpsRedirectFilter}</li>
* </ul>
*
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured
* @author Josh Cummings
* @since 6.5
*/
public final class HttpsRedirectConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<HeadersConfigurer<H>, H> {
private RequestMatcher requestMatcher;
public HttpsRedirectConfigurer<H> requestMatchers(RequestMatcher... matchers) {
this.requestMatcher = new OrRequestMatcher(matchers);
return this;
}
@Override
public void configure(H http) throws Exception {
HttpsRedirectFilter filter = new HttpsRedirectFilter();
if (this.requestMatcher != null) {
filter.setRequestMatcher(this.requestMatcher);
}
PortMapper mapper = http.getSharedObject(PortMapper.class);
if (mapper != null) {
filter.setPortMapper(mapper);
}
http.addFilter(filter);
}
}
@@ -22,10 +22,8 @@ import java.util.List;
import jakarta.servlet.http.HttpSession;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler;
@@ -39,6 +37,7 @@ import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuc
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
@@ -145,7 +144,7 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>>
* (i.e. log out) to protect against
* <a href="https://en.wikipedia.org/wiki/Cross-site_request_forgery">CSRF
* attacks</a>. If you really want to use an HTTP GET, you can use
* <code>logoutRequestMatcher(PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.GEt, logoutUrl));</code>
* <code>logoutRequestMatcher(new AntPathRequestMatcher(logoutUrl, "GET"));</code>
* </p>
* @param logoutUrl the URL that will invoke logout.
* @return the {@link LogoutConfigurer} for further customization
@@ -369,7 +368,7 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>>
}
private RequestMatcher createLogoutRequestMatcher(String httpMethod) {
return RequestMatcherFactory.matcher(HttpMethod.valueOf(httpMethod), this.logoutUrl);
return new AntPathRequestMatcher(this.logoutUrl, httpMethod);
}
}
@@ -17,9 +17,9 @@
package org.springframework.security.config.annotation.web.configurers;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
import org.springframework.security.web.RequestMatcherRedirectFilter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;
/**
@@ -55,7 +55,7 @@ public final class PasswordManagementConfigurer<B extends HttpSecurityBuilder<B>
@Override
public void configure(B http) throws Exception {
RequestMatcherRedirectFilter changePasswordFilter = new RequestMatcherRedirectFilter(
RequestMatcherFactory.matcher(WELL_KNOWN_CHANGE_PASSWORD_PATTERN), this.changePasswordPage);
new AntPathRequestMatcher(WELL_KNOWN_CHANGE_PASSWORD_PATTERN), this.changePasswordPage);
http.addFilterBefore(postProcess(changePasswordFilter), UsernamePasswordAuthenticationFilter.class);
}
@@ -19,7 +19,6 @@ package org.springframework.security.config.annotation.web.configurers;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.authorization.SingleResultAuthorizationManager;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configurers.AbstractConfigAttributeRequestMatcherRegistry.UrlMapping;
import org.springframework.security.web.util.matcher.RequestMatcher;
@@ -64,7 +63,7 @@ final class PermitAllSupport {
SecurityConfig.createList(ExpressionUrlAuthorizationConfigurer.permitAll)));
}
else {
httpConfigurer.addFirst(matcher, SingleResultAuthorizationManager.permitAll());
httpConfigurer.addFirst(matcher, AuthorizeHttpRequestsConfigurer.permitAllAuthorizationManager);
}
}
}
@@ -21,10 +21,8 @@ import java.util.Collections;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.NullRequestCache;
@@ -142,13 +140,13 @@ public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>>
@SuppressWarnings("unchecked")
private RequestMatcher createDefaultSavedRequestMatcher(H http) {
RequestMatcher notFavIcon = new NegatedRequestMatcher(getFaviconRequestMatcher());
RequestMatcher notFavIcon = new NegatedRequestMatcher(new AntPathRequestMatcher("/**/favicon.*"));
RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
boolean isCsrfEnabled = http.getConfigurer(CsrfConfigurer.class) != null;
List<RequestMatcher> matchers = new ArrayList<>();
if (isCsrfEnabled) {
RequestMatcher getRequests = RequestMatcherFactory.matcher(HttpMethod.GET, "/**");
RequestMatcher getRequests = new AntPathRequestMatcher("/**", "GET");
matchers.add(0, getRequests);
}
matchers.add(notFavIcon);
@@ -169,13 +167,4 @@ public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>>
return new NegatedRequestMatcher(mediaRequest);
}
private RequestMatcher getFaviconRequestMatcher() {
if (RequestMatcherFactory.usesPathPatterns()) {
return RequestMatcherFactory.matcher("/favicon.*");
}
else {
return new AntPathRequestMatcher("/**/favicon.*");
}
}
}
@@ -47,7 +47,6 @@ import org.springframework.security.web.authentication.session.NullAuthenticated
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;
import org.springframework.security.web.authentication.session.SessionLimit;
import org.springframework.security.web.context.DelegatingSecurityContextRepository;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.NullSecurityContextRepository;
@@ -124,7 +123,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
private SessionRegistry sessionRegistry;
private SessionLimit sessionLimit;
private Integer maximumSessions;
private String expiredUrl;
@@ -330,7 +329,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
* @return the {@link SessionManagementConfigurer} for further customizations
*/
public ConcurrencyControlConfigurer maximumSessions(int maximumSessions) {
this.sessionLimit = SessionLimit.of(maximumSessions);
this.maximumSessions = maximumSessions;
this.propertiesThatRequireImplicitAuthentication.add("maximumSessions = " + maximumSessions);
return new ConcurrencyControlConfigurer();
}
@@ -571,7 +570,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
SessionRegistry sessionRegistry = getSessionRegistry(http);
ConcurrentSessionControlAuthenticationStrategy concurrentSessionControlStrategy = new ConcurrentSessionControlAuthenticationStrategy(
sessionRegistry);
concurrentSessionControlStrategy.setMaximumSessions(this.sessionLimit);
concurrentSessionControlStrategy.setMaximumSessions(this.maximumSessions);
concurrentSessionControlStrategy.setExceptionIfMaximumExceeded(this.maxSessionsPreventsLogin);
concurrentSessionControlStrategy = postProcess(concurrentSessionControlStrategy);
RegisterSessionAuthenticationStrategy registerSessionStrategy = new RegisterSessionAuthenticationStrategy(
@@ -615,7 +614,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
* @return
*/
private boolean isConcurrentSessionControlEnabled() {
return this.sessionLimit != null;
return this.maximumSessions != null;
}
/**
@@ -707,19 +706,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
* @return the {@link ConcurrencyControlConfigurer} for further customizations
*/
public ConcurrencyControlConfigurer maximumSessions(int maximumSessions) {
SessionManagementConfigurer.this.sessionLimit = SessionLimit.of(maximumSessions);
return this;
}
/**
* Determines the behaviour when a session limit is detected.
* @param sessionLimit the {@link SessionLimit} to check the maximum number of
* sessions for a user
* @return the {@link ConcurrencyControlConfigurer} for further customizations
* @since 6.5
*/
public ConcurrencyControlConfigurer maximumSessions(SessionLimit sessionLimit) {
SessionManagementConfigurer.this.sessionLimit = sessionLimit;
SessionManagementConfigurer.this.maximumSessions = maximumSessions;
return this;
}
@@ -203,7 +203,6 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
return authorities;
}
@Deprecated
public final class StandardInterceptUrlRegistry extends
UrlAuthorizationConfigurer<H>.AbstractInterceptUrlRegistry<StandardInterceptUrlRegistry, AuthorizedUrl> {
@@ -23,7 +23,6 @@ import java.util.Set;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.core.userdetails.UserDetailsService;
@@ -44,9 +43,7 @@ import org.springframework.security.web.webauthn.management.WebAuthnRelyingParty
import org.springframework.security.web.webauthn.management.Webauthn4JRelyingPartyOperations;
import org.springframework.security.web.webauthn.registration.DefaultWebAuthnRegistrationPageGeneratingFilter;
import org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsFilter;
import org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsRepository;
import org.springframework.security.web.webauthn.registration.WebAuthnRegistrationFilter;
import org.springframework.util.Assert;
/**
* Configures WebAuthn for Spring Security applications
@@ -66,17 +63,12 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
private boolean disableDefaultRegistrationPage = false;
private PublicKeyCredentialCreationOptionsRepository creationOptionsRepository;
private HttpMessageConverter<Object> converter;
/**
* The Relying Party id.
* @param rpId the relying party id
* @return the {@link WebAuthnConfigurer} for further customization
*/
public WebAuthnConfigurer<H> rpId(String rpId) {
Assert.hasText(rpId, "rpId be null or empty");
this.rpId = rpId;
return this;
}
@@ -87,7 +79,6 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
* @return the {@link WebAuthnConfigurer} for further customization
*/
public WebAuthnConfigurer<H> rpName(String rpName) {
Assert.hasText(rpName, "rpName can't be null or empty");
this.rpName = rpName;
return this;
}
@@ -109,7 +100,6 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
* @see #allowedOrigins(String...)
*/
public WebAuthnConfigurer<H> allowedOrigins(Set<String> allowedOrigins) {
Assert.notNull(allowedOrigins, "allowedOrigins can't be null");
this.allowedOrigins = allowedOrigins;
return this;
}
@@ -126,59 +116,23 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
/**
* Sets {@link HttpMessageConverter} used for WebAuthn to read/write to the HTTP
* request/response.
* @param converter the {@link HttpMessageConverter}
* @return the {@link WebAuthnConfigurer} for further customization
*/
public WebAuthnConfigurer<H> messageConverter(HttpMessageConverter<Object> converter) {
Assert.notNull(converter, "converter can't be null");
this.converter = converter;
return this;
}
/**
* Sets PublicKeyCredentialCreationOptionsRepository
* @param creationOptionsRepository the creationOptionsRepository
* @return the {@link WebAuthnConfigurer} for further customization
*/
public WebAuthnConfigurer<H> creationOptionsRepository(
PublicKeyCredentialCreationOptionsRepository creationOptionsRepository) {
Assert.notNull(creationOptionsRepository, "creationOptionsRepository can't be null");
this.creationOptionsRepository = creationOptionsRepository;
return this;
}
@Override
public void configure(H http) throws Exception {
UserDetailsService userDetailsService = getSharedOrBean(http, UserDetailsService.class)
.orElseThrow(() -> new IllegalStateException("Missing UserDetailsService Bean"));
UserDetailsService userDetailsService = getSharedOrBean(http, UserDetailsService.class).orElseGet(() -> {
throw new IllegalStateException("Missing UserDetailsService Bean");
});
PublicKeyCredentialUserEntityRepository userEntities = getSharedOrBean(http,
PublicKeyCredentialUserEntityRepository.class)
.orElse(userEntityRepository());
UserCredentialRepository userCredentials = getSharedOrBean(http, UserCredentialRepository.class)
.orElse(userCredentialRepository());
WebAuthnRelyingPartyOperations rpOperations = webAuthnRelyingPartyOperations(userEntities, userCredentials);
PublicKeyCredentialCreationOptionsRepository creationOptionsRepository = creationOptionsRepository();
WebAuthnAuthenticationFilter webAuthnAuthnFilter = new WebAuthnAuthenticationFilter();
webAuthnAuthnFilter.setAuthenticationManager(
new ProviderManager(new WebAuthnAuthenticationProvider(rpOperations, userDetailsService)));
WebAuthnRegistrationFilter webAuthnRegistrationFilter = new WebAuthnRegistrationFilter(userCredentials,
rpOperations);
PublicKeyCredentialCreationOptionsFilter creationOptionsFilter = new PublicKeyCredentialCreationOptionsFilter(
rpOperations);
if (creationOptionsRepository != null) {
webAuthnRegistrationFilter.setCreationOptionsRepository(creationOptionsRepository);
creationOptionsFilter.setCreationOptionsRepository(creationOptionsRepository);
}
if (this.converter != null) {
webAuthnRegistrationFilter.setConverter(this.converter);
creationOptionsFilter.setConverter(this.converter);
}
http.addFilterBefore(webAuthnAuthnFilter, BasicAuthenticationFilter.class);
http.addFilterAfter(webAuthnRegistrationFilter, AuthorizationFilter.class);
http.addFilterBefore(creationOptionsFilter, AuthorizationFilter.class);
http.addFilterAfter(new WebAuthnRegistrationFilter(userCredentials, rpOperations), AuthorizationFilter.class);
http.addFilterBefore(new PublicKeyCredentialCreationOptionsFilter(rpOperations), AuthorizationFilter.class);
http.addFilterBefore(new PublicKeyCredentialRequestOptionsFilter(rpOperations), AuthorizationFilter.class);
DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http
@@ -205,14 +159,6 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
}
}
private PublicKeyCredentialCreationOptionsRepository creationOptionsRepository() {
if (this.creationOptionsRepository != null) {
return this.creationOptionsRepository;
}
ApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);
return context.getBeanProvider(PublicKeyCredentialCreationOptionsRepository.class).getIfUnique();
}
private <C> Optional<C> getSharedOrBean(H http, Class<C> type) {
C shared = http.getSharedObject(type);
return Optional.ofNullable(shared).or(() -> getBeanOrNull(type));
@@ -243,9 +189,12 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
PublicKeyCredentialUserEntityRepository userEntities, UserCredentialRepository userCredentials) {
Optional<WebAuthnRelyingPartyOperations> webauthnOperationsBean = getBeanOrNull(
WebAuthnRelyingPartyOperations.class);
return webauthnOperationsBean.orElseGet(() -> new Webauthn4JRelyingPartyOperations(userEntities,
userCredentials, PublicKeyCredentialRpEntity.builder().id(this.rpId).name(this.rpName).build(),
this.allowedOrigins));
if (webauthnOperationsBean.isPresent()) {
return webauthnOperationsBean.get();
}
String rpName = (this.rpName != null) ? this.rpName : this.rpId;
return new Webauthn4JRelyingPartyOperations(userEntities, userCredentials,
PublicKeyCredentialRpEntity.builder().id(this.rpId).name(rpName).build(), this.allowedOrigins);
}
}
@@ -38,7 +38,6 @@ import org.springframework.core.ResolvableType;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
@@ -57,7 +56,6 @@ import org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationC
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
import org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider;
import org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizedClientRefreshedEventListener;
import org.springframework.security.oauth2.client.oidc.session.InMemoryOidcSessionRegistry;
import org.springframework.security.oauth2.client.oidc.session.OidcSessionInformation;
import org.springframework.security.oauth2.client.oidc.session.OidcSessionRegistry;
@@ -70,7 +68,6 @@ import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository;
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
@@ -84,7 +81,6 @@ 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.PortResolver;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
@@ -95,6 +91,7 @@ import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
@@ -364,8 +361,6 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
public void init(B http) throws Exception {
OAuth2LoginAuthenticationFilter authenticationFilter = new OAuth2LoginAuthenticationFilter(
this.getClientRegistrationRepository(), this.getAuthorizedClientRepository(), this.loginProcessingUrl);
RequestMatcher processUri = RequestMatcherFactory.matcher(this.loginProcessingUrl);
authenticationFilter.setRequiresAuthenticationRequestMatcher(processUri);
authenticationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
if (this.securityContextRepository != null) {
authenticationFilter.setSecurityContextRepository(this.securityContextRepository);
@@ -406,24 +401,14 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService = getOidcUserService();
OidcAuthorizationCodeAuthenticationProvider oidcAuthorizationCodeAuthenticationProvider = new OidcAuthorizationCodeAuthenticationProvider(
accessTokenResponseClient, oidcUserService);
OidcAuthorizedClientRefreshedEventListener oidcAuthorizedClientRefreshedEventListener = new OidcAuthorizedClientRefreshedEventListener();
oidcAuthorizedClientRefreshedEventListener.setUserService(oidcUserService);
oidcAuthorizedClientRefreshedEventListener
.setApplicationEventPublisher(http.getSharedObject(ApplicationContext.class));
JwtDecoderFactory<ClientRegistration> jwtDecoderFactory = this.getJwtDecoderFactoryBean();
if (jwtDecoderFactory != null) {
oidcAuthorizationCodeAuthenticationProvider.setJwtDecoderFactory(jwtDecoderFactory);
oidcAuthorizedClientRefreshedEventListener.setJwtDecoderFactory(jwtDecoderFactory);
}
if (userAuthoritiesMapper != null) {
oidcAuthorizationCodeAuthenticationProvider.setAuthoritiesMapper(userAuthoritiesMapper);
oidcAuthorizedClientRefreshedEventListener.setAuthoritiesMapper(userAuthoritiesMapper);
}
http.authenticationProvider(this.postProcess(oidcAuthorizationCodeAuthenticationProvider));
registerDelegateApplicationListener(this.postProcess(oidcAuthorizedClientRefreshedEventListener));
configureOidcUserRefreshedEventListener(http);
}
else {
http.authenticationProvider(new OidcAuthenticationRequestChecker());
@@ -433,8 +418,19 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
@Override
public void configure(B http) throws Exception {
OAuth2AuthorizationRequestRedirectFilter authorizationRequestFilter = new OAuth2AuthorizationRequestRedirectFilter(
getAuthorizationRequestResolver());
OAuth2AuthorizationRequestRedirectFilter authorizationRequestFilter;
if (this.authorizationEndpointConfig.authorizationRequestResolver != null) {
authorizationRequestFilter = new OAuth2AuthorizationRequestRedirectFilter(
this.authorizationEndpointConfig.authorizationRequestResolver);
}
else {
String authorizationRequestBaseUri = this.authorizationEndpointConfig.authorizationRequestBaseUri;
if (authorizationRequestBaseUri == null) {
authorizationRequestBaseUri = OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI;
}
authorizationRequestFilter = new OAuth2AuthorizationRequestRedirectFilter(
this.getClientRegistrationRepository(), authorizationRequestBaseUri);
}
if (this.authorizationEndpointConfig.authorizationRequestRepository != null) {
authorizationRequestFilter
.setAuthorizationRequestRepository(this.authorizationEndpointConfig.authorizationRequestRepository);
@@ -450,8 +446,7 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
http.addFilter(this.postProcess(authorizationRequestFilter));
OAuth2LoginAuthenticationFilter authenticationFilter = this.getAuthenticationFilter();
if (this.redirectionEndpointConfig.authorizationResponseBaseUri != null) {
authenticationFilter.setRequiresAuthenticationRequestMatcher(
RequestMatcherFactory.matcher(this.redirectionEndpointConfig.authorizationResponseBaseUri));
authenticationFilter.setFilterProcessesUrl(this.redirectionEndpointConfig.authorizationResponseBaseUri);
}
if (this.authorizationEndpointConfig.authorizationRequestRepository != null) {
authenticationFilter
@@ -463,24 +458,7 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
@Override
protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
return RequestMatcherFactory.matcher(loginProcessingUrl);
}
private OAuth2AuthorizationRequestResolver getAuthorizationRequestResolver() {
if (this.authorizationEndpointConfig.authorizationRequestResolver != null) {
return this.authorizationEndpointConfig.authorizationRequestResolver;
}
ClientRegistrationRepository clientRegistrationRepository = this.getClientRegistrationRepository();
ResolvableType resolvableType = ResolvableType.forClass(OAuth2AuthorizationRequestResolver.class);
OAuth2AuthorizationRequestResolver bean = getBeanOrNull(resolvableType);
if (bean != null) {
return bean;
}
String authorizationRequestBaseUri = this.authorizationEndpointConfig.authorizationRequestBaseUri;
if (authorizationRequestBaseUri == null) {
authorizationRequestBaseUri = OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI;
}
return new DefaultOAuth2AuthorizationRequestResolver(clientRegistrationRepository, authorizationRequestBaseUri);
return new AntPathRequestMatcher(loginProcessingUrl);
}
private ClientRegistrationRepository getClientRegistrationRepository() {
@@ -601,8 +579,8 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
}
private AuthenticationEntryPoint getLoginEntryPoint(B http, String providerLoginPage) {
RequestMatcher loginPageMatcher = RequestMatcherFactory.matcher(this.getLoginPage());
RequestMatcher faviconMatcher = RequestMatcherFactory.matcher("/favicon.ico");
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);
@@ -610,13 +588,8 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
RequestMatcher formLoginNotEnabled = getFormLoginNotEnabledRequestMatcher(http);
LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints = new LinkedHashMap<>();
LoginUrlAuthenticationEntryPoint loginUrlEntryPoint = new LoginUrlAuthenticationEntryPoint(providerLoginPage);
PortResolver portResolver = getBeanOrNull(ResolvableType.forClass(PortResolver.class));
if (portResolver != null) {
loginUrlEntryPoint.setPortResolver(portResolver);
}
entryPoints.put(new AndRequestMatcher(notXRequestedWith, new NegatedRequestMatcher(defaultLoginPageMatcher),
formLoginNotEnabled), loginUrlEntryPoint);
formLoginNotEnabled), new LoginUrlAuthenticationEntryPoint(providerLoginPage));
DelegatingAuthenticationEntryPoint loginEntryPoint = new DelegatingAuthenticationEntryPoint(entryPoints);
loginEntryPoint.setDefaultEntryPoint(this.getAuthenticationEntryPoint());
return loginEntryPoint;
@@ -652,16 +625,6 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
registerDelegateApplicationListener(listener);
}
private void configureOidcUserRefreshedEventListener(B http) {
OidcUserRefreshedEventListener oidcUserRefreshedEventListener = new OidcUserRefreshedEventListener();
oidcUserRefreshedEventListener.setSecurityContextHolderStrategy(this.getSecurityContextHolderStrategy());
SecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);
if (securityContextRepository != null) {
oidcUserRefreshedEventListener.setSecurityContextRepository(securityContextRepository);
}
registerDelegateApplicationListener(oidcUserRefreshedEventListener);
}
private void registerDelegateApplicationListener(ApplicationListener<?> delegate) {
DelegatingApplicationListener delegating = getBeanOrNull(
ResolvableType.forType(DelegatingApplicationListener.class));
@@ -127,7 +127,7 @@ public final class OidcBackChannelLogoutHandler implements LogoutHandler {
String computeLogoutEndpoint(HttpServletRequest request, OidcBackChannelLogoutAuthentication token) {
// @formatter:off
UriComponents uriComponents = UriComponentsBuilder
.fromUriString(UrlUtils.buildFullRequestUrl(request))
.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
.replacePath(request.getContextPath())
.replaceQuery(null)
.fragment(null)
@@ -20,14 +20,13 @@ import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
@@ -46,8 +45,7 @@ final class OidcLogoutAuthenticationConverter implements AuthenticationConverter
private final ClientRegistrationRepository clientRegistrationRepository;
private RequestMatcher requestMatcher = PathPatternRequestMatcher.withDefaults()
.matcher(HttpMethod.POST, DEFAULT_LOGOUT_URI);
private RequestMatcher requestMatcher = new AntPathRequestMatcher(DEFAULT_LOGOUT_URI, "POST");
OidcLogoutAuthenticationConverter(ClientRegistrationRepository clientRegistrationRepository) {
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
@@ -1,86 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers.oauth2.client;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.context.ApplicationListener;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.oauth2.client.oidc.authentication.event.OidcUserRefreshedEvent;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.util.Assert;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* An {@link ApplicationListener} that listens for events of type
* {@link OidcUserRefreshedEvent} and refreshes the {@link SecurityContext}.
*
* @author Steve Riesenberg
* @since 6.5
* @see org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizedClientRefreshedEventListener
*/
final class OidcUserRefreshedEventListener implements ApplicationListener<OidcUserRefreshedEvent> {
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
.getContextHolderStrategy();
private SecurityContextRepository securityContextRepository = new HttpSessionSecurityContextRepository();
@Override
public void onApplicationEvent(OidcUserRefreshedEvent event) {
SecurityContext securityContext = this.securityContextHolderStrategy.createEmptyContext();
securityContext.setAuthentication(event.getAuthentication());
this.securityContextHolderStrategy.setContext(securityContext);
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (!(requestAttributes instanceof ServletRequestAttributes servletRequestAttributes)) {
return;
}
HttpServletRequest request = servletRequestAttributes.getRequest();
HttpServletResponse response = servletRequestAttributes.getResponse();
this.securityContextRepository.saveContext(securityContext, request, response);
}
/**
* Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
* the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
* @param securityContextHolderStrategy the {@link SecurityContextHolderStrategy} to
* use
*/
void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
this.securityContextHolderStrategy = securityContextHolderStrategy;
}
/**
* Sets the {@link SecurityContextRepository} to save the {@link SecurityContext} upon
* receiving an {@link OidcUserRefreshedEvent}.
* @param securityContextRepository the {@link SecurityContextRepository} to use
*/
void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {
Assert.notNull(securityContextRepository, "securityContextRepository cannot be null");
this.securityContextRepository = securityContextRepository;
}
}
@@ -1,239 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers.oauth2.server.resource;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationManagerResolver;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
import org.springframework.security.oauth2.server.resource.authentication.DPoPAuthenticationProvider;
import org.springframework.security.oauth2.server.resource.authentication.DPoPAuthenticationToken;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationFilter;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* An {@link AbstractHttpConfigurer} for OAuth 2.0 Demonstrating Proof of Possession
* (DPoP) support.
*
* @author Joe Grandja
* @since 6.5
* @see DPoPAuthenticationProvider
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc9449">RFC 9449
* OAuth 2.0 Demonstrating Proof of Possession (DPoP)</a>
*/
final class DPoPAuthenticationConfigurer<B extends HttpSecurityBuilder<B>>
extends AbstractHttpConfigurer<DPoPAuthenticationConfigurer<B>, B> {
private RequestMatcher requestMatcher;
private AuthenticationConverter authenticationConverter;
private AuthenticationSuccessHandler authenticationSuccessHandler;
private AuthenticationFailureHandler authenticationFailureHandler;
@Override
public void configure(B http) {
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
http.authenticationProvider(new DPoPAuthenticationProvider(getTokenAuthenticationManager(http)));
AuthenticationFilter authenticationFilter = new AuthenticationFilter(authenticationManager,
getAuthenticationConverter());
authenticationFilter.setRequestMatcher(getRequestMatcher());
authenticationFilter.setSuccessHandler(getAuthenticationSuccessHandler());
authenticationFilter.setFailureHandler(getAuthenticationFailureHandler());
authenticationFilter.setSecurityContextRepository(new RequestAttributeSecurityContextRepository());
authenticationFilter = postProcess(authenticationFilter);
http.addFilter(authenticationFilter);
}
private AuthenticationManager getTokenAuthenticationManager(B http) {
OAuth2ResourceServerConfigurer<B> resourceServerConfigurer = http
.getConfigurer(OAuth2ResourceServerConfigurer.class);
final AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver = resourceServerConfigurer
.getAuthenticationManagerResolver();
if (authenticationManagerResolver == null) {
return resourceServerConfigurer.getAuthenticationManager(http);
}
return (authentication) -> {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
AuthenticationManager authenticationManager = authenticationManagerResolver
.resolve(servletRequestAttributes.getRequest());
return authenticationManager.authenticate(authentication);
};
}
private RequestMatcher getRequestMatcher() {
if (this.requestMatcher == null) {
this.requestMatcher = new DPoPRequestMatcher();
}
return this.requestMatcher;
}
private AuthenticationConverter getAuthenticationConverter() {
if (this.authenticationConverter == null) {
this.authenticationConverter = new DPoPAuthenticationConverter();
}
return this.authenticationConverter;
}
private AuthenticationSuccessHandler getAuthenticationSuccessHandler() {
if (this.authenticationSuccessHandler == null) {
this.authenticationSuccessHandler = (request, response, authentication) -> {
// No-op - will continue on filter chain
};
}
return this.authenticationSuccessHandler;
}
private AuthenticationFailureHandler getAuthenticationFailureHandler() {
if (this.authenticationFailureHandler == null) {
this.authenticationFailureHandler = new AuthenticationEntryPointFailureHandler(
new DPoPAuthenticationEntryPoint());
}
return this.authenticationFailureHandler;
}
private static final class DPoPRequestMatcher implements RequestMatcher {
@Override
public boolean matches(HttpServletRequest request) {
String authorization = request.getHeader(HttpHeaders.AUTHORIZATION);
if (!StringUtils.hasText(authorization)) {
return false;
}
return StringUtils.startsWithIgnoreCase(authorization, OAuth2AccessToken.TokenType.DPOP.getValue());
}
}
private static final class DPoPAuthenticationConverter implements AuthenticationConverter {
private static final Pattern AUTHORIZATION_PATTERN = Pattern.compile("^DPoP (?<token>[a-zA-Z0-9-._~+/]+=*)$",
Pattern.CASE_INSENSITIVE);
@Override
public Authentication convert(HttpServletRequest request) {
List<String> authorizationList = Collections.list(request.getHeaders(HttpHeaders.AUTHORIZATION));
if (CollectionUtils.isEmpty(authorizationList)) {
return null;
}
if (authorizationList.size() != 1) {
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST,
"Found multiple Authorization headers.", null);
throw new OAuth2AuthenticationException(error);
}
String authorization = authorizationList.get(0);
if (!StringUtils.startsWithIgnoreCase(authorization, OAuth2AccessToken.TokenType.DPOP.getValue())) {
return null;
}
Matcher matcher = AUTHORIZATION_PATTERN.matcher(authorization);
if (!matcher.matches()) {
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, "DPoP access token is malformed.",
null);
throw new OAuth2AuthenticationException(error);
}
String accessToken = matcher.group("token");
List<String> dPoPProofList = Collections
.list(request.getHeaders(OAuth2AccessToken.TokenType.DPOP.getValue()));
if (CollectionUtils.isEmpty(dPoPProofList) || dPoPProofList.size() != 1) {
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST,
"DPoP proof is missing or invalid.", null);
throw new OAuth2AuthenticationException(error);
}
String dPoPProof = dPoPProofList.get(0);
return new DPoPAuthenticationToken(accessToken, dPoPProof, request.getMethod(),
request.getRequestURL().toString());
}
}
private static final class DPoPAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authenticationException) {
Map<String, String> parameters = new LinkedHashMap<>();
if (authenticationException instanceof OAuth2AuthenticationException oauth2AuthenticationException) {
OAuth2Error error = oauth2AuthenticationException.getError();
parameters.put(OAuth2ParameterNames.ERROR, error.getErrorCode());
if (StringUtils.hasText(error.getDescription())) {
parameters.put(OAuth2ParameterNames.ERROR_DESCRIPTION, error.getDescription());
}
if (StringUtils.hasText(error.getUri())) {
parameters.put(OAuth2ParameterNames.ERROR_URI, error.getUri());
}
}
parameters.put("algs",
JwsAlgorithms.RS256 + " " + JwsAlgorithms.RS384 + " " + JwsAlgorithms.RS512 + " "
+ JwsAlgorithms.PS256 + " " + JwsAlgorithms.PS384 + " " + JwsAlgorithms.PS512 + " "
+ JwsAlgorithms.ES256 + " " + JwsAlgorithms.ES384 + " " + JwsAlgorithms.ES512);
String wwwAuthenticate = toWWWAuthenticateHeader(parameters);
response.addHeader(HttpHeaders.WWW_AUTHENTICATE, wwwAuthenticate);
response.setStatus(HttpStatus.UNAUTHORIZED.value());
}
private static String toWWWAuthenticateHeader(Map<String, String> parameters) {
StringBuilder wwwAuthenticate = new StringBuilder();
wwwAuthenticate.append(OAuth2AccessToken.TokenType.DPOP.getValue());
if (!parameters.isEmpty()) {
wwwAuthenticate.append(" ");
int i = 0;
for (Map.Entry<String, String> entry : parameters.entrySet()) {
wwwAuthenticate.append(entry.getKey()).append("=\"").append(entry.getValue()).append("\"");
if (i++ != parameters.size() - 1) {
wwwAuthenticate.append(", ");
}
}
}
return wwwAuthenticate.toString();
}
}
}
@@ -64,7 +64,6 @@ 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;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
@@ -148,14 +147,6 @@ import org.springframework.web.accept.HeaderContentNegotiationStrategy;
public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<OAuth2ResourceServerConfigurer<H>, H> {
private static final boolean dPoPAuthenticationAvailable;
static {
ClassLoader classLoader = OAuth2ResourceServerConfigurer.class.getClassLoader();
dPoPAuthenticationAvailable = ClassUtils
.isPresent("org.springframework.security.oauth2.jwt.DPoPProofJwtDecoderFactory", classLoader);
}
private static final RequestHeaderRequestMatcher X_REQUESTED_WITH = new RequestHeaderRequestMatcher(
"X-Requested-With", "XMLHttpRequest");
@@ -292,10 +283,6 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
filter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
filter = postProcess(filter);
http.addFilter(filter);
if (dPoPAuthenticationAvailable) {
DPoPAuthenticationConfigurer<H> dPoPAuthenticationConfigurer = new DPoPAuthenticationConfigurer<>();
dPoPAuthenticationConfigurer.configure(http);
}
}
private void validateConfiguration() {
@@ -373,10 +360,6 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
return http.getSharedObject(AuthenticationManager.class);
}
AuthenticationManagerResolver<HttpServletRequest> getAuthenticationManagerResolver() {
return this.authenticationManagerResolver;
}
BearerTokenResolver getBearerTokenResolver() {
if (this.bearerTokenResolver == null) {
if (this.context.getBeanNamesForType(BearerTokenResolver.class).length > 0) {
@@ -23,156 +23,125 @@ import jakarta.servlet.http.HttpServletRequest;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;
import org.springframework.security.authentication.ott.InMemoryOneTimeTokenService;
import org.springframework.security.authentication.ott.OneTimeToken;
import org.springframework.security.authentication.ott.OneTimeTokenAuthenticationProvider;
import org.springframework.security.authentication.ott.OneTimeTokenService;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationFilter;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.ott.DefaultGenerateOneTimeTokenRequestResolver;
import org.springframework.security.web.authentication.ott.GenerateOneTimeTokenFilter;
import org.springframework.security.web.authentication.ott.GenerateOneTimeTokenRequestResolver;
import org.springframework.security.web.authentication.ott.OneTimeTokenAuthenticationConverter;
import org.springframework.security.web.authentication.ott.OneTimeTokenAuthenticationFilter;
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
import org.springframework.security.web.authentication.ui.DefaultOneTimeTokenSubmitPageGeneratingFilter;
import org.springframework.security.web.authentication.ui.DefaultResourcesFilter;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* An {@link AbstractHttpConfigurer} for One-Time Token Login.
*
* <p>
* One-Time Token Login provides an application with the capability to have users log in
* by obtaining a single-use token out of band, for example through email.
*
* <p>
* Defaults are provided for all configuration options, with the only required
* configuration being
* {@link #tokenGenerationSuccessHandler(OneTimeTokenGenerationSuccessHandler)}.
* Alternatively, a {@link OneTimeTokenGenerationSuccessHandler} {@code @Bean} may be
* registered instead.
*
* <h2>Security Filters</h2>
*
* The following {@code Filter}s are populated:
*
* <ul>
* <li>{@link DefaultOneTimeTokenSubmitPageGeneratingFilter}</li>
* <li>{@link GenerateOneTimeTokenFilter}</li>
* <li>{@link OneTimeTokenAuthenticationFilter}</li>
* </ul>
*
* <h2>Shared Objects Used</h2>
*
* The following shared objects are used:
*
* <ul>
* <li>{@link DefaultLoginPageGeneratingFilter} - if {@link #loginPage(String)} is not
* configured and {@code DefaultLoginPageGeneratingFilter} is available, then a default
* login page will be made available</li>
* </ul>
*
* @author Marcus Da Coregio
* @author Daniel Garnier-Moiroux
* @since 6.4
* @see HttpSecurity#oneTimeTokenLogin(Customizer)
* @see DefaultOneTimeTokenSubmitPageGeneratingFilter
* @see GenerateOneTimeTokenFilter
* @see OneTimeTokenAuthenticationFilter
* @see AbstractAuthenticationFilterConfigurer
*/
public final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractAuthenticationFilterConfigurer<H, OneTimeTokenLoginConfigurer<H>, OneTimeTokenAuthenticationFilter> {
import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;
public final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<OneTimeTokenLoginConfigurer<H>, H> {
private final ApplicationContext context;
private OneTimeTokenService oneTimeTokenService;
private String defaultSubmitPageUrl = DefaultOneTimeTokenSubmitPageGeneratingFilter.DEFAULT_SUBMIT_PAGE_URL;
private AuthenticationConverter authenticationConverter = new OneTimeTokenAuthenticationConverter();
private AuthenticationFailureHandler authenticationFailureHandler;
private AuthenticationSuccessHandler authenticationSuccessHandler = new SavedRequestAwareAuthenticationSuccessHandler();
private String defaultSubmitPageUrl = "/login/ott";
private boolean submitPageEnabled = true;
private String loginProcessingUrl = OneTimeTokenAuthenticationFilter.DEFAULT_LOGIN_PROCESSING_URL;
private String loginProcessingUrl = "/login/ott";
private String tokenGeneratingUrl = GenerateOneTimeTokenFilter.DEFAULT_GENERATE_URL;
private String tokenGeneratingUrl = "/ott/generate";
private OneTimeTokenGenerationSuccessHandler oneTimeTokenGenerationSuccessHandler;
private AuthenticationProvider authenticationProvider;
private GenerateOneTimeTokenRequestResolver requestResolver;
public OneTimeTokenLoginConfigurer(ApplicationContext context) {
super(new OneTimeTokenAuthenticationFilter(), OneTimeTokenAuthenticationFilter.DEFAULT_LOGIN_PROCESSING_URL);
this.context = context;
}
@Override
public void init(H http) throws Exception {
super.init(http);
AuthenticationProvider authenticationProvider = getAuthenticationProvider();
public void init(H http) {
AuthenticationProvider authenticationProvider = getAuthenticationProvider(http);
http.authenticationProvider(postProcess(authenticationProvider));
intiDefaultLoginFilter(http);
configureDefaultLoginPage(http);
}
private void intiDefaultLoginFilter(H http) {
private void configureDefaultLoginPage(H http) {
DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http
.getSharedObject(DefaultLoginPageGeneratingFilter.class);
if (loginPageGeneratingFilter == null || isCustomLoginPage()) {
if (loginPageGeneratingFilter == null) {
return;
}
loginPageGeneratingFilter.setOneTimeTokenEnabled(true);
loginPageGeneratingFilter.setOneTimeTokenGenerationUrl(this.tokenGeneratingUrl);
if (!StringUtils.hasText(loginPageGeneratingFilter.getLoginPageUrl())) {
loginPageGeneratingFilter.setLoginPageUrl(DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL);
loginPageGeneratingFilter.setFailureUrl(DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL + "?"
+ DefaultLoginPageGeneratingFilter.ERROR_PARAMETER_NAME);
loginPageGeneratingFilter
.setLogoutSuccessUrl(DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL + "?logout");
if (this.authenticationFailureHandler == null
&& StringUtils.hasText(loginPageGeneratingFilter.getLoginPageUrl())) {
this.authenticationFailureHandler = new SimpleUrlAuthenticationFailureHandler(
loginPageGeneratingFilter.getLoginPageUrl() + "?error");
}
}
@Override
public void configure(H http) throws Exception {
super.configure(http);
public void configure(H http) {
configureSubmitPage(http);
configureOttGenerateFilter(http);
configureOttAuthenticationFilter(http);
}
private void configureOttAuthenticationFilter(H http) {
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
AuthenticationFilter oneTimeTokenAuthenticationFilter = new AuthenticationFilter(authenticationManager,
this.authenticationConverter);
oneTimeTokenAuthenticationFilter.setSecurityContextRepository(getSecurityContextRepository(http));
oneTimeTokenAuthenticationFilter.setRequestMatcher(antMatcher(HttpMethod.POST, this.loginProcessingUrl));
oneTimeTokenAuthenticationFilter.setFailureHandler(getAuthenticationFailureHandler());
oneTimeTokenAuthenticationFilter.setSuccessHandler(this.authenticationSuccessHandler);
http.addFilter(postProcess(oneTimeTokenAuthenticationFilter));
}
private SecurityContextRepository getSecurityContextRepository(H http) {
SecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);
if (securityContextRepository != null) {
return securityContextRepository;
}
return new HttpSessionSecurityContextRepository();
}
private void configureOttGenerateFilter(H http) {
GenerateOneTimeTokenFilter generateFilter = new GenerateOneTimeTokenFilter(getOneTimeTokenService(),
getOneTimeTokenGenerationSuccessHandler());
generateFilter.setRequestMatcher(RequestMatcherFactory.matcher(HttpMethod.POST, this.tokenGeneratingUrl));
generateFilter.setRequestResolver(getGenerateRequestResolver());
GenerateOneTimeTokenFilter generateFilter = new GenerateOneTimeTokenFilter(getOneTimeTokenService(http),
getOneTimeTokenGenerationSuccessHandler(http));
generateFilter.setRequestMatcher(antMatcher(HttpMethod.POST, this.tokenGeneratingUrl));
http.addFilter(postProcess(generateFilter));
http.addFilter(DefaultResourcesFilter.css());
}
private OneTimeTokenGenerationSuccessHandler getOneTimeTokenGenerationSuccessHandler() {
private OneTimeTokenGenerationSuccessHandler getOneTimeTokenGenerationSuccessHandler(H http) {
if (this.oneTimeTokenGenerationSuccessHandler == null) {
this.oneTimeTokenGenerationSuccessHandler = this.context
.getBeanProvider(OneTimeTokenGenerationSuccessHandler.class)
.getIfUnique();
this.oneTimeTokenGenerationSuccessHandler = getBeanOrNull(http, OneTimeTokenGenerationSuccessHandler.class);
}
if (this.oneTimeTokenGenerationSuccessHandler == null) {
throw new IllegalStateException("""
@@ -189,26 +158,21 @@ public final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>>
}
DefaultOneTimeTokenSubmitPageGeneratingFilter submitPage = new DefaultOneTimeTokenSubmitPageGeneratingFilter();
submitPage.setResolveHiddenInputs(this::hiddenInputs);
submitPage.setRequestMatcher(RequestMatcherFactory.matcher(HttpMethod.GET, this.defaultSubmitPageUrl));
submitPage.setLoginProcessingUrl(this.getLoginProcessingUrl());
submitPage.setRequestMatcher(antMatcher(HttpMethod.GET, this.defaultSubmitPageUrl));
submitPage.setLoginProcessingUrl(this.loginProcessingUrl);
http.addFilter(postProcess(submitPage));
}
private AuthenticationProvider getAuthenticationProvider() {
private AuthenticationProvider getAuthenticationProvider(H http) {
if (this.authenticationProvider != null) {
return this.authenticationProvider;
}
UserDetailsService userDetailsService = this.context.getBean(UserDetailsService.class);
this.authenticationProvider = new OneTimeTokenAuthenticationProvider(getOneTimeTokenService(),
UserDetailsService userDetailsService = getContext().getBean(UserDetailsService.class);
this.authenticationProvider = new OneTimeTokenAuthenticationProvider(getOneTimeTokenService(http),
userDetailsService);
return this.authenticationProvider;
}
@Override
protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
return RequestMatcherFactory.matcher(HttpMethod.POST, loginProcessingUrl);
}
/**
* Specifies the {@link AuthenticationProvider} to use when authenticating the user.
* @param authenticationProvider
@@ -246,25 +210,14 @@ public final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>>
* Only POST requests are processed, for that reason make sure that you pass a valid
* CSRF token if CSRF protection is enabled.
* @param loginProcessingUrl
* @see HttpSecurity#csrf(Customizer)
* @see org.springframework.security.config.annotation.web.builders.HttpSecurity#csrf(Customizer)
*/
public OneTimeTokenLoginConfigurer<H> loginProcessingUrl(String loginProcessingUrl) {
Assert.hasText(loginProcessingUrl, "loginProcessingUrl cannot be null or empty");
super.loginProcessingUrl(loginProcessingUrl);
this.loginProcessingUrl = loginProcessingUrl;
return this;
}
/**
* Specifies the URL to send users to if login is required. If used with
* {@link EnableWebSecurity} a default login page will be generated when this
* attribute is not specified.
* @param loginPage
*/
@Override
public OneTimeTokenLoginConfigurer<H> loginPage(String loginPage) {
return super.loginPage(loginPage);
}
/**
* Configures whether the default one-time token submit page should be shown. This
* will prevent the {@link DefaultOneTimeTokenSubmitPageGeneratingFilter} to be
@@ -309,7 +262,7 @@ public final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>>
*/
public OneTimeTokenLoginConfigurer<H> authenticationConverter(AuthenticationConverter authenticationConverter) {
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
this.getAuthenticationFilter().setAuthenticationConverter(authenticationConverter);
this.authenticationConverter = authenticationConverter;
return this;
}
@@ -319,13 +272,11 @@ public final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>>
* {@link SimpleUrlAuthenticationFailureHandler}
* @param authenticationFailureHandler the {@link AuthenticationFailureHandler} to use
* when authentication fails.
* @deprecated Use {@link #failureHandler(AuthenticationFailureHandler)} instead
*/
@Deprecated(since = "6.5")
public OneTimeTokenLoginConfigurer<H> authenticationFailureHandler(
AuthenticationFailureHandler authenticationFailureHandler) {
Assert.notNull(authenticationFailureHandler, "authenticationFailureHandler cannot be null");
super.failureHandler(authenticationFailureHandler);
this.authenticationFailureHandler = authenticationFailureHandler;
return this;
}
@@ -334,57 +285,51 @@ public final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>>
* {@link SavedRequestAwareAuthenticationSuccessHandler} with no additional properties
* set.
* @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler}.
* @deprecated Use {@link #successHandler(AuthenticationSuccessHandler)} instead
*/
@Deprecated(since = "6.5")
public OneTimeTokenLoginConfigurer<H> authenticationSuccessHandler(
AuthenticationSuccessHandler authenticationSuccessHandler) {
Assert.notNull(authenticationSuccessHandler, "authenticationSuccessHandler cannot be null");
super.successHandler(authenticationSuccessHandler);
this.authenticationSuccessHandler = authenticationSuccessHandler;
return this;
}
/**
* Use this {@link GenerateOneTimeTokenRequestResolver} when resolving
* {@link GenerateOneTimeTokenRequest} from {@link HttpServletRequest}. By default,
* the {@link DefaultGenerateOneTimeTokenRequestResolver} is used.
* @param requestResolver the {@link GenerateOneTimeTokenRequestResolver}
* @since 6.5
*/
public OneTimeTokenLoginConfigurer<H> generateRequestResolver(GenerateOneTimeTokenRequestResolver requestResolver) {
Assert.notNull(requestResolver, "requestResolver cannot be null");
this.requestResolver = requestResolver;
return this;
}
private GenerateOneTimeTokenRequestResolver getGenerateRequestResolver() {
if (this.requestResolver != null) {
return this.requestResolver;
private AuthenticationFailureHandler getAuthenticationFailureHandler() {
if (this.authenticationFailureHandler != null) {
return this.authenticationFailureHandler;
}
this.requestResolver = this.context.getBeanProvider(GenerateOneTimeTokenRequestResolver.class)
.getIfUnique(DefaultGenerateOneTimeTokenRequestResolver::new);
return this.requestResolver;
this.authenticationFailureHandler = new SimpleUrlAuthenticationFailureHandler("/login?error");
return this.authenticationFailureHandler;
}
private OneTimeTokenService getOneTimeTokenService() {
private OneTimeTokenService getOneTimeTokenService(H http) {
if (this.oneTimeTokenService != null) {
return this.oneTimeTokenService;
}
this.oneTimeTokenService = this.context.getBeanProvider(OneTimeTokenService.class)
.getIfUnique(InMemoryOneTimeTokenService::new);
OneTimeTokenService bean = getBeanOrNull(http, OneTimeTokenService.class);
if (bean != null) {
this.oneTimeTokenService = bean;
}
else {
this.oneTimeTokenService = new InMemoryOneTimeTokenService();
}
return this.oneTimeTokenService;
}
private <C> C getBeanOrNull(H http, Class<C> clazz) {
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
if (context == null) {
return null;
}
return context.getBeanProvider(clazz).getIfUnique();
}
private Map<String, String> hiddenInputs(HttpServletRequest request) {
CsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
return (token != null) ? Collections.singletonMap(token.getParameterName(), token.getToken())
: Collections.emptyMap();
}
/**
* @deprecated Use this.context instead
*/
@Deprecated
public ApplicationContext getContext() {
return this.context;
}
@@ -28,7 +28,6 @@ import org.springframework.context.ApplicationContext;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
@@ -52,12 +51,12 @@ import org.springframework.security.saml2.provider.service.web.authentication.Op
import org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver;
import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.PortResolver;
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.ParameterRequestMatcher;
@@ -128,11 +127,15 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
private String[] authenticationRequestParams = { "registrationId={registrationId}" };
private RequestMatcher authenticationRequestMatcher;
private RequestMatcher authenticationRequestMatcher = RequestMatchers.anyOf(
new AntPathRequestMatcher(Saml2AuthenticationRequestResolver.DEFAULT_AUTHENTICATION_REQUEST_URI),
new AntPathQueryRequestMatcher(this.authenticationRequestUri, this.authenticationRequestParams));
private Saml2AuthenticationRequestResolver authenticationRequestResolver;
private RequestMatcher loginProcessingUrl;
private RequestMatcher loginProcessingUrl = RequestMatchers.anyOf(
new AntPathRequestMatcher(Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI),
new AntPathRequestMatcher("/login/saml2/sso"));
private RelyingPartyRegistrationRepository relyingPartyRegistrationRepository;
@@ -235,8 +238,8 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
this.authenticationRequestUri = parts[0];
this.authenticationRequestParams = new String[parts.length - 1];
System.arraycopy(parts, 1, this.authenticationRequestParams, 0, parts.length - 1);
this.authenticationRequestMatcher = new PathQueryRequestMatcher(
RequestMatcherFactory.matcher(this.authenticationRequestUri), this.authenticationRequestParams);
this.authenticationRequestMatcher = new AntPathQueryRequestMatcher(this.authenticationRequestUri,
this.authenticationRequestParams);
return this;
}
@@ -253,13 +256,13 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
@Override
public Saml2LoginConfigurer<B> loginProcessingUrl(String loginProcessingUrl) {
Assert.hasText(loginProcessingUrl, "loginProcessingUrl cannot be empty");
this.loginProcessingUrl = RequestMatcherFactory.matcher(loginProcessingUrl);
this.loginProcessingUrl = new AntPathRequestMatcher(loginProcessingUrl);
return this;
}
@Override
protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
return RequestMatcherFactory.matcher(loginProcessingUrl);
return new AntPathRequestMatcher(loginProcessingUrl);
}
/**
@@ -281,7 +284,7 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
relyingPartyRegistrationRepository(http);
this.saml2WebSsoAuthenticationFilter = new Saml2WebSsoAuthenticationFilter(getAuthenticationConverter(http));
this.saml2WebSsoAuthenticationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
this.saml2WebSsoAuthenticationFilter.setRequiresAuthenticationRequestMatcher(getLoginProcessingEndpoint());
this.saml2WebSsoAuthenticationFilter.setRequiresAuthenticationRequestMatcher(this.loginProcessingUrl);
setAuthenticationRequestRepository(http, this.saml2WebSsoAuthenticationFilter);
setAuthenticationFilter(this.saml2WebSsoAuthenticationFilter);
if (StringUtils.hasText(this.loginPage)) {
@@ -337,21 +340,16 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
}
private AuthenticationEntryPoint getLoginEntryPoint(B http, String providerLoginPage) {
RequestMatcher loginPageMatcher = RequestMatcherFactory.matcher(this.getLoginPage());
RequestMatcher faviconMatcher = RequestMatcherFactory.matcher("/favicon.ico");
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<>();
LoginUrlAuthenticationEntryPoint loginUrlEntryPoint = new LoginUrlAuthenticationEntryPoint(providerLoginPage);
PortResolver portResolver = getBeanOrNull(http, PortResolver.class);
if (portResolver != null) {
loginUrlEntryPoint.setPortResolver(portResolver);
}
entryPoints.put(new AndRequestMatcher(notXRequestedWith, new NegatedRequestMatcher(defaultLoginPageMatcher)),
loginUrlEntryPoint);
new LoginUrlAuthenticationEntryPoint(providerLoginPage));
DelegatingAuthenticationEntryPoint loginEntryPoint = new DelegatingAuthenticationEntryPoint(entryPoints);
loginEntryPoint.setDefaultEntryPoint(this.getAuthenticationEntryPoint());
return loginEntryPoint;
@@ -378,38 +376,17 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
if (USE_OPENSAML_5) {
OpenSaml5AuthenticationRequestResolver openSamlAuthenticationRequestResolver = new OpenSaml5AuthenticationRequestResolver(
relyingPartyRegistrationRepository(http));
openSamlAuthenticationRequestResolver.setRequestMatcher(getAuthenticationRequestMatcher());
openSamlAuthenticationRequestResolver.setRequestMatcher(this.authenticationRequestMatcher);
return openSamlAuthenticationRequestResolver;
}
else {
OpenSaml4AuthenticationRequestResolver openSamlAuthenticationRequestResolver = new OpenSaml4AuthenticationRequestResolver(
relyingPartyRegistrationRepository(http));
openSamlAuthenticationRequestResolver.setRequestMatcher(getAuthenticationRequestMatcher());
openSamlAuthenticationRequestResolver.setRequestMatcher(this.authenticationRequestMatcher);
return openSamlAuthenticationRequestResolver;
}
}
private RequestMatcher getAuthenticationRequestMatcher() {
if (this.authenticationRequestMatcher == null) {
this.authenticationRequestMatcher = RequestMatchers.anyOf(
RequestMatcherFactory
.matcher(Saml2AuthenticationRequestResolver.DEFAULT_AUTHENTICATION_REQUEST_URI),
new PathQueryRequestMatcher(RequestMatcherFactory.matcher(this.authenticationRequestUri),
this.authenticationRequestParams));
}
return this.authenticationRequestMatcher;
}
private RequestMatcher getLoginProcessingEndpoint() {
if (this.loginProcessingUrl == null) {
this.loginProcessingUrl = RequestMatchers.anyOf(
RequestMatcherFactory.matcher(Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI),
RequestMatcherFactory.matcher("/login/saml2/sso"));
}
return this.loginProcessingUrl;
}
private AuthenticationConverter getAuthenticationConverter(B http) {
if (this.authenticationConverter != null) {
return this.authenticationConverter;
@@ -430,7 +407,7 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
OpenSaml5AuthenticationTokenConverter converter = new OpenSaml5AuthenticationTokenConverter(
this.relyingPartyRegistrationRepository);
converter.setAuthenticationRequestRepository(getAuthenticationRequestRepository(http));
converter.setRequestMatcher(getLoginProcessingEndpoint());
converter.setRequestMatcher(this.loginProcessingUrl);
return converter;
}
authenticationConverterBean = getBeanOrNull(http, OpenSaml4AuthenticationTokenConverter.class);
@@ -440,7 +417,7 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
OpenSaml4AuthenticationTokenConverter converter = new OpenSaml4AuthenticationTokenConverter(
this.relyingPartyRegistrationRepository);
converter.setAuthenticationRequestRepository(getAuthenticationRequestRepository(http));
converter.setRequestMatcher(getLoginProcessingEndpoint());
converter.setRequestMatcher(this.loginProcessingUrl);
return converter;
}
@@ -464,7 +441,7 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
if (csrf == null) {
return;
}
csrf.ignoringRequestMatchers(getLoginProcessingEndpoint());
csrf.ignoringRequestMatchers(this.loginProcessingUrl);
}
private void initDefaultLoginFilter(B http) {
@@ -532,13 +509,13 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
}
}
static class PathQueryRequestMatcher implements RequestMatcher {
static class AntPathQueryRequestMatcher implements RequestMatcher {
private final RequestMatcher matcher;
PathQueryRequestMatcher(RequestMatcher pathMatcher, String... params) {
AntPathQueryRequestMatcher(String path, String... params) {
List<RequestMatcher> matchers = new ArrayList<>();
matchers.add(pathMatcher);
matchers.add(new AntPathRequestMatcher(path));
for (String param : params) {
String[] parts = param.split("=");
if (parts.length == 1) {
@@ -23,11 +23,9 @@ import jakarta.servlet.http.HttpServletRequest;
import org.opensaml.core.Version;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpMethod;
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.RequestMatcherFactory;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
@@ -66,6 +64,7 @@ import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.security.web.csrf.CsrfLogoutHandler;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.ParameterRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
@@ -305,19 +304,19 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
}
private RequestMatcher createLogoutMatcher() {
RequestMatcher logout = RequestMatcherFactory.matcher(HttpMethod.POST, this.logoutUrl);
RequestMatcher logout = new AntPathRequestMatcher(this.logoutUrl, "POST");
RequestMatcher saml2 = new Saml2RequestMatcher(getSecurityContextHolderStrategy());
return new AndRequestMatcher(logout, saml2);
}
private RequestMatcher createLogoutRequestMatcher() {
RequestMatcher logout = RequestMatcherFactory.matcher(this.logoutRequestConfigurer.logoutUrl);
RequestMatcher logout = new AntPathRequestMatcher(this.logoutRequestConfigurer.logoutUrl);
RequestMatcher samlRequest = new ParameterRequestMatcher("SAMLRequest");
return new AndRequestMatcher(logout, samlRequest);
}
private RequestMatcher createLogoutResponseMatcher() {
RequestMatcher logout = RequestMatcherFactory.matcher(this.logoutResponseConfigurer.logoutUrl);
RequestMatcher logout = new AntPathRequestMatcher(this.logoutResponseConfigurer.logoutUrl);
RequestMatcher samlResponse = new ParameterRequestMatcher("SAMLResponse");
return new AndRequestMatcher(logout, samlResponse);
}
@@ -22,7 +22,6 @@ import org.opensaml.core.Version;
import org.springframework.context.ApplicationContext;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.saml2.provider.service.metadata.OpenSaml4MetadataResolver;
@@ -33,6 +32,7 @@ import org.springframework.security.saml2.provider.service.registration.RelyingP
import org.springframework.security.saml2.provider.service.web.Saml2MetadataFilter;
import org.springframework.security.saml2.provider.service.web.metadata.RequestMatcherMetadataResponseResolver;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;
/**
@@ -111,12 +111,12 @@ public class Saml2MetadataConfigurer<H extends HttpSecurityBuilder<H>>
if (USE_OPENSAML_5) {
RequestMatcherMetadataResponseResolver metadata = new RequestMatcherMetadataResponseResolver(
registrations, new OpenSaml5MetadataResolver());
metadata.setRequestMatcher(RequestMatcherFactory.matcher(metadataUrl));
metadata.setRequestMatcher(new AntPathRequestMatcher(metadataUrl));
return metadata;
}
RequestMatcherMetadataResponseResolver metadata = new RequestMatcherMetadataResponseResolver(registrations,
new OpenSaml4MetadataResolver());
metadata.setRequestMatcher(RequestMatcherFactory.matcher(metadataUrl));
metadata.setRequestMatcher(new AntPathRequestMatcher(metadataUrl));
return metadata;
};
return this;
@@ -21,7 +21,6 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler;
import org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;
import org.springframework.security.messaging.util.matcher.MessageMatcherFactory;
import org.springframework.util.AntPathMatcher;
final class MessageMatcherAuthorizationManagerConfiguration {
@@ -30,10 +29,6 @@ final class MessageMatcherAuthorizationManagerConfiguration {
@Scope("prototype")
MessageMatcherDelegatingAuthorizationManager.Builder messageAuthorizationManagerBuilder(
ApplicationContext context) {
MessageMatcherFactory.setApplicationContext(context);
if (MessageMatcherFactory.usesPathPatterns()) {
return MessageMatcherDelegatingAuthorizationManager.builder();
}
return MessageMatcherDelegatingAuthorizationManager.builder()
.simpDestPathMatcher(
() -> (context.getBeanNamesForType(SimpAnnotationMethodMessageHandler.class).length > 0)
@@ -1,43 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.aot.hint;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference;
/**
* Runtime hints for
* {@link org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration}
*
* @author Marcus da Coregio
*/
class WebSecurityConfigurationRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.reflection()
.registerType(TypeReference
.of("org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy"),
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
hints.reflection()
.registerType(TypeReference.of("org.springframework.web.filter.ServletRequestPathFilter"),
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
}
}
@@ -65,7 +65,8 @@ public class AuthenticationManagerFactoryBean implements FactoryBean<Authenticat
if (uds == null) {
throw new NoSuchBeanDefinitionException(BeanIds.AUTHENTICATION_MANAGER, MISSING_BEAN_ERROR_MESSAGE);
}
DaoAuthenticationProvider provider = new DaoAuthenticationProvider(uds);
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(uds);
PasswordEncoder passwordEncoder = this.bf.getBeanProvider(PasswordEncoder.class).getIfUnique();
if (passwordEncoder != null) {
provider.setPasswordEncoder(passwordEncoder);
@@ -21,8 +21,6 @@ import java.util.List;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.annotation.SecurityAnnotationScanner;
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;
/**
@@ -31,14 +29,7 @@ import org.springframework.security.web.access.channel.ChannelDecisionManagerImp
*
* @author Luke Taylor
* @since 3.0
* @deprecated In modern Spring Security APIs, each API manages its own configuration
* context. As such there is no direct replacement for this interface. In the case of
* method security, please see {@link SecurityAnnotationScanner} and
* {@link AuthorizationManager}. In the case of channel security, please see
* {@code HttpsRedirectFilter}. In the case of web security, please see
* {@link AuthorizationManager}.
*/
@Deprecated
public final class ChannelAttributeFactory {
private static final String OPT_REQUIRES_HTTP = "http";
@@ -39,7 +39,6 @@ import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.UnreachableFilterChainException;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.access.intercept.AuthorizationFilter;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
@@ -54,6 +53,7 @@ import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
import org.springframework.security.web.session.SessionManagementFilter;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
public class DefaultFilterChainValidator implements FilterChainProxy.FilterChainValidator {
@@ -69,67 +69,31 @@ public class DefaultFilterChainValidator implements FilterChainProxy.FilterChain
}
checkPathOrder(new ArrayList<>(fcp.getFilterChains()));
checkForDuplicateMatchers(new ArrayList<>(fcp.getFilterChains()));
checkAuthorizationFilters(new ArrayList<>(fcp.getFilterChains()));
}
private void checkPathOrder(List<SecurityFilterChain> filterChains) {
// Check that the universal pattern is listed at the end, if at all
Iterator<SecurityFilterChain> chains = filterChains.iterator();
while (chains.hasNext()) {
if (chains.next() instanceof DefaultSecurityFilterChain securityFilterChain) {
if (AnyRequestMatcher.INSTANCE.equals(securityFilterChain.getRequestMatcher()) && chains.hasNext()) {
throw new UnreachableFilterChainException("A universal match pattern ('/**') is defined "
+ " before other patterns in the filter chain, causing them to be ignored. Please check the "
+ "ordering in your <security:http> namespace or FilterChainProxy bean configuration",
securityFilterChain, chains.next());
}
RequestMatcher matcher = ((DefaultSecurityFilterChain) chains.next()).getRequestMatcher();
if (AnyRequestMatcher.INSTANCE.equals(matcher) && chains.hasNext()) {
throw new IllegalArgumentException("A universal match pattern ('/**') is defined "
+ " before other patterns in the filter chain, causing them to be ignored. Please check the "
+ "ordering in your <security:http> namespace or FilterChainProxy bean configuration");
}
}
}
private void checkForDuplicateMatchers(List<SecurityFilterChain> chains) {
DefaultSecurityFilterChain filterChain = null;
for (SecurityFilterChain chain : chains) {
if (filterChain != null) {
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
if (defaultChain.getRequestMatcher().equals(filterChain.getRequestMatcher())) {
throw new UnreachableFilterChainException(
"The FilterChainProxy contains two filter chains using the" + " matcher "
+ defaultChain.getRequestMatcher()
+ ". If you are using multiple <http> namespace "
+ "elements, you must use a 'pattern' attribute to define the request patterns to which they apply.",
defaultChain, chain);
}
while (chains.size() > 1) {
DefaultSecurityFilterChain chain = (DefaultSecurityFilterChain) chains.remove(0);
for (SecurityFilterChain test : chains) {
if (chain.getRequestMatcher().equals(((DefaultSecurityFilterChain) test).getRequestMatcher())) {
throw new IllegalArgumentException("The FilterChainProxy contains two filter chains using the"
+ " matcher " + chain.getRequestMatcher() + ". If you are using multiple <http> namespace "
+ "elements, you must use a 'pattern' attribute to define the request patterns to which they apply.");
}
}
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
filterChain = defaultChain;
}
}
}
private void checkAuthorizationFilters(List<SecurityFilterChain> chains) {
Filter authorizationFilter = null;
Filter filterSecurityInterceptor = null;
for (SecurityFilterChain chain : chains) {
for (Filter filter : chain.getFilters()) {
if (filter instanceof AuthorizationFilter) {
authorizationFilter = filter;
}
if (filter instanceof FilterSecurityInterceptor) {
filterSecurityInterceptor = filter;
}
}
if (authorizationFilter != null && filterSecurityInterceptor != null) {
this.logger.warn(
"It is not recommended to use authorizeRequests in the configuration. Please only use authorizeHttpRequests");
}
if (filterSecurityInterceptor != null) {
this.logger.warn(
"Usage of authorizeRequests is deprecated. Please use authorizeHttpRequests in the configuration");
}
authorizationFilter = null;
filterSecurityInterceptor = null;
}
}
@@ -178,7 +178,7 @@ public class FormLoginBeanDefinitionParser {
}
this.loginProcessingUrl = loginUrl;
BeanDefinitionBuilder matcherBuilder = BeanDefinitionBuilder
.rootBeanDefinition(RequestMatcherFactoryBean.class);
.rootBeanDefinition("org.springframework.security.web.util.matcher.AntPathRequestMatcher");
matcherBuilder.addConstructorArgValue(loginUrl);
if (this.loginMethod != null) {
matcherBuilder.addConstructorArgValue("POST");
@@ -51,7 +51,6 @@ import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.HandlerMappingIntrospectorRequestTransformer;
import org.springframework.security.web.access.PathPatternRequestTransformer;
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.web.access.channel.InsecureChannelProcessor;
@@ -83,7 +82,7 @@ import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
import org.springframework.security.web.session.SessionManagementFilter;
import org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy;
import org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy;
import org.springframework.security.web.transport.HttpsRedirectFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@@ -123,10 +122,6 @@ class HttpConfigurationBuilder {
private static final String ATT_SESSION_AUTH_STRATEGY_REF = "session-authentication-strategy-ref";
private static final String ATT_MAX_SESSIONS_REF = "max-sessions-ref";
private static final String ATT_MAX_SESSIONS = "max-sessions";
private static final String ATT_SESSION_AUTH_ERROR_URL = "session-authentication-error-url";
private static final String ATT_SECURITY_CONTEXT_HOLDER_STRATEGY = "security-context-holder-strategy-ref";
@@ -177,8 +172,6 @@ class HttpConfigurationBuilder {
private BeanDefinition cpf;
private BeanDefinition httpsRedirectFilter;
private BeanDefinition securityContextPersistenceFilter;
private BeanDefinition forceEagerSessionCreationFilter;
@@ -255,7 +248,6 @@ class HttpConfigurationBuilder {
createServletApiFilter(authenticationManager);
createJaasApiFilter();
createChannelProcessingFilter();
createHttpsRedirectFilter();
createFilterSecurity(authenticationManager);
createAddHeadersFilter();
createCorsFilter();
@@ -493,16 +485,10 @@ class HttpConfigurationBuilder {
concurrentSessionStrategy.addConstructorArgValue(this.sessionRegistryRef);
String maxSessions = this.pc.getReaderContext()
.getEnvironment()
.resolvePlaceholders(sessionCtrlElt.getAttribute(ATT_MAX_SESSIONS));
.resolvePlaceholders(sessionCtrlElt.getAttribute("max-sessions"));
if (StringUtils.hasText(maxSessions)) {
concurrentSessionStrategy.addPropertyValue("maximumSessions", maxSessions);
}
String maxSessionsRef = this.pc.getReaderContext()
.getEnvironment()
.resolvePlaceholders(sessionCtrlElt.getAttribute(ATT_MAX_SESSIONS_REF));
if (StringUtils.hasText(maxSessionsRef)) {
concurrentSessionStrategy.addPropertyReference("maximumSessions", maxSessionsRef);
}
String exceptionIfMaximumExceeded = sessionCtrlElt.getAttribute("error-if-maximum-exceeded");
if (StringUtils.hasText(exceptionIfMaximumExceeded)) {
concurrentSessionStrategy.addPropertyValue("exceptionIfMaximumExceeded", exceptionIfMaximumExceeded);
@@ -605,12 +591,6 @@ class HttpConfigurationBuilder {
.error("Cannot use 'expired-url' attribute and 'expired-session-strategy-ref'" + " attribute together.",
source);
}
String maxSessions = element.getAttribute(ATT_MAX_SESSIONS);
String maxSessionsRef = element.getAttribute(ATT_MAX_SESSIONS_REF);
if (StringUtils.hasText(maxSessions) && StringUtils.hasText(maxSessionsRef)) {
this.pc.getReaderContext()
.error("Cannot use 'max-sessions' attribute and 'max-sessions-ref' attribute together.", source);
}
if (StringUtils.hasText(expiryUrl)) {
BeanDefinitionBuilder expiredSessionBldr = BeanDefinitionBuilder
.rootBeanDefinition(SimpleRedirectSessionInformationExpiredStrategy.class);
@@ -660,19 +640,6 @@ class HttpConfigurationBuilder {
}
}
private void createHttpsRedirectFilter() {
String ref = this.httpElt
.getAttribute(HttpSecurityBeanDefinitionParser.ATT_REDIRECT_TO_HTTPS_REQUEST_MATCHER_REF);
if (!StringUtils.hasText(ref)) {
return;
}
RootBeanDefinition channelFilter = new RootBeanDefinition(HttpsRedirectFilter.class);
channelFilter.getPropertyValues().addPropertyValue("requestMatcher", new RuntimeBeanReference(ref));
channelFilter.getPropertyValues().addPropertyValue("portMapper", this.portMapper);
this.httpsRedirectFilter = channelFilter;
}
@Deprecated
private void createChannelProcessingFilter() {
ManagedMap<BeanMetadataElement, BeanDefinition> channelRequestMap = parseInterceptUrlsForChannelSecurity();
if (channelRequestMap.isEmpty()) {
@@ -708,9 +675,7 @@ class HttpConfigurationBuilder {
* Parses the intercept-url elements to obtain the map used by channel security. This
* will be empty unless the <tt>requires-channel</tt> attribute has been used on a URL
* path.
* @deprecated please use {@link #createHttpsRedirectFilter} instead
*/
@Deprecated
private ManagedMap<BeanMetadataElement, BeanDefinition> parseInterceptUrlsForChannelSecurity() {
ManagedMap<BeanMetadataElement, BeanDefinition> channelRequestMap = new ManagedMap<>();
for (Element urlElt : this.interceptUrls) {
@@ -751,7 +716,7 @@ class HttpConfigurationBuilder {
requestCacheBldr.addPropertyValue("portResolver", this.portResolver);
if (this.csrfFilter != null) {
BeanDefinitionBuilder requestCacheMatcherBldr = BeanDefinitionBuilder
.rootBeanDefinition(RequestMatcherFactoryBean.class);
.rootBeanDefinition(AntPathRequestMatcher.class);
requestCacheMatcherBldr.addConstructorArgValue("/**");
requestCacheMatcherBldr.addConstructorArgValue("GET");
requestCacheBldr.addPropertyValue("requestMatcher", requestCacheMatcherBldr.getBeanDefinition());
@@ -916,9 +881,6 @@ class HttpConfigurationBuilder {
if (this.disableUrlRewriteFilter != null) {
filters.add(new OrderDecorator(this.disableUrlRewriteFilter, SecurityFilters.DISABLE_ENCODE_URL_FILTER));
}
if (this.httpsRedirectFilter != null) {
filters.add(new OrderDecorator(this.httpsRedirectFilter, SecurityFilters.HTTPS_REDIRECT_FILTER));
}
if (this.cpf != null) {
filters.add(new OrderDecorator(this.cpf, SecurityFilters.CHANNEL_FILTER));
}
@@ -975,17 +937,10 @@ class HttpConfigurationBuilder {
@Override
public AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer getObject()
throws Exception {
AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer requestTransformer = this.applicationContext
.getBeanProvider(
AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer.class)
.getIfUnique();
if (requestTransformer != null) {
return requestTransformer;
}
HandlerMappingIntrospector hmi = this.applicationContext.getBeanProvider(HandlerMappingIntrospector.class)
.getIfAvailable();
return (hmi != null) ? new HandlerMappingIntrospectorRequestTransformer(hmi)
: new PathPatternRequestTransformer();
: AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer.IDENTITY;
}
@Override
@@ -81,8 +81,6 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
static final String ATT_REQUEST_MATCHER_REF = "request-matcher-ref";
static final String ATT_REDIRECT_TO_HTTPS_REQUEST_MATCHER_REF = "redirect-to-https-request-matcher-ref";
static final String ATT_PATH_PATTERN = "pattern";
static final String ATT_HTTP_METHOD = "method";
@@ -242,10 +240,6 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
}
private RuntimeBeanReference createPortResolver(BeanReference portMapper, ParserContext pc) {
String beanName = "portResolver";
if (pc.getRegistry().containsBeanDefinition(beanName)) {
return new RuntimeBeanReference(beanName);
}
RootBeanDefinition portResolver = new RootBeanDefinition(PortResolverImpl.class);
portResolver.getPropertyValues().addPropertyValue("portMapper", portMapper);
String portResolverName = pc.getReaderContext().generateBeanName(portResolver);
@@ -134,7 +134,7 @@ class LogoutBeanDefinitionParser implements BeanDefinitionParser {
private BeanDefinition getLogoutRequestMatcher(String logoutUrl) {
BeanDefinitionBuilder matcherBuilder = BeanDefinitionBuilder
.rootBeanDefinition(RequestMatcherFactoryBean.class);
.rootBeanDefinition("org.springframework.security.web.util.matcher.AntPathRequestMatcher");
matcherBuilder.addConstructorArgValue(logoutUrl);
if (this.csrfEnabled) {
matcherBuilder.addConstructorArgValue("POST");
@@ -1,78 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.http;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.messaging.simp.SimpMessageType;
import org.springframework.security.messaging.util.matcher.MessageMatcher;
import org.springframework.security.messaging.util.matcher.PathPatternMessageMatcher;
import org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
@Deprecated
public final class MessageMatcherFactoryBean implements FactoryBean<MessageMatcher<?>>, ApplicationContextAware {
private PathPatternMessageMatcher.Builder builder;
private final SimpMessageType method;
private final String path;
private PathMatcher pathMatcher = new AntPathMatcher();
public MessageMatcherFactoryBean(String path) {
this(path, null);
}
public MessageMatcherFactoryBean(String path, SimpMessageType method) {
this.method = method;
this.path = path;
}
@Override
public MessageMatcher<?> getObject() throws Exception {
if (this.builder != null) {
return this.builder.matcher(this.method, this.path);
}
if (this.method == SimpMessageType.SUBSCRIBE) {
return SimpDestinationMessageMatcher.createSubscribeMatcher(this.path, this.pathMatcher);
}
if (this.method == SimpMessageType.MESSAGE) {
return SimpDestinationMessageMatcher.createMessageMatcher(this.path, this.pathMatcher);
}
return new SimpDestinationMessageMatcher(this.path, this.pathMatcher);
}
@Override
public Class<?> getObjectType() {
return null;
}
public void setPathMatcher(PathMatcher pathMatcher) {
this.pathMatcher = pathMatcher;
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.builder = context.getBeanProvider(PathPatternMessageMatcher.Builder.class).getIfUnique();
}
}
@@ -34,7 +34,6 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.core.ResolvableType;
import org.springframework.security.oauth2.client.AuthorizationCodeOAuth2AuthorizedClientProvider;
@@ -198,12 +197,6 @@ final class OAuth2AuthorizedClientManagerRegistrar implements BeanDefinitionRegi
authorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient);
}
ApplicationEventPublisher applicationEventPublisher = getBeanOfType(
ResolvableType.forClass(ApplicationEventPublisher.class));
if (applicationEventPublisher != null) {
authorizedClientProvider.setApplicationEventPublisher(applicationEventPublisher);
}
return authorizedClientProvider;
}
@@ -19,6 +19,7 @@ package org.springframework.security.config.http;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -26,13 +27,11 @@ import org.w3c.dom.Element;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.context.ApplicationContext;
@@ -58,6 +57,7 @@ import org.springframework.security.web.authentication.DelegatingAuthenticationE
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.MediaTypeRequestMatcher;
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
@@ -235,7 +235,7 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser {
.getBeanDefinition();
}
else {
Map<BeanDefinition, AuthenticationEntryPoint> entryPoint = getLoginEntryPoint(element);
Map<RequestMatcher, AuthenticationEntryPoint> entryPoint = getLoginEntryPoint(element);
if (entryPoint != null) {
this.oauth2LoginAuthenticationEntryPoint = BeanDefinitionBuilder
.rootBeanDefinition(DelegatingAuthenticationEntryPoint.class)
@@ -364,35 +364,42 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser {
return this.oauth2LoginLinks;
}
private Map<BeanDefinition, AuthenticationEntryPoint> getLoginEntryPoint(Element element) {
Map<BeanDefinition, AuthenticationEntryPoint> entryPoints = null;
private Map<RequestMatcher, AuthenticationEntryPoint> getLoginEntryPoint(Element element) {
Map<RequestMatcher, AuthenticationEntryPoint> entryPoints = null;
Element clientRegsElt = DomUtils.getChildElementByTagName(element.getOwnerDocument().getDocumentElement(),
Elements.CLIENT_REGISTRATIONS);
if (clientRegsElt != null) {
List<Element> clientRegList = DomUtils.getChildElementsByTagName(clientRegsElt, ELT_CLIENT_REGISTRATION);
if (clientRegList.size() == 1) {
BeanDefinition loginPageMatcher = BeanDefinitionBuilder
.rootBeanDefinition(RequestMatcherFactoryBean.class)
.addConstructorArgValue(DEFAULT_LOGIN_URI)
.getBeanDefinition();
BeanDefinition faviconMatcher = BeanDefinitionBuilder
.rootBeanDefinition(RequestMatcherFactoryBean.class)
.addConstructorArgValue("/favicon.ico")
.getBeanDefinition();
BeanDefinition entryPointMatcher = BeanDefinitionBuilder
.rootBeanDefinition(EntryPointMatcherFactoryBean.class)
.addConstructorArgValue(loginPageMatcher)
.addConstructorArgValue(faviconMatcher)
.getBeanDefinition();
RequestMatcher loginPageMatcher = new AntPathRequestMatcher(DEFAULT_LOGIN_URI);
RequestMatcher faviconMatcher = new AntPathRequestMatcher("/favicon.ico");
RequestMatcher defaultEntryPointMatcher = this.getAuthenticationEntryPointMatcher();
RequestMatcher defaultLoginPageMatcher = new AndRequestMatcher(
new OrRequestMatcher(loginPageMatcher, faviconMatcher), defaultEntryPointMatcher);
RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
Element clientRegElt = clientRegList.get(0);
entryPoints = new ManagedMap<>();
entryPoints.put(entryPointMatcher, new LoginUrlAuthenticationEntryPoint(
DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/" + clientRegElt.getAttribute(ATT_REGISTRATION_ID)));
entryPoints = new LinkedHashMap<>();
entryPoints.put(
new AndRequestMatcher(notXRequestedWith, new NegatedRequestMatcher(defaultLoginPageMatcher)),
new LoginUrlAuthenticationEntryPoint(DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/"
+ clientRegElt.getAttribute(ATT_REGISTRATION_ID)));
}
}
return entryPoints;
}
private RequestMatcher getAuthenticationEntryPointMatcher() {
ContentNegotiationStrategy contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
MediaTypeRequestMatcher mediaMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy,
MediaType.APPLICATION_XHTML_XML, new MediaType("image", "*"), MediaType.TEXT_HTML,
MediaType.TEXT_PLAIN);
mediaMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
return new AndRequestMatcher(Arrays.asList(notXRequestedWith, mediaMatcher));
}
private static class OidcAuthenticationRequestChecker implements AuthenticationProvider {
@Override
@@ -456,42 +463,4 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser {
}
@Deprecated
static class EntryPointMatcherFactoryBean implements FactoryBean<RequestMatcher> {
private final RequestMatcher entryPointMatcher;
EntryPointMatcherFactoryBean(RequestMatcher loginPageMatcher, RequestMatcher faviconMatcher) {
RequestMatcher defaultEntryPointMatcher = getAuthenticationEntryPointMatcher();
RequestMatcher defaultLoginPageMatcher = new AndRequestMatcher(
new OrRequestMatcher(loginPageMatcher, faviconMatcher), defaultEntryPointMatcher);
RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
this.entryPointMatcher = new AndRequestMatcher(notXRequestedWith,
new NegatedRequestMatcher(defaultLoginPageMatcher));
}
private RequestMatcher getAuthenticationEntryPointMatcher() {
ContentNegotiationStrategy contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
MediaTypeRequestMatcher mediaMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy,
MediaType.APPLICATION_XHTML_XML, new MediaType("image", "*"), MediaType.TEXT_HTML,
MediaType.TEXT_PLAIN);
mediaMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
return new AndRequestMatcher(Arrays.asList(notXRequestedWith, mediaMatcher));
}
@Override
public RequestMatcher getObject() {
return this.entryPointMatcher;
}
@Override
public Class<?> getObjectType() {
return RequestMatcher.class;
}
}
}
@@ -40,7 +40,7 @@ import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
import org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenAuthenticationProvider;
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
@@ -300,7 +300,7 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
String clientId = element.getAttribute(CLIENT_ID);
String clientSecret = element.getAttribute(CLIENT_SECRET);
BeanDefinitionBuilder introspectorBuilder = BeanDefinitionBuilder
.rootBeanDefinition(SpringOpaqueTokenIntrospector.class);
.rootBeanDefinition(NimbusOpaqueTokenIntrospector.class);
introspectorBuilder.addConstructorArgValue(introspectionUri);
introspectorBuilder.addConstructorArgValue(clientId);
introspectorBuilder.addConstructorArgValue(clientSecret);
@@ -1,64 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.http;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.http.HttpMethod;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
@Deprecated
public final class RequestMatcherFactoryBean implements FactoryBean<RequestMatcher>, ApplicationContextAware {
private PathPatternRequestMatcher.Builder builder;
private final HttpMethod method;
private final String path;
public RequestMatcherFactoryBean(String path) {
this(path, null);
}
public RequestMatcherFactoryBean(String path, HttpMethod method) {
this.method = method;
this.path = path;
}
@Override
public RequestMatcher getObject() throws Exception {
if (this.builder != null) {
return this.builder.matcher(this.method, this.path);
}
return new AntPathRequestMatcher(this.path, (this.method != null) ? this.method.name() : null);
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.builder = context.getBeanProvider(PathPatternRequestMatcher.Builder.class).getIfUnique();
}
}
@@ -43,6 +43,7 @@ import org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthen
import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
@@ -215,7 +216,7 @@ final class Saml2LoginBeanDefinitionParser implements BeanDefinitionParser {
}
if (saml2LoginAuthenticationEntryPoint != null) {
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
.rootBeanDefinition(RequestMatcherFactoryBean.class);
.rootBeanDefinition(AntPathRequestMatcher.class);
requestMatcherBuilder.addConstructorArgValue(this.loginProcessingUrl);
BeanDefinition requestMatcher = requestMatcherBuilder.getBeanDefinition();
this.entryPoints.put(requestMatcher, saml2LoginAuthenticationEntryPoint);
@@ -259,7 +260,7 @@ final class Saml2LoginBeanDefinitionParser implements BeanDefinitionParser {
private void registerDefaultCsrfOverride() {
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
.rootBeanDefinition(RequestMatcherFactoryBean.class);
.rootBeanDefinition(AntPathRequestMatcher.class);
requestMatcherBuilder.addConstructorArgValue(this.loginProcessingUrl);
BeanDefinition requestMatcher = requestMatcherBuilder.getBeanDefinition();
this.csrfIgnoreRequestMatchers.add(requestMatcher);
@@ -41,6 +41,7 @@ import org.springframework.security.web.authentication.logout.LogoutSuccessEvent
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.ParameterRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.CollectionUtils;
@@ -170,7 +171,7 @@ final class Saml2LogoutBeanDefinitionParser implements BeanDefinitionParser {
}
private BeanMetadataElement createLogoutRequestMatcher() {
BeanMetadataElement logoutMatcher = BeanDefinitionBuilder.rootBeanDefinition(RequestMatcherFactoryBean.class)
BeanMetadataElement logoutMatcher = BeanDefinitionBuilder.rootBeanDefinition(AntPathRequestMatcher.class)
.addConstructorArgValue(this.logoutUrl)
.addConstructorArgValue("POST")
.getBeanDefinition();
@@ -183,8 +184,7 @@ final class Saml2LogoutBeanDefinitionParser implements BeanDefinitionParser {
}
private BeanMetadataElement createSaml2LogoutRequestMatcher() {
BeanMetadataElement logoutRequestMatcher = BeanDefinitionBuilder
.rootBeanDefinition(RequestMatcherFactoryBean.class)
BeanMetadataElement logoutRequestMatcher = BeanDefinitionBuilder.rootBeanDefinition(AntPathRequestMatcher.class)
.addConstructorArgValue(this.logoutRequestUrl)
.getBeanDefinition();
BeanMetadataElement saml2RequestMatcher = BeanDefinitionBuilder
@@ -198,7 +198,7 @@ final class Saml2LogoutBeanDefinitionParser implements BeanDefinitionParser {
private BeanMetadataElement createSaml2LogoutResponseMatcher() {
BeanMetadataElement logoutResponseMatcher = BeanDefinitionBuilder
.rootBeanDefinition(RequestMatcherFactoryBean.class)
.rootBeanDefinition(AntPathRequestMatcher.class)
.addConstructorArgValue(this.logoutResponseUrl)
.getBeanDefinition();
BeanMetadataElement saml2ResponseMatcher = BeanDefinitionBuilder
@@ -33,11 +33,8 @@ enum SecurityFilters {
FORCE_EAGER_SESSION_FILTER,
@Deprecated
CHANNEL_FILTER,
HTTPS_REDIRECT_FILTER,
SECURITY_CONTEXT_FILTER,
CONCURRENT_SESSION_FILTER,
@@ -23,6 +23,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.web.RequestMatcherRedirectFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StringUtils;
/**
@@ -44,12 +45,9 @@ public final class WellKnownChangePasswordBeanDefinitionParser implements BeanDe
*/
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinition requestMatcher = BeanDefinitionBuilder.rootBeanDefinition(RequestMatcherFactoryBean.class)
.addConstructorArgValue(WELL_KNOWN_CHANGE_PASSWORD_PATTERN)
.getBeanDefinition();
BeanDefinition changePasswordFilter = BeanDefinitionBuilder
.rootBeanDefinition(RequestMatcherRedirectFilter.class)
.addConstructorArgValue(requestMatcher)
.addConstructorArgValue(new AntPathRequestMatcher(WELL_KNOWN_CHANGE_PASSWORD_PATTERN))
.addConstructorArgValue(getChangePasswordPage(element))
.getBeanDefinition();
parserContext.getReaderContext().registerWithGeneratedName(changePasswordFilter);
@@ -24,7 +24,6 @@ import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.config.BeanIds;
/**
@@ -33,9 +32,7 @@ import org.springframework.security.config.BeanIds;
* @author Luke Taylor
* @author Ben Alex
* @author Rob Winch
* @deprecated Please use {@link AuthorizationManager} instead
*/
@Deprecated
abstract class MethodConfigUtils {
@SuppressWarnings("unchecked")
@@ -125,7 +125,7 @@ public final class ClientRegistrationsBeanDefinitionParser implements BeanDefini
getOptionalIfNotEmpty(parserContext, clientRegistrationElt.getAttribute(ATT_CLIENT_SECRET))
.ifPresent(builder::clientSecret);
getOptionalIfNotEmpty(parserContext, clientRegistrationElt.getAttribute(ATT_CLIENT_AUTHENTICATION_METHOD))
.map(ClientAuthenticationMethod::valueOf)
.map(ClientAuthenticationMethod::new)
.ifPresent(builder::clientAuthenticationMethod);
getOptionalIfNotEmpty(parserContext, clientRegistrationElt.getAttribute(ATT_AUTHORIZATION_GRANT_TYPE))
.map(AuthorizationGrantType::new)
@@ -1,133 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.web;
import reactor.util.annotation.NonNull;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.web.util.pattern.PathPatternParser;
/**
* Use this factory bean to configure the {@link PathPatternRequestMatcher.Builder} bean
* used to create request matchers in {@link AuthorizeHttpRequestsConfigurer} and other
* parts of the DSL.
*
* @author Josh Cummings
* @since 6.5
*/
public final class PathPatternRequestMatcherBuilderFactoryBean implements
FactoryBean<PathPatternRequestMatcher.Builder>, ApplicationContextAware, BeanNameAware, BeanFactoryAware {
static final String MVC_PATTERN_PARSER_BEAN_NAME = "mvcPatternParser";
private final PathPatternParser parser;
private ApplicationContext context;
private String beanName;
private ConfigurableListableBeanFactory beanFactory;
/**
* Construct this factory bean using the default {@link PathPatternParser}
*
* <p>
* If you are using Spring MVC, it will use the Spring MVC instance.
*/
public PathPatternRequestMatcherBuilderFactoryBean() {
this(null);
}
/**
* Construct this factory bean using this {@link PathPatternParser}.
*
* <p>
* If you are using Spring MVC, it is likely incorrect to call this constructor.
* Please call the default constructor instead.
* @param parser the {@link PathPatternParser} to use
*/
public PathPatternRequestMatcherBuilderFactoryBean(PathPatternParser parser) {
this.parser = parser;
}
@Override
public PathPatternRequestMatcher.Builder getObject() throws Exception {
if (!this.context.containsBean(MVC_PATTERN_PARSER_BEAN_NAME)) {
PathPatternParser parser = (this.parser != null) ? this.parser : PathPatternParser.defaultInstance;
return PathPatternRequestMatcher.withPathPatternParser(parser);
}
PathPatternParser mvc = this.context.getBean(MVC_PATTERN_PARSER_BEAN_NAME, PathPatternParser.class);
PathPatternParser parser = (this.parser != null) ? this.parser : mvc;
if (mvc.equals(parser)) {
return PathPatternRequestMatcher.withPathPatternParser(parser);
}
throw new IllegalArgumentException("Spring Security and Spring MVC must use the same path pattern parser. "
+ "To have Spring Security use Spring MVC's [" + describe(mvc, MVC_PATTERN_PARSER_BEAN_NAME)
+ "] simply publish this bean [" + describe(this, this.beanName) + "] using its default constructor");
}
@Override
public Class<?> getObjectType() {
return PathPatternRequestMatcher.Builder.class;
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.context = context;
}
@Override
public void setBeanName(@NonNull String name) {
this.beanName = name;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableListableBeanFactory listable) {
this.beanFactory = listable;
}
}
private String describe(Object bean, String name) {
String text = bean.getClass().getSimpleName();
if (name == null) {
return text;
}
text += "defined as '" + name + "'";
if (this.beanFactory == null) {
return text;
}
BeanDefinition bd = this.beanFactory.getBeanDefinition(name);
String description = bd.getResourceDescription();
if (description == null) {
return text;
}
text += " in [" + description + "]";
return text;
}
}
@@ -1,66 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.web.messaging;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;
import org.springframework.security.messaging.util.matcher.PathPatternMessageMatcher;
import org.springframework.web.util.pattern.PathPatternParser;
/**
* Use this factory bean to configure the {@link PathPatternMessageMatcher.Builder} bean
* used to create request matchers in {@link MessageMatcherDelegatingAuthorizationManager}
* and other parts of the DSL.
*
* @author Pat McCusker
* @since 6.5
*/
public final class PathPatternMessageMatcherBuilderFactoryBean
implements FactoryBean<PathPatternMessageMatcher.Builder> {
private PathPatternParser parser;
/**
* Create {@link PathPatternMessageMatcher}s using
* {@link PathPatternParser#defaultInstance}
*/
public PathPatternMessageMatcherBuilderFactoryBean() {
}
/**
* Create {@link PathPatternMessageMatcher}s using the given {@link PathPatternParser}
* @param parser the {@link PathPatternParser} to use
*/
public PathPatternMessageMatcherBuilderFactoryBean(PathPatternParser parser) {
this.parser = parser;
}
@Override
public PathPatternMessageMatcher.Builder getObject() throws Exception {
if (this.parser == null) {
return PathPatternMessageMatcher.withDefaults();
}
return PathPatternMessageMatcher.withPathPatternParser(this.parser);
}
@Override
public Class<?> getObjectType() {
return PathPatternMessageMatcher.Builder.class;
}
}
@@ -29,7 +29,6 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -54,7 +53,6 @@ import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.DelegatingReactiveAuthenticationManager;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.ReactiveAuthenticationManagerResolver;
import org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;
import org.springframework.security.authentication.ott.OneTimeToken;
import org.springframework.security.authentication.ott.reactive.InMemoryReactiveOneTimeTokenService;
import org.springframework.security.authentication.ott.reactive.OneTimeTokenReactiveAuthenticationManager;
@@ -111,9 +109,9 @@ import org.springframework.security.oauth2.jwt.ReactiveJwtDecoderFactory;
import org.springframework.security.oauth2.server.resource.authentication.JwtReactiveAuthenticationManager;
import org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenReactiveAuthenticationManager;
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.introspection.NimbusReactiveOpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.introspection.SpringReactiveOpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.web.access.server.BearerTokenServerAccessDeniedHandler;
import org.springframework.security.oauth2.server.resource.web.server.BearerTokenServerAuthenticationEntryPoint;
import org.springframework.security.oauth2.server.resource.web.server.authentication.ServerBearerTokenAuthenticationConverter;
@@ -158,9 +156,7 @@ import org.springframework.security.web.server.authentication.logout.LogoutWebFi
import org.springframework.security.web.server.authentication.logout.SecurityContextServerLogoutHandler;
import org.springframework.security.web.server.authentication.logout.ServerLogoutHandler;
import org.springframework.security.web.server.authentication.logout.ServerLogoutSuccessHandler;
import org.springframework.security.web.server.authentication.ott.DefaultServerGenerateOneTimeTokenRequestResolver;
import org.springframework.security.web.server.authentication.ott.GenerateOneTimeTokenWebFilter;
import org.springframework.security.web.server.authentication.ott.ServerGenerateOneTimeTokenRequestResolver;
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenAuthenticationConverter;
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler;
import org.springframework.security.web.server.authorization.AuthorizationContext;
@@ -2736,7 +2732,7 @@ public class ServerHttpSecurity {
ServerHttpSecurity.this.defaultEntryPoints.add(new DelegateEntry(preferredMatcher, this.entryPoint));
AuthenticationWebFilter authenticationFilter = new AuthenticationWebFilter(this.authenticationManager);
authenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler());
authenticationFilter.setServerAuthenticationConverter(new ServerHttpBasicAuthenticationConverter());
authenticationFilter.setAuthenticationConverter(new ServerHttpBasicAuthenticationConverter());
authenticationFilter.setSecurityContextRepository(this.securityContextRepository);
authenticationFilter.setAuthenticationSuccessHandler(getAuthenticationSuccessHandler(http));
http.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.HTTP_BASIC);
@@ -3014,7 +3010,7 @@ public class ServerHttpSecurity {
AuthenticationWebFilter authenticationFilter = new AuthenticationWebFilter(this.authenticationManager);
authenticationFilter.setRequiresAuthenticationMatcher(this.requiresAuthenticationMatcher);
authenticationFilter.setAuthenticationFailureHandler(this.authenticationFailureHandler);
authenticationFilter.setServerAuthenticationConverter(new ServerFormLoginAuthenticationConverter());
authenticationFilter.setAuthenticationConverter(new ServerFormLoginAuthenticationConverter());
authenticationFilter.setAuthenticationSuccessHandler(getAuthenticationSuccessHandler(http));
authenticationFilter.setSecurityContextRepository(this.securityContextRepository);
http.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.FORM_LOGIN);
@@ -3039,8 +3035,7 @@ public class ServerHttpSecurity {
return;
}
if (http.formLogin != null && http.formLogin.isEntryPointExplicit
|| http.oauth2Login != null && StringUtils.hasText(http.oauth2Login.loginPage)
|| http.oneTimeTokenLogin != null && StringUtils.hasText(http.oneTimeTokenLogin.loginPage)) {
|| http.oauth2Login != null && StringUtils.hasText(http.oauth2Login.loginPage)) {
return;
}
LoginPageGeneratingWebFilter loginPage = null;
@@ -3055,13 +3050,6 @@ public class ServerHttpSecurity {
}
loginPage.setOauth2AuthenticationUrlToClientName(urlToText);
}
if (http.oneTimeTokenLogin != null) {
if (loginPage == null) {
loginPage = new LoginPageGeneratingWebFilter();
}
loginPage.setOneTimeTokenEnabled(true);
loginPage.setGenerateOneTimeTokenUrl(http.oneTimeTokenLogin.tokenGeneratingUrl);
}
if (loginPage != null) {
http.addFilterAt(loginPage, SecurityWebFiltersOrder.LOGIN_PAGE_GENERATING);
http.addFilterBefore(DefaultResourcesWebFilter.css(), SecurityWebFiltersOrder.LOGIN_PAGE_GENERATING);
@@ -5443,7 +5431,7 @@ public class ServerHttpSecurity {
public OpaqueTokenSpec introspectionUri(String introspectionUri) {
Assert.hasText(introspectionUri, "introspectionUri cannot be empty");
this.introspectionUri = introspectionUri;
this.introspector = () -> new SpringReactiveOpaqueTokenIntrospector(this.introspectionUri,
this.introspector = () -> new NimbusReactiveOpaqueTokenIntrospector(this.introspectionUri,
this.clientId, this.clientSecret);
return this;
}
@@ -5459,7 +5447,7 @@ public class ServerHttpSecurity {
Assert.notNull(clientSecret, "clientSecret cannot be null");
this.clientId = clientId;
this.clientSecret = clientSecret;
this.introspector = () -> new SpringReactiveOpaqueTokenIntrospector(this.introspectionUri,
this.introspector = () -> new NimbusReactiveOpaqueTokenIntrospector(this.introspectionUri,
this.clientId, this.clientSecret);
return this;
}
@@ -5952,8 +5940,6 @@ public class ServerHttpSecurity {
private ServerSecurityContextRepository securityContextRepository;
private ServerGenerateOneTimeTokenRequestResolver requestResolver;
private String loginProcessingUrl = "/login/ott";
private String defaultSubmitPageUrl = "/login/ott";
@@ -5962,13 +5948,11 @@ public class ServerHttpSecurity {
private boolean submitPageEnabled = true;
private String loginPage;
protected void configure(ServerHttpSecurity http) {
configureSubmitPage(http);
configureOttGenerateFilter(http);
configureOttAuthenticationFilter(http);
configureDefaultEntryPoint(http);
configureDefaultLoginPage(http);
}
private void configureOttAuthenticationFilter(ServerHttpSecurity http) {
@@ -6001,33 +5985,20 @@ public class ServerHttpSecurity {
getTokenGenerationSuccessHandler());
generateFilter
.setRequestMatcher(ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, this.tokenGeneratingUrl));
generateFilter.setGenerateRequestResolver(getRequestResolver());
http.addFilterAt(generateFilter, SecurityWebFiltersOrder.ONE_TIME_TOKEN);
}
private void configureDefaultEntryPoint(ServerHttpSecurity http) {
MediaTypeServerWebExchangeMatcher htmlMatcher = new MediaTypeServerWebExchangeMatcher(
MediaType.APPLICATION_XHTML_XML, new MediaType("image", "*"), MediaType.TEXT_HTML,
MediaType.TEXT_PLAIN);
htmlMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
ServerWebExchangeMatcher xhrMatcher = (exchange) -> {
if (exchange.getRequest().getHeaders().getOrEmpty("X-Requested-With").contains("XMLHttpRequest")) {
return ServerWebExchangeMatcher.MatchResult.match();
private void configureDefaultLoginPage(ServerHttpSecurity http) {
if (http.formLogin != null) {
for (WebFilter webFilter : http.webFilters) {
OrderedWebFilter orderedWebFilter = (OrderedWebFilter) webFilter;
if (orderedWebFilter.webFilter instanceof LoginPageGeneratingWebFilter loginPageGeneratingFilter) {
loginPageGeneratingFilter.setOneTimeTokenEnabled(true);
loginPageGeneratingFilter.setGenerateOneTimeTokenUrl(this.tokenGeneratingUrl);
break;
}
}
return ServerWebExchangeMatcher.MatchResult.notMatch();
};
ServerWebExchangeMatcher notXhrMatcher = new NegatedServerWebExchangeMatcher(xhrMatcher);
ServerWebExchangeMatcher defaultEntryPointMatcher = new AndServerWebExchangeMatcher(notXhrMatcher,
htmlMatcher);
String loginPage = "/login";
if (this.loginPage != null) {
loginPage = this.loginPage;
}
RedirectServerAuthenticationEntryPoint defaultEntryPoint = new RedirectServerAuthenticationEntryPoint(
loginPage);
defaultEntryPoint.setRequestCache(http.requestCache.requestCache);
http.defaultEntryPoints.add(new DelegateEntry(defaultEntryPointMatcher, defaultEntryPoint));
}
/**
@@ -6141,32 +6112,6 @@ public class ServerHttpSecurity {
return this;
}
/**
* Use this {@link ServerGenerateOneTimeTokenRequestResolver} when resolving
* {@link GenerateOneTimeTokenRequest} from {@link ServerWebExchange}. By default,
* the {@link DefaultServerGenerateOneTimeTokenRequestResolver} is used.
* @param requestResolver the
* {@link DefaultServerGenerateOneTimeTokenRequestResolver} to use
* @since 6.5
*/
public OneTimeTokenLoginSpec generateRequestResolver(
ServerGenerateOneTimeTokenRequestResolver requestResolver) {
Assert.notNull(requestResolver, "generateRequestResolver cannot be null");
this.requestResolver = requestResolver;
return this;
}
private ServerGenerateOneTimeTokenRequestResolver getRequestResolver() {
if (this.requestResolver != null) {
return this.requestResolver;
}
ServerGenerateOneTimeTokenRequestResolver bean = getBeanOrNull(
ServerGenerateOneTimeTokenRequestResolver.class);
this.requestResolver = Objects.requireNonNullElseGet(bean,
DefaultServerGenerateOneTimeTokenRequestResolver::new);
return this.requestResolver;
}
/**
* Specifies the URL to process the login request, defaults to {@code /login/ott}.
* Only POST requests are processed, for that reason make sure that you pass a
@@ -6255,19 +6200,6 @@ public class ServerHttpSecurity {
return this.tokenGenerationSuccessHandler;
}
/**
* Specifies the URL to send users to if login is required. A default login page
* will be generated when this attribute is not specified.
* @param loginPage the URL to send users to if login is required
* @return the {@link OAuth2LoginSpec} for further configuration
* @since 6.5
*/
public OneTimeTokenLoginSpec loginPage(String loginPage) {
Assert.hasText(loginPage, "loginPage cannot be empty");
this.loginPage = loginPage;
return this;
}
}
}
@@ -50,7 +50,6 @@ import org.springframework.security.access.vote.ConsensusBased;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.config.Elements;
import org.springframework.security.config.http.MessageMatcherFactoryBean;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
@@ -64,6 +63,7 @@ import org.springframework.security.messaging.access.intercept.MessageMatcherDel
import org.springframework.security.messaging.context.AuthenticationPrincipalArgumentResolver;
import org.springframework.security.messaging.context.SecurityContextChannelInterceptor;
import org.springframework.security.messaging.util.matcher.MessageMatcher;
import org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher;
import org.springframework.security.messaging.util.matcher.SimpMessageTypeMatcher;
import org.springframework.security.messaging.web.csrf.CsrfChannelInterceptor;
import org.springframework.security.messaging.web.socket.server.CsrfTokenHandshakeInterceptor;
@@ -270,18 +270,25 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
matcher.addConstructorArgValue(messageType);
return matcher.getBeanDefinition();
}
BeanDefinitionBuilder matcher = BeanDefinitionBuilder.rootBeanDefinition(MessageMatcherFactoryBean.class);
matcher.addConstructorArgValue(matcherPattern);
if (hasMessageType) {
String factoryName = null;
if (hasPattern && hasMessageType) {
SimpMessageType type = SimpMessageType.valueOf(messageType);
matcher.addConstructorArgValue(type);
if (SimpMessageType.SUBSCRIBE != type && SimpMessageType.MESSAGE != type) {
if (SimpMessageType.MESSAGE == type) {
factoryName = "createMessageMatcher";
}
else if (SimpMessageType.SUBSCRIBE == type) {
factoryName = "createSubscribeMatcher";
}
else {
parserContext.getReaderContext()
.error("Cannot use intercept-websocket@message-type=" + messageType
+ " with a pattern because the type does not have a destination.", interceptMessage);
}
}
matcher.addPropertyValue("pathMatcher", new RuntimeBeanReference("springSecurityMessagePathMatcher"));
BeanDefinitionBuilder matcher = BeanDefinitionBuilder.rootBeanDefinition(SimpDestinationMessageMatcher.class);
matcher.setFactoryMethod(factoryName);
matcher.addConstructorArgValue(matcherPattern);
matcher.addConstructorArgValue(new RuntimeBeanReference("springSecurityMessagePathMatcher"));
return matcher.getBeanDefinition();
}
@@ -275,13 +275,6 @@ class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl {
val authenticated: AuthorizationManager<RequestAuthorizationContext> =
AuthenticatedAuthorizationManager.authenticated()
/**
* Specify that URLs are allowed by users who have authenticated and were not "remembered".
* @since 6.5
*/
val fullyAuthenticated: AuthorizationManager<RequestAuthorizationContext> =
AuthenticatedAuthorizationManager.fullyAuthenticated()
internal fun get(): (AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry) -> Unit {
return { requests ->
authorizationRules.forEach { rule ->
@@ -192,7 +192,7 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
* @Bean
* fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
* http {
* securityMatcher(PathPatternRequestMatcher.withDefaults().matcher("/private/&ast;&ast;"))
* securityMatcher(AntPathRequestMatcher("/private/&ast;&ast;"))
* formLogin {
* loginPage = "/log-in"
* }
@@ -527,47 +527,12 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
* @param requiresChannelConfiguration custom configuration that specifies
* channel security
* @see [RequiresChannelDsl]
* @deprecated please use [redirectToHttps] instead
*/
@Deprecated(message="since 6.5 use redirectToHttps instead")
fun requiresChannel(requiresChannelConfiguration: RequiresChannelDsl.() -> Unit) {
val requiresChannelCustomizer = RequiresChannelDsl().apply(requiresChannelConfiguration).get()
this.http.requiresChannel(requiresChannelCustomizer)
}
/**
* Configures channel security. In order for this configuration to be useful at least
* one mapping to a required channel must be provided.
*
* Example:
*
* The example below demonstrates how to require HTTPS for every request. Only
* requiring HTTPS for some requests is supported, for example if you need to differentiate
* between local and production deployments.
*
* ```
* @Configuration
* @EnableWebSecurity
* class RequireHttpsConfig {
*
* @Bean
* fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
* http {
* redirectToHttps { }
* }
* return http.build();
* }
* }
* ```
* @param httpsRedirectConfiguration custom configuration to apply to HTTPS redirect rules
* @see [HttpsRedirectDsl]
* @since 6.5
*/
fun redirectToHttps(httpsRedirectConfiguration: HttpsRedirectDsl.() -> Unit) {
val httpsRedirectCustomizer = HttpsRedirectDsl().apply(httpsRedirectConfiguration).get()
this.http.redirectToHttps(httpsRedirectCustomizer)
}
/**
* Adds X509 based pre authentication to an application
*
@@ -1,41 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configurers.HttpsRedirectConfigurer
import org.springframework.security.web.PortMapper
import org.springframework.security.web.util.matcher.RequestMatcher
/**
* A Kotlin DSL to configure [ServerHttpSecurity] HTTPS redirection rules using idiomatic
* Kotlin code.
*
* @author Eleftheria Stein
* @since 5.4
* @property portMapper the [PortMapper] that specifies a custom HTTPS port to redirect to.
*/
@SecurityMarker
class HttpsRedirectDsl {
var requestMatchers: Array<out RequestMatcher>? = null
internal fun get(): (HttpsRedirectConfigurer<HttpSecurity>) -> Unit {
return { https ->
requestMatchers?.also { https.requestMatchers(*requestMatchers!!) }
}
}
}
@@ -23,7 +23,6 @@ import org.springframework.security.config.annotation.web.configurers.ott.OneTim
import org.springframework.security.web.authentication.AuthenticationConverter
import org.springframework.security.web.authentication.AuthenticationFailureHandler
import org.springframework.security.web.authentication.AuthenticationSuccessHandler
import org.springframework.security.web.authentication.ott.GenerateOneTimeTokenRequestResolver
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler
/**
@@ -35,7 +34,6 @@ import org.springframework.security.web.authentication.ott.OneTimeTokenGeneratio
* @property authenticationConverter Use this [AuthenticationConverter] when converting incoming requests to an authentication
* @property authenticationFailureHandler the [AuthenticationFailureHandler] to use when authentication
* @property authenticationSuccessHandler the [AuthenticationSuccessHandler] to be used
* @property generateRequestResolver the [GenerateOneTimeTokenRequestResolver] to be used
* @property defaultSubmitPageUrl sets the URL that the default submit page will be generated
* @property showDefaultSubmitPage configures whether the default one-time token submit page should be shown
* @property loginProcessingUrl the URL to process the login request
@@ -49,7 +47,6 @@ class OneTimeTokenLoginDsl {
var authenticationConverter: AuthenticationConverter? = null
var authenticationFailureHandler: AuthenticationFailureHandler? = null
var authenticationSuccessHandler: AuthenticationSuccessHandler? = null
var generateRequestResolver: GenerateOneTimeTokenRequestResolver? = null
var defaultSubmitPageUrl: String? = null
var loginProcessingUrl: String? = null
var tokenGeneratingUrl: String? = null
@@ -62,20 +59,15 @@ class OneTimeTokenLoginDsl {
tokenService?.also { oneTimeTokenLoginConfigurer.tokenService(tokenService) }
authenticationConverter?.also { oneTimeTokenLoginConfigurer.authenticationConverter(authenticationConverter) }
authenticationFailureHandler?.also {
oneTimeTokenLoginConfigurer.failureHandler(
oneTimeTokenLoginConfigurer.authenticationFailureHandler(
authenticationFailureHandler
)
}
authenticationSuccessHandler?.also {
oneTimeTokenLoginConfigurer.successHandler(
oneTimeTokenLoginConfigurer.authenticationSuccessHandler(
authenticationSuccessHandler
)
}
generateRequestResolver?.also {
oneTimeTokenLoginConfigurer.generateRequestResolver(
generateRequestResolver
)
}
defaultSubmitPageUrl?.also { oneTimeTokenLoginConfigurer.defaultSubmitPageUrl(defaultSubmitPageUrl) }
showDefaultSubmitPage?.also { oneTimeTokenLoginConfigurer.showDefaultSubmitPage(showDefaultSubmitPage!!) }
loginProcessingUrl?.also { oneTimeTokenLoginConfigurer.loginProcessingUrl(loginProcessingUrl) }
@@ -35,7 +35,6 @@ import org.springframework.web.servlet.handler.HandlerMappingIntrospector
* @property channelProcessors the [ChannelProcessor] instances to use in
* [ChannelDecisionManagerImpl]
*/
@Deprecated(message="since 6.5 use redirectToHttps instead")
class RequiresChannelDsl : AbstractRequestMatcherDsl() {
private val channelSecurityRules = mutableListOf<AuthorizationRule>()
@@ -16,16 +16,14 @@
package org.springframework.security.config.annotation.web
import org.springframework.http.converter.HttpMessageConverter
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configurers.WebAuthnConfigurer
import org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsRepository
/**
* A Kotlin DSL to configure [HttpSecurity] webauthn using idiomatic Kotlin code.
* @property rpName the relying party name
* @property rpId the relying party id
* @property allowedOrigins allowed origins
* @property the allowed origins
* @property disableDefaultRegistrationPage disable default webauthn registration page
* @since 6.4
* @author Rob Winch
@@ -37,8 +35,6 @@ class WebAuthnDsl {
var rpId: String? = null
var allowedOrigins: Set<String>? = null
var disableDefaultRegistrationPage: Boolean? = false
var creationOptionsRepository: PublicKeyCredentialCreationOptionsRepository? = null
var messageConverter: HttpMessageConverter<Any>? = null
internal fun get(): (WebAuthnConfigurer<HttpSecurity>) -> Unit {
return { webAuthn ->
@@ -46,8 +42,6 @@ class WebAuthnDsl {
rpId?.also { webAuthn.rpId(rpId) }
allowedOrigins?.also { webAuthn.allowedOrigins(allowedOrigins) }
disableDefaultRegistrationPage?.also { webAuthn.disableDefaultRegistrationPage(disableDefaultRegistrationPage!!) }
creationOptionsRepository?.also { webAuthn.creationOptionsRepository(creationOptionsRepository) }
messageConverter?.also { webAuthn.messageConverter(messageConverter) }
}
}
}
@@ -19,9 +19,7 @@ package org.springframework.security.config.annotation.web.session
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer
import org.springframework.security.core.session.SessionRegistry
import org.springframework.security.web.authentication.session.SessionLimit
import org.springframework.security.web.session.SessionInformationExpiredStrategy
import org.springframework.util.Assert
/**
* A Kotlin DSL to configure the behaviour of multiple sessions using idiomatic
@@ -46,21 +44,12 @@ class SessionConcurrencyDsl {
var expiredSessionStrategy: SessionInformationExpiredStrategy? = null
var maxSessionsPreventsLogin: Boolean? = null
var sessionRegistry: SessionRegistry? = null
private var sessionLimit: SessionLimit? = null
fun maximumSessions(max: SessionLimit) {
this.sessionLimit = max
}
internal fun get(): (SessionManagementConfigurer<HttpSecurity>.ConcurrencyControlConfigurer) -> Unit {
Assert.isTrue(maximumSessions == null || sessionLimit == null, "You cannot specify maximumSessions as both an Int and a SessionLimit. Please use only one.")
return { sessionConcurrencyControl ->
maximumSessions?.also {
sessionConcurrencyControl.maximumSessions(maximumSessions!!)
}
sessionLimit?.also {
sessionConcurrencyControl.maximumSessions(sessionLimit!!)
}
expiredUrl?.also {
sessionConcurrencyControl.expiredUrl(expiredUrl)
}
@@ -17,7 +17,6 @@
package org.springframework.security.config.web.server
import org.springframework.security.web.server.header.CacheControlServerHttpHeadersWriter
import org.springframework.security.web.server.header.ServerHttpHeadersWriter
import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter
import org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter
import org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter
@@ -44,7 +43,6 @@ class ServerHeadersDsl {
private var crossOriginOpenerPolicy: ((ServerHttpSecurity.HeaderSpec.CrossOriginOpenerPolicySpec) -> Unit)? = null
private var crossOriginEmbedderPolicy: ((ServerHttpSecurity.HeaderSpec.CrossOriginEmbedderPolicySpec) -> Unit)? = null
private var crossOriginResourcePolicy: ((ServerHttpSecurity.HeaderSpec.CrossOriginResourcePolicySpec) -> Unit)? = null
private var writers = mutableListOf<ServerHttpHeadersWriter>()
private var disabled = false
@@ -200,16 +198,6 @@ class ServerHeadersDsl {
this.crossOriginResourcePolicy = ServerCrossOriginResourcePolicyDsl().apply(crossOriginResourcePolicyConfig).get()
}
/**
* Configures custom headers writer
*
* @since 6.5
* @param writer the [ServerHttpHeadersWriter] to provide custom headers writer
*/
fun writer(writer: ServerHttpHeadersWriter) {
this.writers.add(writer)
}
/**
* Disables HTTP response headers.
*/
@@ -256,9 +244,6 @@ class ServerHeadersDsl {
crossOriginResourcePolicy?.also {
headers.crossOriginResourcePolicy(crossOriginResourcePolicy)
}
writers.also {
writers.forEach { writer -> headers.writer(writer) }
}
if (disabled) {
headers.disable()
}

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