1
0
mirror of synced 2026-07-13 22:55:10 +00:00

Compare commits

..

114 Commits

Author SHA1 Message Date
github-actions[bot] 3512fc76f3 Release 6.5.2 2025-07-21 18:34:18 +00:00
Rob Winch 90584ef8ce Merge branch '6.4.x' into 6.5.x 2025-07-21 09:42:59 -05:00
Rob Winch 7b606362f8 Fix samples branch 2025-07-21 09:42:51 -05:00
Rob Winch e38c059e7c Fix samples branch 2025-07-21 09:42:08 -05:00
Rob Winch 80ccb9b3cf Merge branch '6.4.x' into 6.5.x
Closes gh-17580
2025-07-21 09:29:20 -05:00
Rob Winch 829af961f0 Use Meaningful Configurer Names in Test
This just renames the Configurer names used in
AbstractConfiguredSecurityBuilderTests to be more meaningful.

Issue gh-17020 gh-17011

Signed-off-by: Rob Winch <362503+rwinch@users.noreply.github.com>
2025-07-21 09:27:36 -05:00
Rob Winch fca704e61f Fix getConfigurersInInitializing Semantics
A getter should not mutate state. This removes getConfigurersInInitializing
in favor of inline code since this is just used once.

Issue gh-17020 gh-17011

Signed-off-by: Rob Winch <362503+rwinch@users.noreply.github.com>
2025-07-21 09:27:36 -05:00
Rob Winch ea9dd2728e Support add nested security configurers during builder initialization
Closes gh-17011

Signed-off-by: DingHao <dh.hiekn@gmail.com>
2025-07-21 09:27:27 -05:00
Rob Winch 409f845db2 Merge branch '6.4.x' into 6.5.x 2025-07-21 08:20:35 -05:00
Rob Winch 258aa66f0d Merge branch '6.3.x' into 6.4.x 2025-07-21 08:20:16 -05:00
Rob Winch fbaa15bae1 Bump org.springframework:spring-framework-bom from 6.2.8 to 6.2.9 2025-07-21 08:18:52 -05:00
Rob Winch e21a80a96d Bump org.springframework.data:spring-data-bom from 2024.1.7 to 2024.1.8 2025-07-21 08:18:49 -05:00
Rob Winch 236ef46cf8 Bump io.micrometer:micrometer-observation from 1.14.8 to 1.14.9 2025-07-21 08:18:47 -05:00
Rob Winch a0b0d02965 Bump org.hibernate.orm:hibernate-core from 6.6.20.Final to 6.6.22.Final 2025-07-21 08:18:45 -05:00
Rob Winch 97e5b103e8 Bump io.mockk:mockk from 1.14.4 to 1.14.5 2025-07-21 08:18:42 -05:00
Rob Winch d5d31a0892 Bump org.apache.maven:maven-resolver-provider from 3.9.10 to 3.9.11 2025-07-21 08:11:46 -05:00
Rob Winch 1b3f8435ce Bump org.hibernate.orm:hibernate-core from 6.6.20.Final to 6.6.22.Final 2025-07-21 08:11:44 -05:00
Rob Winch 22fbbb9365 Bump io.micrometer:micrometer-observation from 1.14.8 to 1.14.9 2025-07-21 08:11:41 -05:00
Rob Winch f1622351e5 Bump org.springframework.data:spring-data-bom from 2024.1.7 to 2024.1.8 2025-07-21 08:11:39 -05:00
Rob Winch e5c45c9dda Bump org.springframework:spring-framework-bom from 6.2.8 to 6.2.9 2025-07-21 08:11:37 -05:00
Rob Winch 82fa658c9b Bump org.apache.maven:maven-resolver-provider from 3.9.10 to 3.9.11 2025-07-21 07:52:56 -05:00
dependabot[bot] 51a703a291 Bump org.springframework:spring-framework-bom from 6.2.8 to 6.2.9
Bumps [org.springframework:spring-framework-bom](https://github.com/spring-projects/spring-framework) from 6.2.8 to 6.2.9.
- [Release notes](https://github.com/spring-projects/spring-framework/releases)
- [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-21 04:43:53 +00:00
dependabot[bot] ae689cd220 Bump org.springframework.data:spring-data-bom from 2024.1.7 to 2024.1.8
Bumps [org.springframework.data:spring-data-bom](https://github.com/spring-projects/spring-data-bom) from 2024.1.7 to 2024.1.8.
- [Release notes](https://github.com/spring-projects/spring-data-bom/releases)
- [Commits](https://github.com/spring-projects/spring-data-bom/compare/2024.1.7...2024.1.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-21 04:41:51 +00:00
dependabot[bot] 2a3d1340d1 Bump io.micrometer:micrometer-observation from 1.14.8 to 1.14.9
Bumps [io.micrometer:micrometer-observation](https://github.com/micrometer-metrics/micrometer) from 1.14.8 to 1.14.9.
- [Release notes](https://github.com/micrometer-metrics/micrometer/releases)
- [Commits](https://github.com/micrometer-metrics/micrometer/compare/v1.14.8...v1.14.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-21 04:40:58 +00:00
dependabot[bot] 9f40a72f19 Bump org.hibernate.orm:hibernate-core from 6.6.20.Final to 6.6.22.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.20.Final to 6.6.22.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.22/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.20...6.6.22)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-21 04:36:16 +00:00
dependabot[bot] 470384597c Bump org.apache.maven:maven-resolver-provider from 3.9.10 to 3.9.11
Bumps org.apache.maven:maven-resolver-provider from 3.9.10 to 3.9.11.

---
updated-dependencies:
- dependency-name: org.apache.maven:maven-resolver-provider
  dependency-version: 3.9.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-21 04:36:08 +00:00
dependabot[bot] c27f7206d9 Bump org.apache.maven:maven-resolver-provider from 3.9.10 to 3.9.11
Bumps org.apache.maven:maven-resolver-provider from 3.9.10 to 3.9.11.

---
updated-dependencies:
- dependency-name: org.apache.maven:maven-resolver-provider
  dependency-version: 3.9.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-21 04:23:35 +00:00
dependabot[bot] a5129a136d Bump io.mockk:mockk from 1.14.4 to 1.14.5
---
updated-dependencies:
- dependency-name: io.mockk:mockk
  dependency-version: 1.14.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-21 04:20:57 +00:00
dependabot[bot] 150615eba5 Bump org.hibernate.orm:hibernate-core from 6.6.20.Final to 6.6.22.Final
---
updated-dependencies:
- dependency-name: org.hibernate.orm:hibernate-core
  dependency-version: 6.6.22.Final
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-21 04:18:57 +00:00
dependabot[bot] fb07dc9d3a Bump io.micrometer:micrometer-observation from 1.14.8 to 1.14.9
---
updated-dependencies:
- dependency-name: io.micrometer:micrometer-observation
  dependency-version: 1.14.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-21 04:18:31 +00:00
dependabot[bot] 32f909ff64 Bump org.springframework.data:spring-data-bom from 2024.1.7 to 2024.1.8
---
updated-dependencies:
- dependency-name: org.springframework.data:spring-data-bom
  dependency-version: 2024.1.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-21 04:15:46 +00:00
dependabot[bot] 2aebb9ce9e Bump org.springframework:spring-framework-bom from 6.2.8 to 6.2.9
---
updated-dependencies:
- dependency-name: org.springframework:spring-framework-bom
  dependency-version: 6.2.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-21 04:13:19 +00:00
Rob Winch 3db0b0ed95 Merge branch '6.4.x' into 6.5.x 2025-07-18 09:14:48 -05:00
Rob Winch 2cb200079c Merge branch '6.3.x' into 6.4.x 2025-07-18 09:14:35 -05:00
Rob Winch cb515e5c57 Merge branch 'npm_and_yarn/docs/6.3.x/springio/antora-extensions-1.14.6' into 6.3.x 2025-07-18 09:14:23 -05:00
Rob Winch 02dbb288d3 Merge branch '6.4.x' into 6.5.x 2025-07-18 09:12:01 -05:00
Rob Winch b56533c13c Merge branch '6.3.x' into 6.4.x 2025-07-18 09:11:45 -05:00
Rob Winch a3df90405f Bump org.hibernate.orm:hibernate-core from 6.6.20.Final to 6.6.21.Final 2025-07-18 09:10:38 -05:00
Rob Winch dd8763a572 Bump org.springframework:spring-framework-bom from 6.2.8 to 6.2.9 2025-07-18 09:10:36 -05:00
Rob Winch cf9375f441 Bump io.micrometer:micrometer-observation from 1.14.8 to 1.14.9 2025-07-18 09:10:34 -05:00
Rob Winch bf5496310e Bump org.apache.maven:maven-resolver-provider from 3.9.10 to 3.9.11 2025-07-18 09:10:32 -05:00
Rob Winch f2452c3732 Bump io.mockk:mockk from 1.14.4 to 1.14.5 2025-07-18 09:10:30 -05:00
Rob Winch b63a337015 Bump io.micrometer:micrometer-observation from 1.14.8 to 1.14.9 2025-07-18 09:09:33 -05:00
Rob Winch 880ba0e1aa Bump org.springframework:spring-framework-bom from 6.2.8 to 6.2.9 2025-07-18 09:09:31 -05:00
Rob Winch 9262a8ecc8 Bump org.hibernate.orm:hibernate-core from 6.6.20.Final to 6.6.21.Final 2025-07-18 09:09:29 -05:00
Rob Winch 4084b70335 Bump org.apache.maven:maven-resolver-provider from 3.9.10 to 3.9.11 2025-07-18 09:09:28 -05:00
Rob Winch 59365e9ac5 Bump org.apache.maven:maven-resolver-provider from 3.9.10 to 3.9.11 2025-07-18 09:05:22 -05:00
Joe Grandja ecec7cb98f Merge branch '6.4.x' into 6.5.x
Closes gh-17557
2025-07-18 08:40:31 -04:00
Marcus Hert da Coregio 2a38de48b8 Fix securityContextRepository() initialization in oauth2Login() DSL
Closes gh-17502

Signed-off-by: Marcus Hert da Coregio <marcusdacoregio@gmail.com>
2025-07-18 07:48:05 -04:00
dependabot[bot] 382f2788d3 Bump io.mockk:mockk from 1.14.4 to 1.14.5
Bumps [io.mockk:mockk](https://github.com/mockk/mockk) from 1.14.4 to 1.14.5.
- [Release notes](https://github.com/mockk/mockk/releases)
- [Commits](https://github.com/mockk/mockk/compare/1.14.4...1.14.5)

---
updated-dependencies:
- dependency-name: io.mockk:mockk
  dependency-version: 1.14.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-18 03:44:04 +00:00
dependabot[bot] 385d10ebf1 Bump org.apache.maven:maven-resolver-provider from 3.9.10 to 3.9.11
Bumps org.apache.maven:maven-resolver-provider from 3.9.10 to 3.9.11.

---
updated-dependencies:
- dependency-name: org.apache.maven:maven-resolver-provider
  dependency-version: 3.9.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-18 03:43:59 +00:00
dependabot[bot] 5f243de06b Bump io.micrometer:micrometer-observation from 1.14.8 to 1.14.9
Bumps [io.micrometer:micrometer-observation](https://github.com/micrometer-metrics/micrometer) from 1.14.8 to 1.14.9.
- [Release notes](https://github.com/micrometer-metrics/micrometer/releases)
- [Commits](https://github.com/micrometer-metrics/micrometer/compare/v1.14.8...v1.14.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-18 03:43:48 +00:00
dependabot[bot] 6716a0c4fe Bump org.springframework:spring-framework-bom from 6.2.8 to 6.2.9
Bumps [org.springframework:spring-framework-bom](https://github.com/spring-projects/spring-framework) from 6.2.8 to 6.2.9.
- [Release notes](https://github.com/spring-projects/spring-framework/releases)
- [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-18 03:43:25 +00:00
dependabot[bot] c1173dbab1 Bump org.hibernate.orm:hibernate-core from 6.6.20.Final to 6.6.21.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.20.Final to 6.6.21.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.21/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.20...6.6.21)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-18 03:43:04 +00:00
dependabot[bot] 284a32c698 Bump org.apache.maven:maven-resolver-provider from 3.9.10 to 3.9.11
---
updated-dependencies:
- dependency-name: org.apache.maven:maven-resolver-provider
  dependency-version: 3.9.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-18 03:34:19 +00:00
dependabot[bot] 3d028b1c22 Bump org.hibernate.orm:hibernate-core from 6.6.20.Final to 6.6.21.Final
---
updated-dependencies:
- dependency-name: org.hibernate.orm:hibernate-core
  dependency-version: 6.6.21.Final
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-18 03:34:02 +00:00
dependabot[bot] 126166da1e Bump org.springframework:spring-framework-bom from 6.2.8 to 6.2.9
---
updated-dependencies:
- dependency-name: org.springframework:spring-framework-bom
  dependency-version: 6.2.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-18 03:33:55 +00:00
dependabot[bot] f4387f299a Bump io.micrometer:micrometer-observation from 1.14.8 to 1.14.9
---
updated-dependencies:
- dependency-name: io.micrometer:micrometer-observation
  dependency-version: 1.14.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-18 03:33:52 +00:00
dependabot[bot] 854703cb47 Bump org.apache.maven:maven-resolver-provider from 3.9.10 to 3.9.11
---
updated-dependencies:
- dependency-name: org.apache.maven:maven-resolver-provider
  dependency-version: 3.9.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-18 03:15:01 +00:00
Josh Cummings 72eb3065de Remove AuthorizationWebProxyConfiguration From Reactive
Closes gh-17545
2025-07-17 17:42:45 -06:00
Rob Winch d2fcba82f7 Merge branch '6.4.x' into 6.5.x 2025-07-17 13:03:06 -05:00
Rob Winch b81d894cc8 Merge branch '6.3.x' into 6.4.x 2025-07-17 13:02:57 -05:00
Rob Winch ca108fe5dc Bump io.micrometer:micrometer-observation from 1.14.8 to 1.14.9 2025-07-17 13:00:47 -05:00
Rob Winch 5ddbe18cb0 Bump org.hibernate.orm:hibernate-core from 6.6.20.Final to 6.6.21.Final 2025-07-17 13:00:44 -05:00
Rob Winch ea8aad7865 Bump org.apache.maven:maven-resolver-provider from 3.9.10 to 3.9.11 2025-07-17 13:00:41 -05:00
Rob Winch 8e563d6324 Bump io.mockk:mockk from 1.14.4 to 1.14.5 2025-07-17 13:00:39 -05:00
Rob Winch b2f4bf9b74 Bump io.micrometer:micrometer-observation from 1.14.8 to 1.14.9 2025-07-17 12:59:55 -05:00
Rob Winch ac7f6260b1 Bump org.hibernate.orm:hibernate-core from 6.6.20.Final to 6.6.21.Final 2025-07-17 12:59:52 -05:00
Rob Winch ebca0df783 Bump org.apache.maven:maven-resolver-provider from 3.9.10 to 3.9.11 2025-07-17 12:59:50 -05:00
Rob Winch 3395e061f0 Bump org.apache.maven:maven-resolver-provider from 3.9.10 to 3.9.11 2025-07-17 12:58:50 -05:00
dependabot[bot] bc746e260f Bump io.mockk:mockk from 1.14.4 to 1.14.5
Bumps [io.mockk:mockk](https://github.com/mockk/mockk) from 1.14.4 to 1.14.5.
- [Release notes](https://github.com/mockk/mockk/releases)
- [Commits](https://github.com/mockk/mockk/compare/1.14.4...1.14.5)

---
updated-dependencies:
- dependency-name: io.mockk:mockk
  dependency-version: 1.14.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-17 04:01:29 +00:00
dependabot[bot] e2f96480a4 Bump org.apache.maven:maven-resolver-provider from 3.9.10 to 3.9.11
---
updated-dependencies:
- dependency-name: org.apache.maven:maven-resolver-provider
  dependency-version: 3.9.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-16 04:00:56 +00:00
dependabot[bot] ba17c3182b Bump org.apache.maven:maven-resolver-provider from 3.9.10 to 3.9.11
Bumps org.apache.maven:maven-resolver-provider from 3.9.10 to 3.9.11.

---
updated-dependencies:
- dependency-name: org.apache.maven:maven-resolver-provider
  dependency-version: 3.9.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-16 03:51:21 +00:00
dependabot[bot] 92116773ea Bump org.apache.maven:maven-resolver-provider from 3.9.10 to 3.9.11
---
updated-dependencies:
- dependency-name: org.apache.maven:maven-resolver-provider
  dependency-version: 3.9.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-16 03:41:55 +00:00
dependabot[bot] 9377a9aad4 Bump org.hibernate.orm:hibernate-core from 6.6.20.Final to 6.6.21.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.20.Final to 6.6.21.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.21/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.20...6.6.21)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-14 04:25:28 +00:00
dependabot[bot] 1e03085a12 Bump org.hibernate.orm:hibernate-core from 6.6.20.Final to 6.6.21.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.20.Final to 6.6.21.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.21/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.20...6.6.21)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-14 04:21:28 +00:00
dependabot[bot] 5d765f92b6 Bump io.micrometer:micrometer-observation from 1.14.8 to 1.14.9
Bumps [io.micrometer:micrometer-observation](https://github.com/micrometer-metrics/micrometer) from 1.14.8 to 1.14.9.
- [Release notes](https://github.com/micrometer-metrics/micrometer/releases)
- [Commits](https://github.com/micrometer-metrics/micrometer/compare/v1.14.8...v1.14.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-14 04:20:00 +00:00
dependabot[bot] c92187a9a7 Bump io.micrometer:micrometer-observation from 1.14.8 to 1.14.9
Bumps [io.micrometer:micrometer-observation](https://github.com/micrometer-metrics/micrometer) from 1.14.8 to 1.14.9.
- [Release notes](https://github.com/micrometer-metrics/micrometer/releases)
- [Commits](https://github.com/micrometer-metrics/micrometer/compare/v1.14.8...v1.14.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-14 04:18:51 +00:00
dependabot[bot] 759736672d Bump @springio/antora-extensions from 1.14.4 to 1.14.6 in /docs
Bumps [@springio/antora-extensions](https://github.com/spring-io/antora-extensions) from 1.14.4 to 1.14.6.
- [Changelog](https://github.com/spring-io/antora-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/antora-extensions/compare/v1.14.4...v1.14.6)

---
updated-dependencies:
- dependency-name: "@springio/antora-extensions"
  dependency-version: 1.14.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-14 01:05:46 +00:00
Josh Cummings 7960d2803d Add Migration Steps for PathMatcher Usage
Issue gh-17509
2025-07-10 14:53:39 -06:00
Josh Cummings 4b15b2b94e Add Migration Steps for Messaging
Closes gh-17509
2025-07-10 13:19:42 -06:00
Josh Cummings bc0d706275 Use PathPatternMessageMatcher.Builder in XML Config
Closes gh-17508
2025-07-10 13:16:14 -06:00
Josh Cummings 9209a33678 Remove References to Deprecated OpenSaml Components
Issue gh-11658
2025-07-09 14:10:33 -06:00
Rob Winch 7a7d2cacd2 Merge branch '6.4.x' into 6.5.x 2025-07-07 14:31:17 -05:00
dependabot[bot] fa2e4c67a0 Bump org.springframework:spring-framework-bom from 6.2.7 to 6.2.8
Bumps [org.springframework:spring-framework-bom](https://github.com/spring-projects/spring-framework) from 6.2.7 to 6.2.8.
- [Release notes](https://github.com/spring-projects/spring-framework/releases)
- [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-07 14:31:11 -05:00
dependabot[bot] 45ce51fe8d Bump com.webauthn4j:webauthn4j-core
Bumps [com.webauthn4j:webauthn4j-core](https://github.com/webauthn4j/webauthn4j) from 0.29.3.RELEASE to 0.29.4.RELEASE.
- [Release notes](https://github.com/webauthn4j/webauthn4j/releases)
- [Changelog](https://github.com/webauthn4j/webauthn4j/blob/master/github-release-notes-generator.yml)
- [Commits](https://github.com/webauthn4j/webauthn4j/compare/0.29.3.RELEASE...0.29.4.RELEASE)

---
updated-dependencies:
- dependency-name: com.webauthn4j:webauthn4j-core
  dependency-version: 0.29.4.RELEASE
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-07 14:31:11 -05:00
dependabot[bot] 2d3a1dc210 Bump org.hibernate.orm:hibernate-core from 6.6.17.Final to 6.6.20.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.17.Final to 6.6.20.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.20/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.17...6.6.20)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-07 14:31:10 -05:00
dependabot[bot] 64bc37b5d6 Bump org.springframework.data:spring-data-bom from 2024.1.6 to 2024.1.7
---
updated-dependencies:
- dependency-name: org.springframework.data:spring-data-bom
  dependency-version: 2024.1.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-07 14:30:49 -05:00
dependabot[bot] afc25b1ea3 Bump org.hibernate.orm:hibernate-core from 6.6.19.Final to 6.6.20.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.19.Final to 6.6.20.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.20/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.19...6.6.20)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-07 14:30:49 -05:00
Josh Cummings bc20bd6340 Merge branch '6.4.x' into 6.5.x
Closes gh-17495
2025-07-07 12:53:59 -06:00
Josh Cummings 8461feb028 Merge branch '6.3.x' into 6.4.x
Closes gh-17494
2025-07-07 12:53:47 -06:00
Josh Cummings 4f5b17334e Pick Up csrfChannelInterceptor in XML
Closes gh-17493
2025-07-07 12:53:27 -06:00
Josh Cummings ea3ba62022 Correct Servlet Path JavaDoc
Initially PathPatternRequestMatcher was designed to match relative
to the servlet path. However, this was changed to be relative to
the context path. This commit updates the documentation and removes
references to the servlet path other than in the context of setting
a basePath to remove boilerplate.

Issue gh-16430
2025-07-07 11:34:29 -06:00
Josh Cummings 8fb8e26c01 Merge branch '6.4.x' into 6.5.x 2025-07-03 14:40:04 -06:00
Josh Cummings 3f4ef169f6 Merge branch '6.3.x' into 6.4.x 2025-07-03 14:38:39 -06:00
dependabot[bot] 192bafe9a5 Bump org.springframework.ldap:spring-ldap-core from 3.2.12 to 3.2.13
Bumps [org.springframework.ldap:spring-ldap-core](https://github.com/spring-projects/spring-ldap) from 3.2.12 to 3.2.13.
- [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.12...3.2.13)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-03 14:38:21 -06:00
Josh Cummings b5ddf9ccd1 Merge branch '6.4.x' into 6.5.x 2025-07-03 14:19:31 -06:00
Josh Cummings 43042b6724 Merge branch '6.3.x' into 6.4.x 2025-07-03 14:19:20 -06:00
dependabot[bot] b144c37cad Bump org.springframework:spring-framework-bom from 6.1.20 to 6.1.21
Bumps [org.springframework:spring-framework-bom](https://github.com/spring-projects/spring-framework) from 6.1.20 to 6.1.21.
- [Release notes](https://github.com/spring-projects/spring-framework/releases)
- [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.20...v6.1.21)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-03 14:17:18 -06:00
dependabot[bot] 22992afbe8 Bump org-apache-maven-resolver from 1.9.23 to 1.9.24
Bumps `org-apache-maven-resolver` from 1.9.23 to 1.9.24.

Updates `org.apache.maven.resolver:maven-resolver-connector-basic` from 1.9.23 to 1.9.24
- [Release notes](https://github.com/apache/maven-resolver/releases)
- [Commits](https://github.com/apache/maven-resolver/compare/maven-resolver-1.9.23...maven-resolver-1.9.24)

Updates `org.apache.maven.resolver:maven-resolver-impl` from 1.9.23 to 1.9.24
- [Release notes](https://github.com/apache/maven-resolver/releases)
- [Commits](https://github.com/apache/maven-resolver/compare/maven-resolver-1.9.23...maven-resolver-1.9.24)

Updates `org.apache.maven.resolver:maven-resolver-transport-http` from 1.9.23 to 1.9.24

---
updated-dependencies:
- dependency-name: org.apache.maven.resolver:maven-resolver-connector-basic
  dependency-version: 1.9.24
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.apache.maven.resolver:maven-resolver-impl
  dependency-version: 1.9.24
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.apache.maven.resolver:maven-resolver-transport-http
  dependency-version: 1.9.24
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-03 14:17:06 -06:00
dependabot[bot] 39b53ee0be Bump org.springframework.data:spring-data-bom
Bumps [org.springframework.data:spring-data-bom](https://github.com/spring-projects/spring-data-bom) from 2024.0.12 to 2024.0.13.
- [Release notes](https://github.com/spring-projects/spring-data-bom/releases)
- [Commits](https://github.com/spring-projects/spring-data-bom/compare/2024.0.12...2024.0.13)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-03 14:16:33 -06:00
dependabot[bot] 23fa7c3047 Bump io-spring-javaformat from 0.0.46 to 0.0.47
Bumps `io-spring-javaformat` from 0.0.46 to 0.0.47.

Updates `io.spring.javaformat:spring-javaformat-checkstyle` from 0.0.46 to 0.0.47
- [Release notes](https://github.com/spring-io/spring-javaformat/releases)
- [Commits](https://github.com/spring-io/spring-javaformat/compare/v0.0.46...v0.0.47)

Updates `io.spring.javaformat:spring-javaformat-gradle-plugin` from 0.0.46 to 0.0.47
- [Release notes](https://github.com/spring-io/spring-javaformat/releases)
- [Commits](https://github.com/spring-io/spring-javaformat/compare/v0.0.46...v0.0.47)

---
updated-dependencies:
- dependency-name: io.spring.javaformat:spring-javaformat-checkstyle
  dependency-version: 0.0.47
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: io.spring.javaformat:spring-javaformat-gradle-plugin
  dependency-version: 0.0.47
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-03 14:16:20 -06:00
Josh Cummings 3bef5f9f40 Merge branch '6.4.x' into 6.5.x 2025-07-03 13:43:10 -06:00
dependabot[bot] 43e2e53b90 Bump org.springframework:spring-framework-bom from 6.2.7 to 6.2.8
Bumps [org.springframework:spring-framework-bom](https://github.com/spring-projects/spring-framework) from 6.2.7 to 6.2.8.
- [Release notes](https://github.com/spring-projects/spring-framework/releases)
- [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-03 13:42:46 -06:00
dependabot[bot] 3fec6959b1 Bump org.springframework.ldap:spring-ldap-core from 3.2.12 to 3.2.13
Bumps [org.springframework.ldap:spring-ldap-core](https://github.com/spring-projects/spring-ldap) from 3.2.12 to 3.2.13.
- [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.12...3.2.13)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-03 13:41:32 -06:00
dependabot[bot] a8f8fcb046 Bump org-apache-maven-resolver from 1.9.23 to 1.9.24
Bumps `org-apache-maven-resolver` from 1.9.23 to 1.9.24.

Updates `org.apache.maven.resolver:maven-resolver-connector-basic` from 1.9.23 to 1.9.24
- [Release notes](https://github.com/apache/maven-resolver/releases)
- [Commits](https://github.com/apache/maven-resolver/compare/maven-resolver-1.9.23...maven-resolver-1.9.24)

Updates `org.apache.maven.resolver:maven-resolver-impl` from 1.9.23 to 1.9.24
- [Release notes](https://github.com/apache/maven-resolver/releases)
- [Commits](https://github.com/apache/maven-resolver/compare/maven-resolver-1.9.23...maven-resolver-1.9.24)

Updates `org.apache.maven.resolver:maven-resolver-transport-http` from 1.9.23 to 1.9.24

---
updated-dependencies:
- dependency-name: org.apache.maven.resolver:maven-resolver-connector-basic
  dependency-version: 1.9.24
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.apache.maven.resolver:maven-resolver-impl
  dependency-version: 1.9.24
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.apache.maven.resolver:maven-resolver-transport-http
  dependency-version: 1.9.24
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-03 13:41:22 -06:00
dependabot[bot] 16956fe01c Bump io-spring-javaformat from 0.0.46 to 0.0.47
Bumps `io-spring-javaformat` from 0.0.46 to 0.0.47.

Updates `io.spring.javaformat:spring-javaformat-checkstyle` from 0.0.46 to 0.0.47
- [Release notes](https://github.com/spring-io/spring-javaformat/releases)
- [Commits](https://github.com/spring-io/spring-javaformat/compare/v0.0.46...v0.0.47)

Updates `io.spring.javaformat:spring-javaformat-gradle-plugin` from 0.0.46 to 0.0.47
- [Release notes](https://github.com/spring-io/spring-javaformat/releases)
- [Commits](https://github.com/spring-io/spring-javaformat/compare/v0.0.46...v0.0.47)

---
updated-dependencies:
- dependency-name: io.spring.javaformat:spring-javaformat-checkstyle
  dependency-version: 0.0.47
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: io.spring.javaformat:spring-javaformat-gradle-plugin
  dependency-version: 0.0.47
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-03 13:41:08 -06:00
dependabot[bot] a66efc26f5 Bump org.hibernate.orm:hibernate-core from 6.6.17.Final to 6.6.19.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.17.Final to 6.6.19.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.19/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.17...6.6.19)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-03 13:40:58 -06:00
dependabot[bot] 86562760d9 Bump org.springframework.data:spring-data-bom from 2024.1.6 to 2024.1.7
Bumps [org.springframework.data:spring-data-bom](https://github.com/spring-projects/spring-data-bom) from 2024.1.6 to 2024.1.7.
- [Release notes](https://github.com/spring-projects/spring-data-bom/releases)
- [Commits](https://github.com/spring-projects/spring-data-bom/compare/2024.1.6...2024.1.7)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-03 13:20:01 -06:00
dependabot[bot] eba43dc03f Bump io.mockk:mockk from 1.14.2 to 1.14.4
Bumps [io.mockk:mockk](https://github.com/mockk/mockk) from 1.14.2 to 1.14.4.
- [Release notes](https://github.com/mockk/mockk/releases)
- [Commits](https://github.com/mockk/mockk/compare/1.14.2...1.14.4)

---
updated-dependencies:
- dependency-name: io.mockk:mockk
  dependency-version: 1.14.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-03 12:51:14 -06:00
dependabot[bot] 0b872588d3 Bump org.springframework.ldap:spring-ldap-core from 3.2.12 to 3.2.13
Bumps [org.springframework.ldap:spring-ldap-core](https://github.com/spring-projects/spring-ldap) from 3.2.12 to 3.2.13.
- [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.12...3.2.13)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-03 12:51:04 -06:00
dependabot[bot] cfaac1963c Bump org-apache-maven-resolver from 1.9.23 to 1.9.24
Bumps `org-apache-maven-resolver` from 1.9.23 to 1.9.24.

Updates `org.apache.maven.resolver:maven-resolver-connector-basic` from 1.9.23 to 1.9.24
- [Release notes](https://github.com/apache/maven-resolver/releases)
- [Commits](https://github.com/apache/maven-resolver/compare/maven-resolver-1.9.23...maven-resolver-1.9.24)

Updates `org.apache.maven.resolver:maven-resolver-impl` from 1.9.23 to 1.9.24
- [Release notes](https://github.com/apache/maven-resolver/releases)
- [Commits](https://github.com/apache/maven-resolver/compare/maven-resolver-1.9.23...maven-resolver-1.9.24)

Updates `org.apache.maven.resolver:maven-resolver-transport-http` from 1.9.23 to 1.9.24

---
updated-dependencies:
- dependency-name: org.apache.maven.resolver:maven-resolver-connector-basic
  dependency-version: 1.9.24
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.apache.maven.resolver:maven-resolver-impl
  dependency-version: 1.9.24
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.apache.maven.resolver:maven-resolver-transport-http
  dependency-version: 1.9.24
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-03 12:50:51 -06:00
dependabot[bot] 8b5a43033f Bump io-spring-javaformat from 0.0.46 to 0.0.47
Bumps `io-spring-javaformat` from 0.0.46 to 0.0.47.

Updates `io.spring.javaformat:spring-javaformat-checkstyle` from 0.0.46 to 0.0.47
- [Release notes](https://github.com/spring-io/spring-javaformat/releases)
- [Commits](https://github.com/spring-io/spring-javaformat/compare/v0.0.46...v0.0.47)

Updates `io.spring.javaformat:spring-javaformat-gradle-plugin` from 0.0.46 to 0.0.47
- [Release notes](https://github.com/spring-io/spring-javaformat/releases)
- [Commits](https://github.com/spring-io/spring-javaformat/compare/v0.0.46...v0.0.47)

---
updated-dependencies:
- dependency-name: io.spring.javaformat:spring-javaformat-checkstyle
  dependency-version: 0.0.47
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: io.spring.javaformat:spring-javaformat-gradle-plugin
  dependency-version: 0.0.47
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-03 12:50:45 -06:00
Andrey Litvitski 25d51a0d99 Include HTTP Method in equals and hashCode
Closes gh-17180

Signed-off-by: Andrey Litvitski <andrey1010102008@gmail.com>
2025-07-02 13:18:36 -06:00
907 changed files with 17359 additions and 16521 deletions
-26
View File
@@ -4,32 +4,6 @@ registries:
type: maven-repository
url: https://repo.spring.io/milestone
updates:
- package-ecosystem: gradle
target-branch: 6.5.x
directory: /
schedule:
interval: daily
time: '03:00'
timezone: Etc/UTC
labels:
- 'type: dependency-upgrade'
registries:
- spring-milestones
ignore:
- dependency-name: com.nimbusds:nimbus-jose-jwt
- dependency-name: org.python:jython
- dependency-name: org.apache.directory.server:*
- dependency-name: org.apache.directory.shared:*
- dependency-name: org.junit:junit-bom
update-types:
- version-update:semver-major
- dependency-name: org.mockito:mockito-bom
update-types:
- version-update:semver-major
- dependency-name: '*'
update-types:
- version-update:semver-major
- version-update:semver-minor
- package-ecosystem: gradle
target-branch: 6.4.x
directory: /
@@ -39,25 +39,48 @@ jobs:
toolchain: 17
with:
java-version: ${{ matrix.java-version }}
test-args: --refresh-dependencies -PforceMavenRepositories=snapshot,https://oss.sonatype.org/content/repositories/snapshots -PisOverrideVersionCatalog -PtestToolchain=${{ matrix.toolchain }} -PspringFrameworkVersion=7.+ -PreactorVersion=2025.+ -PspringDataVersion=2025.+ --stacktrace
test-args: --refresh-dependencies -PforceMavenRepositories=snapshot -PisOverrideVersionCatalog -PtestToolchain=${{ matrix.toolchain }} -PspringFrameworkVersion=6.2.+ -PreactorVersion=2023.0.+ -PspringDataVersion=2024.0.+ --stacktrace
secrets: inherit
check-samples:
name: Check Samples
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'spring-projects' }}
steps:
- uses: actions/checkout@v4
- name: Set up gradle
uses: spring-io/spring-gradle-build-action@v2
with:
java-version: 17
distribution: temurin
- name: Check samples project
env:
LOCAL_REPOSITORY_PATH: ${{ github.workspace }}/build/publications/repos
SAMPLES_DIR: ../spring-security-samples
run: |
# Extract version from gradle.properties
version=$(cat gradle.properties | grep "version=" | awk -F'=' '{print $2}')
# Extract samplesBranch from gradle.properties
samples_branch=$(cat gradle.properties | grep "samplesBranch=" | awk -F'=' '{print $2}')
./gradlew publishMavenJavaPublicationToLocalRepository
./gradlew cloneRepository -PrepositoryName="spring-projects/spring-security-samples" -Pref="$samples_branch" -PcloneOutputDirectory="$SAMPLES_DIR"
./gradlew --refresh-dependencies --project-dir "$SAMPLES_DIR" --init-script spring-security-ci.gradle -PlocalRepositoryPath="$LOCAL_REPOSITORY_PATH" -PspringSecurityVersion="$version" test integrationTest
deploy-artifacts:
name: Deploy Artifacts
needs: [ build, test]
needs: [ build, test, check-samples ]
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml@v1
with:
should-deploy-artifacts: ${{ needs.build.outputs.should-deploy-artifacts }}
secrets: inherit
deploy-docs:
name: Deploy Docs
needs: [ build, test ]
needs: [ build, test, check-samples ]
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-docs.yml@v1
with:
should-deploy-docs: ${{ needs.build.outputs.should-deploy-artifacts }}
secrets: inherit
deploy-schema:
name: Deploy Schema
needs: [ build, test ]
needs: [ build, test, check-samples ]
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml@v1
with:
should-deploy-schema: ${{ needs.build.outputs.should-deploy-artifacts }}
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
strategy:
matrix:
# List of active maintenance branches.
branch: [ main, 6.5.x, 6.4.x, 6.3.x ]
branch: [ main, 6.4.x, 6.3.x ]
runs-on: ubuntu-latest
steps:
- name: Checkout
-3
View File
@@ -79,9 +79,6 @@ 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.
Additionally, since Streams are https://github.com/spring-projects/spring-security/issues/7154[much slower] than `for` loops, please use them judiciously.
The team may ask you to change to a `for` loop if the given code is along a hot path.
To format the code as well as check the style, run `./gradlew format && ./gradlew check`.
[[submit-a-pull-request]]
-2
View File
@@ -1,6 +1,5 @@
plugins {
id "java-gradle-plugin"
id "groovy-gradle-plugin"
id "java"
id "groovy"
}
@@ -77,7 +76,6 @@ dependencies {
implementation libs.com.github.spullara.mustache.java.compiler
implementation libs.io.spring.javaformat.spring.javaformat.gradle.plugin
implementation libs.io.spring.nohttp.nohttp.gradle
implementation libs.org.jetbrains.kotlin.kotlin.gradle.plugin
implementation (libs.net.sourceforge.htmlunit) {
exclude group: 'org.eclipse.jetty.websocket', module: 'websocket-client'
}
@@ -61,7 +61,7 @@ public class ManagementConfigurationPlugin implements Plugin<Project> {
PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class);
publishing.getPublications().withType(MavenPublication.class, (mavenPublication -> {
mavenPublication.versionMapping((versions) ->
versions.allVariants((versionMapping) -> versionMapping.fromResolutionResult())
versions.allVariants(versionMapping -> versionMapping.fromResolutionResult())
);
}));
});
@@ -71,4 +71,4 @@ public class ManagementConfigurationPlugin implements Plugin<Project> {
}));
});
}
}
}
@@ -80,11 +80,6 @@ class RepositoryConventionPlugin implements Plugin<Project> {
}
url = 'https://repo.spring.io/release/'
}
forceMavenRepositories.findAll { it.startsWith('https://') || it.startsWith('file://') }.each { mavenUrl ->
maven {
url mavenUrl
}
}
}
}
@@ -32,13 +32,10 @@ public class SchemaZipPlugin implements Plugin<Project> {
for (def key : schemas.keySet()) {
def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1')
assert shortName != key
def schemaResourceName = schemas.get(key)
File xsdFile = module.sourceSets.main.resources.find {
it.path.endsWith(schemaResourceName)
}
if (xsdFile == null) {
throw new IllegalStateException("Could not find schema file for resource name " + schemaResourceName + " in src/main/resources")
it.path.endsWith(schemas.get(key))
}
assert xsdFile != null
schemaZip.into (shortName) {
duplicatesStrategy 'exclude'
from xsdFile.path
@@ -1,17 +0,0 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id 'kotlin'
}
project.plugins.withId("org.jetbrains.kotlin.jvm", (kotlinProject) -> {
project.tasks.withType(KotlinCompile).configureEach {
kotlinOptions {
languageVersion = '2.2'
apiVersion = '2.2'
freeCompilerArgs = ["-Xjsr305=strict", "-Xsuppress-version-warnings"]
jvmTarget = '17'
}
}
})
@@ -81,6 +81,9 @@ public class CheckClasspathForProhibitedDependencies extends DefaultTask {
if (group.startsWith("javax")) {
return true;
}
if (group.equals("commons-logging")) {
return true;
}
if (group.equals("org.slf4j") && id.getName().equals("jcl-over-slf4j")) {
return true;
}
@@ -46,7 +46,7 @@ public class CheckExpectedBranchVersionPlugin implements Plugin<Project> {
task.setDescription("Check if the project version matches the branch version");
task.onlyIf("skipCheckExpectedBranchVersion property is false or not present", CheckExpectedBranchVersionPlugin::skipPropertyFalseOrNotPresent);
task.getVersion().convention(project.provider(() -> project.getVersion().toString()));
task.getBranchName().convention(project.getProviders().exec((execSpec) -> execSpec.setCommandLine("git", "symbolic-ref", "--short", "HEAD")).getStandardOutput().getAsText());
task.getBranchName().convention(project.getProviders().exec(execSpec -> execSpec.setCommandLine("git", "symbolic-ref", "--short", "HEAD")).getStandardOutput().getAsText());
task.getOutputFile().convention(project.getLayout().getBuildDirectory().file("check-expected-branch-version"));
});
project.getTasks().named(JavaBasePlugin.CHECK_TASK_NAME, checkTask -> checkTask.dependsOn(checkExpectedBranchVersionTask));
-1
View File
@@ -14,7 +14,6 @@ dependencies {
provided 'jakarta.servlet:jakarta.servlet-api'
testImplementation project(path : ':spring-security-web', configuration : 'tests')
testImplementation "org.assertj:assertj-core"
testImplementation "org.junit.jupiter:junit-jupiter-api"
testImplementation "org.junit.jupiter:junit-jupiter-params"
@@ -52,6 +52,7 @@ 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;
import org.springframework.util.StringUtils;
@@ -334,7 +335,7 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
}
public final void setProxyReceptorUrl(final String proxyReceptorUrl) {
this.proxyReceptorMatcher = PathPatternRequestMatcher.withDefaults().matcher(proxyReceptorUrl);
this.proxyReceptorMatcher = new AntPathRequestMatcher("/**" + proxyReceptorUrl);
}
public final void setProxyGrantingTicketStorage(final ProxyGrantingTicketStorage proxyGrantingTicketStorage) {
@@ -55,8 +55,6 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;
import static org.springframework.security.web.servlet.TestMockHttpServletRequests.post;
/**
* Tests {@link CasAuthenticationFilter}.
@@ -81,7 +79,9 @@ public class CasAuthenticationFilterTests {
@Test
public void testNormalOperation() throws Exception {
MockHttpServletRequest request = post("/login/cas").param("ticket", "ST-0-ER94xMJmn6pha35CQRoZ").build();
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/login/cas");
request.setServletPath("/login/cas");
request.addParameter("ticket", "ST-0-ER94xMJmn6pha35CQRoZ");
CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setAuthenticationManager((a) -> a);
assertThat(filter.requiresAuthentication(request, new MockHttpServletResponse())).isTrue();
@@ -104,22 +104,24 @@ public class CasAuthenticationFilterTests {
String url = "/login/cas";
CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setFilterProcessesUrl(url);
MockHttpServletRequest request = post(url).build();
MockHttpServletRequest request = new MockHttpServletRequest("POST", url);
MockHttpServletResponse response = new MockHttpServletResponse();
request.setServletPath(url);
assertThat(filter.requiresAuthentication(request, response)).isTrue();
}
@Test
public void testRequiresAuthenticationProxyRequest() {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
MockHttpServletRequest request = get("/pgtCallback").build();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
request.setServletPath("/pgtCallback");
assertThat(filter.requiresAuthentication(request, response)).isFalse();
filter.setProxyReceptorUrl(request.getServletPath());
assertThat(filter.requiresAuthentication(request, response)).isFalse();
filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));
assertThat(filter.requiresAuthentication(request, response)).isTrue();
request = get("/other").build();
request.setServletPath("/other");
assertThat(filter.requiresAuthentication(request, response)).isFalse();
}
@@ -131,10 +133,12 @@ public class CasAuthenticationFilterTests {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setFilterProcessesUrl(url);
filter.setServiceProperties(properties);
MockHttpServletRequest request = post(url).build();
MockHttpServletRequest request = new MockHttpServletRequest("POST", url);
MockHttpServletResponse response = new MockHttpServletResponse();
request.setServletPath(url);
assertThat(filter.requiresAuthentication(request, response)).isTrue();
request = post("/other").build();
request = new MockHttpServletRequest("POST", "/other");
request.setServletPath("/other");
assertThat(filter.requiresAuthentication(request, response)).isFalse();
request.setParameter(properties.getArtifactParameter(), "value");
assertThat(filter.requiresAuthentication(request, response)).isTrue();
@@ -152,8 +156,9 @@ public class CasAuthenticationFilterTests {
@Test
public void testAuthenticateProxyUrl() throws Exception {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
MockHttpServletRequest request = get("/pgtCallback").build();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
request.setServletPath("/pgtCallback");
filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));
filter.setProxyReceptorUrl(request.getServletPath());
assertThat(filter.attemptAuthentication(request, response)).isNull();
@@ -167,7 +172,9 @@ public class CasAuthenticationFilterTests {
given(manager.authenticate(any(Authentication.class))).willReturn(authentication);
ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setAuthenticateAllArtifacts(true);
MockHttpServletRequest request = post("/authenticate").param("ticket", "ST-1-123").build();
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/authenticate");
request.setParameter("ticket", "ST-1-123");
request.setServletPath("/authenticate");
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = mock(FilterChain.class);
CasAuthenticationFilter filter = new CasAuthenticationFilter();
@@ -193,9 +200,10 @@ public class CasAuthenticationFilterTests {
@Test
public void testChainNotInvokedForProxyReceptor() throws Exception {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
MockHttpServletRequest request = get("/pgtCallback").build();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = mock(FilterChain.class);
request.setServletPath("/pgtCallback");
filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));
filter.setProxyReceptorUrl(request.getServletPath());
filter.doFilter(request, response, chain);
@@ -263,14 +271,16 @@ public class CasAuthenticationFilterTests {
@Test
public void requiresAuthenticationWhenProxyRequestMatcherThenMatches() {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
MockHttpServletRequest request = get("/pgtCallback").build();
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 = get("/other").build();
request.setRequestURI("/other");
request.setServletPath("/other");
assertThat(filter.requiresAuthentication(request, response)).isFalse();
}
+16 -2
View File
@@ -4,7 +4,7 @@ import trang.RncToXsd
apply plugin: 'io.spring.convention.spring-module'
apply plugin: 'trang'
apply plugin: 'security-kotlin'
apply plugin: 'kotlin'
configurations {
opensaml5 {
@@ -78,6 +78,12 @@ dependencies {
exclude group: 'commons-logging', module: 'commons-logging'
exclude group: 'xml-apis', module: 'xml-apis'
}
testImplementation "org.apache.directory.server:apacheds-core"
testImplementation "org.apache.directory.server:apacheds-core-entry"
testImplementation "org.apache.directory.server:apacheds-protocol-shared"
testImplementation "org.apache.directory.server:apacheds-protocol-ldap"
testImplementation "org.apache.directory.server:apacheds-server-jndi"
testImplementation 'org.apache.directory.shared:shared-ldap'
testImplementation "com.unboundid:unboundid-ldapsdk"
testImplementation 'jakarta.persistence:jakarta.persistence-api'
testImplementation "org.hibernate.orm:hibernate-core"
@@ -121,7 +127,6 @@ dependencies {
testRuntimeOnly 'org.hsqldb:hsqldb'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
}
def rncToXsd = tasks.named('rncToXsd', RncToXsd)
@@ -153,6 +158,15 @@ tasks.named('sourcesJar', Jar).configure {
}
}
tasks.withType(KotlinCompile).configureEach {
kotlinOptions {
languageVersion = "1.7"
apiVersion = "1.7"
freeCompilerArgs = ["-Xjsr305=strict", "-Xsuppress-version-warnings"]
jvmTarget = "17"
}
}
configure(project.tasks.withType(Test)) {
doFirst {
systemProperties['springSecurityVersion'] = version
@@ -44,7 +44,7 @@ import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMap
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.MockMvc;
@@ -326,11 +326,11 @@ public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
abstract static class BaseLdapServerConfig extends BaseLdapProviderConfig {
@Bean
UnboundIdContainer ldapServer() throws Exception {
UnboundIdContainer unboundIdContainer = new UnboundIdContainer("dc=springframework,dc=org",
ApacheDSContainer ldapServer() throws Exception {
ApacheDSContainer apacheDSContainer = new ApacheDSContainer("dc=springframework,dc=org",
"classpath:/test-server.ldif");
unboundIdContainer.setPort(getPort());
return unboundIdContainer;
apacheDSContainer.setPort(getPort());
return apacheDSContainer;
}
}
@@ -74,7 +74,8 @@ public class HelloRSocketITests {
// @formatter:off
this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
.interceptors((registry) ->
registry.forSocketAcceptor(this.interceptor)
)
.acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0))
@@ -87,7 +87,8 @@ public class HelloRSocketObservationITests {
// @formatter:off
this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
.interceptors((registry) ->
registry.forSocketAcceptor(this.interceptor)
)
.acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0))
@@ -74,7 +74,8 @@ public class HelloRSocketWithWebFluxITests {
// @formatter:off
this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
.interceptors((registry) ->
registry.forSocketAcceptor(this.interceptor)
)
.acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0))
@@ -86,7 +86,8 @@ public class JwtITests {
// @formatter:off
this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
.interceptors((registry) ->
registry.forSocketAcceptor(this.interceptor)
)
.acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0))
@@ -81,7 +81,8 @@ public class RSocketMessageHandlerConnectionITests {
// @formatter:off
this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
.interceptors((registry) ->
registry.forSocketAcceptor(this.interceptor)
)
.acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0))
@@ -79,7 +79,8 @@ public class RSocketMessageHandlerITests {
// @formatter:off
this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
.interceptors((registry) ->
registry.forSocketAcceptor(this.interceptor)
)
.acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0))
@@ -79,7 +79,8 @@ public class SimpleAuthenticationITests {
// @formatter:off
this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
.interceptors((registry) ->
registry.forSocketAcceptor(this.interceptor)
)
.acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0))
@@ -43,7 +43,7 @@ import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMap
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
@@ -226,18 +226,18 @@ public class LdapBindAuthenticationManagerFactoryITests {
@EnableWebSecurity
abstract static class BaseLdapServerConfig implements DisposableBean {
private UnboundIdContainer container;
private ApacheDSContainer container;
@Bean
UnboundIdContainer ldapServer() {
this.container = new UnboundIdContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
ApacheDSContainer ldapServer() throws Exception {
this.container = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
this.container.setPort(0);
return this.container;
}
@Bean
BaseLdapPathContextSource contextSource(UnboundIdContainer container) {
int port = container.getPort();
BaseLdapPathContextSource contextSource(ApacheDSContainer container) {
int port = container.getLocalPort();
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org");
}
@@ -31,7 +31,7 @@ import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
@@ -93,18 +93,18 @@ public class LdapPasswordComparisonAuthenticationManagerFactoryITests {
@EnableWebSecurity
abstract static class BaseLdapServerConfig implements DisposableBean {
private UnboundIdContainer container;
private ApacheDSContainer container;
@Bean
UnboundIdContainer ldapServer() {
this.container = new UnboundIdContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
ApacheDSContainer ldapServer() throws Exception {
this.container = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
this.container.setPort(0);
return this.container;
}
@Bean
BaseLdapPathContextSource contextSource(UnboundIdContainer container) {
int port = container.getPort();
BaseLdapPathContextSource contextSource(ApacheDSContainer container) {
int port = container.getLocalPort();
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org");
}
@@ -56,7 +56,7 @@ public class LdapProviderBeanDefinitionParserTests {
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
AuthenticationManager.class);
Authentication auth = authenticationManager
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("otherben", "otherbenspassword"));
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("ben", "benspassword"));
UserDetails ben = (UserDetails) auth.getPrincipal();
assertThat(ben.getAuthorities()).hasSize(3);
}
@@ -127,27 +127,6 @@ public class LdapProviderBeanDefinitionParserTests {
assertThat(auth).isNotNull();
}
@Test
public void supportsShaPasswordEncoder() {
this.appCtx = new InMemoryXmlApplicationContext("""
<ldap-server ldif='classpath:test-server.ldif' port='0'/>
<authentication-manager>
<ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>
<password-compare>
<password-encoder ref='pe' />
</password-compare>
</ldap-authentication-provider>
</authentication-manager>
<b:bean id='pe' class='org.springframework.security.crypto.password.LdapShaPasswordEncoder' />
""");
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
AuthenticationManager.class);
Authentication auth = authenticationManager
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("ben", "benspassword"));
assertThat(auth).isNotNull();
}
@Test
public void inetOrgContextMapperIsSupported() {
this.appCtx = new InMemoryXmlApplicationContext(
@@ -26,7 +26,7 @@ import org.springframework.ldap.core.LdapTemplate;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.util.InMemoryXmlApplicationContext;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
@@ -92,9 +92,9 @@ public class LdapServerBeanDefinitionParserTests {
@Test
public void defaultLdifFileIsSuccessful() {
this.appCtx = new InMemoryXmlApplicationContext("<ldap-server/>");
UnboundIdContainer dsContainer = this.appCtx.getBean(UnboundIdContainer.class);
ApacheDSContainer dsContainer = this.appCtx.getBean(ApacheDSContainer.class);
assertThat(ReflectionTestUtils.getField(dsContainer, "ldif")).isEqualTo("classpath*:*.ldif");
assertThat(ReflectionTestUtils.getField(dsContainer, "ldifResources")).isEqualTo("classpath*:*.ldif");
}
private int getDefaultPort() throws IOException {
@@ -7,6 +7,7 @@
<logger name="org.springframework.security" level="${sec.log.level:-WARN}"/>
<logger name="org.apache.directory" level="ERROR"/>
<logger name="JdbmTable" level="INFO"/>
<logger name="JdbmIndex" level="INFO"/>
<logger name="org.apache.mina" level="WARN"/>
@@ -54,6 +54,8 @@ public abstract class BeanIds {
public static final String METHOD_SECURITY_METADATA_SOURCE_ADVISOR = PREFIX + "methodSecurityMetadataSourceAdvisor";
public static final String EMBEDDED_APACHE_DS = PREFIX + "apacheDirectoryServerContainer";
public static final String EMBEDDED_UNBOUNDID = PREFIX + "unboundidServerContainer";
public static final String CONTEXT_SOURCE = PREFIX + "securityContextSource";
@@ -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 7.0. Please update your schema declarations to the 7.0 schema.",
+ "with Spring Security 6.5. Please update your schema declarations to the 6.5 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-7\\.0.*.xsd.*")
return schemaLocation.matches("(?m).*spring-security-6\\.5.*.xsd.*")
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
|| !schemaLocation.matches("(?m).*spring-security.*");
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,6 +50,7 @@ import org.springframework.web.filter.DelegatingFilterProxy;
* @param <O> The object that this builder returns
* @param <B> The type of this builder (that is returned by the base class)
* @author Rob Winch
* @author DingHao
* @see WebSecurity
*/
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
@@ -59,7 +60,7 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();
private List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();
private final Map<Class<?>, Object> sharedObjects = new HashMap<>();
@@ -386,8 +387,12 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.init((B) this);
}
for (SecurityConfigurer<O, B> configurer : this.configurersAddedInInitializing) {
configurer.init((B) this);
while (!this.configurersAddedInInitializing.isEmpty()) {
List<SecurityConfigurer<O, B>> toInit = this.configurersAddedInInitializing;
this.configurersAddedInInitializing = new ArrayList<>();
for (SecurityConfigurer<O, B> configurer : toInit) {
configurer.init((B) this);
}
}
}
@@ -50,6 +50,17 @@ public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>>
public void configure(B builder) throws Exception {
}
/**
* Return the {@link SecurityBuilder} when done using the {@link SecurityConfigurer}.
* This is useful for method chaining.
* @return the {@link SecurityBuilder} for further customizations
* @deprecated For removal in 7.0. Use the lambda based configuration instead.
*/
@Deprecated(since = "6.1", forRemoval = true)
public B and() {
return getBuilder();
}
/**
* Gets the {@link SecurityBuilder}. Cannot be null.
* @return the {@link SecurityBuilder}
@@ -37,6 +37,7 @@ import org.springframework.security.ldap.authentication.LdapAuthenticator;
import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import org.springframework.security.ldap.search.LdapUserSearch;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper;
@@ -59,8 +60,12 @@ import org.springframework.util.ClassUtils;
public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuilder<B>>
extends SecurityConfigurerAdapter<AuthenticationManager, B> {
private static final String APACHEDS_CLASSNAME = "org.apache.directory.server.core.DefaultDirectoryService";
private static final String UNBOUNDID_CLASSNAME = "com.unboundid.ldap.listener.InMemoryDirectoryServer";
private static final boolean apacheDsPresent;
private static final boolean unboundIdPresent;
private String groupRoleAttribute = "cn";
@@ -95,6 +100,7 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
static {
ClassLoader classLoader = LdapAuthenticationProviderConfigurer.class.getClassLoader();
apacheDsPresent = ClassUtils.isPresent(APACHEDS_CLASSNAME, classLoader);
unboundIdPresent = ClassUtils.isPresent(UNBOUNDID_CLASSNAME, classLoader);
}
@@ -386,10 +392,6 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
return this;
}
public B and() {
return getBuilder();
}
@Override
public void configure(B builder) throws Exception {
LdapAuthenticationProvider provider = postProcess(build());
@@ -465,6 +467,8 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
*/
public final class ContextSourceBuilder {
private static final String APACHEDS_CLASSNAME = "org.apache.directory.server.core.DefaultDirectoryService";
private static final String UNBOUNDID_CLASSNAME = "com.unboundid.ldap.listener.InMemoryDirectoryServer";
private static final int DEFAULT_PORT = 33389;
@@ -580,8 +584,14 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
return contextSource;
}
private void startEmbeddedLdapServer() {
if (unboundIdPresent) {
private void startEmbeddedLdapServer() throws Exception {
if (apacheDsPresent) {
ApacheDSContainer apacheDsContainer = new ApacheDSContainer(this.root, this.ldif);
apacheDsContainer.setPort(getPort());
postProcess(apacheDsContainer);
this.port = apacheDsContainer.getLocalPort();
}
else if (unboundIdPresent) {
UnboundIdContainer unboundIdContainer = new UnboundIdContainer(this.root, this.ldif);
unboundIdContainer.setPort(getPort());
postProcess(unboundIdContainer);
@@ -41,8 +41,4 @@ public class InMemoryUserDetailsManagerConfigurer<B extends ProviderManagerBuild
super(new InMemoryUserDetailsManager(new ArrayList<>()));
}
public B and() {
return getBuilder();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,22 +16,13 @@
package org.springframework.security.config.annotation.method.configuration;
import java.util.List;
import org.springframework.aop.framework.AopInfrastructureBean;
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.data.domain.PageImpl;
import org.springframework.data.domain.SliceImpl;
import org.springframework.data.geo.GeoPage;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.GeoResults;
import org.springframework.security.aot.hint.SecurityHintsRegistrar;
import org.springframework.security.authorization.AuthorizationProxyFactory;
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;
import org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar;
@Configuration(proxyBeanMethods = false)
@@ -43,45 +34,4 @@ final class AuthorizationProxyDataConfiguration implements AopInfrastructureBean
return new AuthorizeReturnObjectDataHintsRegistrar(proxyFactory);
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
DataTargetVisitor dataTargetVisitor() {
return new DataTargetVisitor();
}
private static final class DataTargetVisitor implements AuthorizationAdvisorProxyFactory.TargetVisitor, Ordered {
private static final int DEFAULT_ORDER = 200;
@Override
public Object visit(AuthorizationAdvisorProxyFactory proxyFactory, Object target) {
if (target instanceof GeoResults<?> geoResults) {
return new GeoResults<>(proxyFactory.proxy(geoResults.getContent()), geoResults.getAverageDistance());
}
if (target instanceof GeoResult<?> geoResult) {
return new GeoResult<>(proxyFactory.proxy(geoResult.getContent()), geoResult.getDistance());
}
if (target instanceof GeoPage<?> geoPage) {
GeoResults<?> results = new GeoResults<>(proxyFactory.proxy(geoPage.getContent()),
geoPage.getAverageDistance());
return new GeoPage<>(results, geoPage.getPageable(), geoPage.getTotalElements());
}
if (target instanceof PageImpl<?> page) {
List<?> content = proxyFactory.proxy(page.getContent());
return new PageImpl<>(content, page.getPageable(), page.getTotalElements());
}
if (target instanceof SliceImpl<?> slice) {
List<?> content = proxyFactory.proxy(slice.getContent());
return new SliceImpl<>(content, slice.getPageable(), slice.hasNext());
}
return null;
}
@Override
public int getOrder() {
return DEFAULT_ORDER;
}
}
}
@@ -16,12 +16,8 @@
package org.springframework.security.config.annotation.method.configuration;
import java.util.List;
import java.util.Map;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -29,17 +25,12 @@ import org.springframework.context.annotation.Role;
import org.springframework.core.Ordered;
import org.springframework.http.HttpEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;
import org.springframework.security.web.util.ThrowableAnalyzer;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
@Configuration
class AuthorizationProxyWebConfiguration implements WebMvcConfigurer {
class AuthorizationProxyWebConfiguration {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@@ -47,18 +38,6 @@ class AuthorizationProxyWebConfiguration implements WebMvcConfigurer {
return new WebTargetVisitor();
}
@Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
for (int i = 0; i < resolvers.size(); i++) {
HandlerExceptionResolver resolver = resolvers.get(i);
if (resolver instanceof DefaultHandlerExceptionResolver) {
resolvers.add(i, new AccessDeniedExceptionResolver());
return;
}
}
resolvers.add(new AccessDeniedExceptionResolver());
}
static class WebTargetVisitor implements AuthorizationAdvisorProxyFactory.TargetVisitor, Ordered {
private static final int DEFAULT_ORDER = 100;
@@ -75,7 +54,7 @@ class AuthorizationProxyWebConfiguration implements WebMvcConfigurer {
if (target instanceof ModelAndView mav) {
View view = mav.getView();
String viewName = mav.getViewName();
Map<String, Object> model = proxyFactory.proxy(mav.getModel());
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());
@@ -91,24 +70,4 @@ class AuthorizationProxyWebConfiguration implements WebMvcConfigurer {
}
static class AccessDeniedExceptionResolver implements HandlerExceptionResolver {
final ThrowableAnalyzer throwableAnalyzer = new ThrowableAnalyzer();
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);
Throwable accessDeniedException = this.throwableAnalyzer
.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
if (accessDeniedException != null) {
return new ModelAndView((model, req, res) -> {
throw ex;
});
}
return null;
}
}
}
@@ -38,9 +38,6 @@ class ReactiveMethodSecuritySelector 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.server.ServerWebExchange",
null);
private static final boolean isObservabilityPresent = ClassUtils
.isPresent("io.micrometer.observation.ObservationRegistry", null);
@@ -64,9 +61,6 @@ class ReactiveMethodSecuritySelector implements ImportSelector {
if (isDataPresent) {
imports.add(AuthorizationProxyDataConfiguration.class.getName());
}
if (isWebPresent) {
imports.add(AuthorizationProxyWebConfiguration.class.getName());
}
if (isObservabilityPresent) {
imports.add(ReactiveMethodObservationConfiguration.class.getName());
}
@@ -18,20 +18,41 @@ package org.springframework.security.config.annotation.web;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletRegistration;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.ApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpMethod;
import org.springframework.lang.Nullable;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.security.config.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.ServletRegistrationsSupport.RegistrationMapping;
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;
import org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
/**
* A base class for registering {@link RequestMatcher}'s. For example, it might allow for
@@ -44,12 +65,23 @@ import org.springframework.util.Assert;
*/
public abstract class AbstractRequestMatcherRegistry<C> {
private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
private static final String HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector";
private static final boolean mvcPresent;
private static final RequestMatcher ANY_REQUEST = AnyRequestMatcher.INSTANCE;
private ApplicationContext context;
private boolean anyRequestConfigured = false;
static {
mvcPresent = ClassUtils.isPresent(HANDLER_MAPPING_INTROSPECTOR,
AbstractRequestMatcherRegistry.class.getClassLoader());
}
private final Log logger = LogFactory.getLog(getClass());
protected final void setApplicationContext(ApplicationContext context) {
@@ -75,6 +107,36 @@ public abstract class AbstractRequestMatcherRegistry<C> {
return configurer;
}
/**
* Creates {@link MvcRequestMatcher} instances for the method and patterns passed in
* @param method the HTTP method to use or null if any should be used
* @param mvcPatterns the Spring MVC patterns to match on
* @return a List of {@link MvcRequestMatcher} instances
*/
protected final List<MvcRequestMatcher> createMvcMatchers(HttpMethod method, String... mvcPatterns) {
Assert.state(!this.anyRequestConfigured, "Can't configure mvcMatchers after anyRequest");
ResolvableType type = ResolvableType.forClassWithGenerics(ObjectPostProcessor.class, Object.class);
ObjectProvider<ObjectPostProcessor<Object>> postProcessors = this.context.getBeanProvider(type);
ObjectPostProcessor<Object> opp = postProcessors.getObject();
if (!this.context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
throw new NoSuchBeanDefinitionException("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.");
}
HandlerMappingIntrospector introspector = this.context.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME,
HandlerMappingIntrospector.class);
List<MvcRequestMatcher> matchers = new ArrayList<>(mvcPatterns.length);
for (String mvcPattern : mvcPatterns) {
MvcRequestMatcher matcher = new MvcRequestMatcher(introspector, mvcPattern);
opp.postProcess(matcher);
if (method != null) {
matcher.setMethod(method);
}
matchers.add(matcher);
}
return matchers;
}
/**
* Maps a {@link List} of
* {@link org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher}
@@ -118,9 +180,12 @@ public abstract class AbstractRequestMatcherRegistry<C> {
/**
* <p>
* Match when the {@link HttpMethod} is {@code method} and when the request URI
* matches one of {@code patterns}. See
* {@link org.springframework.web.util.pattern.PathPattern} for matching rules.
* If the {@link HandlerMappingIntrospector} is available in the classpath, maps to an
* {@link MvcRequestMatcher} that also specifies a specific {@link HttpMethod} to
* match on. This matcher will use the same rules that Spring MVC uses for matching.
* For example, often times a mapping of the path "/path" will match on "/path",
* "/path/", "/path.html", etc. If the {@link HandlerMappingIntrospector} is not
* available, maps to an {@link AntPathRequestMatcher}.
* </p>
* <p>
* If a specific {@link RequestMatcher} must be specified, use
@@ -128,7 +193,8 @@ public abstract class AbstractRequestMatcherRegistry<C> {
* </p>
* @param method the {@link HttpMethod} to use or {@code null} for any
* {@link HttpMethod}.
* @param patterns the patterns to match on
* @param patterns the patterns to match on. The rules for matching are defined by
* Spring MVC if {@link MvcRequestMatcher} is used
* @return the object that is chained after creating the {@link RequestMatcher}.
* @since 5.8
*/
@@ -139,13 +205,27 @@ public abstract class AbstractRequestMatcherRegistry<C> {
+ "leading slash in all your request matcher patterns. In future versions of "
+ "Spring Security, leaving out the leading slash will result in an exception.");
}
Assert.state(!this.anyRequestConfigured, "Can't configure requestMatchers after anyRequest");
PathPatternRequestMatcher.Builder builder = this.context
.getBeanProvider(PathPatternRequestMatcher.Builder.class)
.getIfUnique(PathPatternRequestMatcher::withDefaults);
if (!mvcPresent) {
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
}
if (!(this.context instanceof WebApplicationContext)) {
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
}
WebApplicationContext context = (WebApplicationContext) this.context;
ServletContext servletContext = context.getServletContext();
if (servletContext == null) {
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
}
List<RequestMatcher> matchers = new ArrayList<>();
for (String pattern : patterns) {
matchers.add(builder.matcher(method, pattern));
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));
}
}
return requestMatchers(matchers.toArray(new RequestMatcher[0]));
}
@@ -159,16 +239,64 @@ public abstract class AbstractRequestMatcherRegistry<C> {
return false;
}
private RequestMatcher resolve(AntPathRequestMatcher ant, MvcRequestMatcher mvc, ServletContext servletContext) {
ServletRegistrationsSupport registrations = new ServletRegistrationsSupport(servletContext);
Collection<RegistrationMapping> mappings = registrations.mappings();
if (mappings.isEmpty()) {
return new DispatcherServletDelegatingRequestMatcher(ant, mvc, new MockMvcRequestMatcher());
}
Collection<RegistrationMapping> dispatcherServletMappings = registrations.dispatcherServletMappings();
if (dispatcherServletMappings.isEmpty()) {
return new DispatcherServletDelegatingRequestMatcher(ant, mvc, new MockMvcRequestMatcher());
}
if (dispatcherServletMappings.size() > 1) {
String errorMessage = computeErrorMessage(servletContext.getServletRegistrations().values());
throw new IllegalArgumentException(errorMessage);
}
RegistrationMapping dispatcherServlet = dispatcherServletMappings.iterator().next();
if (mappings.size() > 1 && !dispatcherServlet.isDefault()) {
String errorMessage = computeErrorMessage(servletContext.getServletRegistrations().values());
throw new IllegalArgumentException(errorMessage);
}
if (dispatcherServlet.isDefault()) {
if (mappings.size() == 1) {
return mvc;
}
return new DispatcherServletDelegatingRequestMatcher(ant, mvc);
}
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.
""";
Map<String, Collection<String>> mappings = new LinkedHashMap<>();
for (ServletRegistration registration : registrations) {
mappings.put(registration.getClassName(), registration.getMappings());
}
return String.format(template, mappings);
}
/**
* <p>
* Match when the request URI matches one of {@code patterns}. See
* {@link org.springframework.web.util.pattern.PathPattern} for matching rules.
* If the {@link HandlerMappingIntrospector} is available in the classpath, maps to an
* {@link MvcRequestMatcher} that does not care which {@link HttpMethod} is used. This
* matcher will use the same rules that Spring MVC uses for matching. For example,
* often times a mapping of the path "/path" will match on "/path", "/path/",
* "/path.html", etc. If the {@link HandlerMappingIntrospector} is not available, maps
* to an {@link AntPathRequestMatcher}.
* </p>
* <p>
* If a specific {@link RequestMatcher} must be specified, use
* {@link #requestMatchers(RequestMatcher...)} instead
* </p>
* @param patterns the patterns to match on
* @param patterns the patterns to match on. The rules for matching are defined by
* Spring MVC if {@link MvcRequestMatcher} is used
* @return the object that is chained after creating the {@link RequestMatcher}.
* @since 5.8
*/
@@ -178,7 +306,12 @@ public abstract class AbstractRequestMatcherRegistry<C> {
/**
* <p>
* Match when the {@link HttpMethod} is {@code method}
* If the {@link HandlerMappingIntrospector} is available in the classpath, maps to an
* {@link MvcRequestMatcher} that matches on a specific {@link HttpMethod}. This
* matcher will use the same rules that Spring MVC uses for matching. For example,
* often times a mapping of the path "/path" will match on "/path", "/path/",
* "/path.html", etc. If the {@link HandlerMappingIntrospector} is not available, maps
* to an {@link AntPathRequestMatcher}.
* </p>
* <p>
* If a specific {@link RequestMatcher} must be specified, use
@@ -202,4 +335,182 @@ public abstract class AbstractRequestMatcherRegistry<C> {
*/
protected abstract C chainRequestMatchers(List<RequestMatcher> requestMatchers);
/**
* Utilities for creating {@link RequestMatcher} instances.
*
* @author Rob Winch
* @since 3.2
*/
private static final class RequestMatchers {
private RequestMatchers() {
}
/**
* Create a {@link List} of {@link AntPathRequestMatcher} instances.
* @param httpMethod the {@link HttpMethod} to use or {@code null} for any
* {@link HttpMethod}.
* @param antPatterns the ant patterns to create {@link AntPathRequestMatcher}
* from
* @return a {@link List} of {@link AntPathRequestMatcher} instances
*/
static List<RequestMatcher> antMatchers(HttpMethod httpMethod, String... antPatterns) {
return Arrays.asList(antMatchersAsArray(httpMethod, antPatterns));
}
/**
* Create a {@link List} of {@link AntPathRequestMatcher} instances that do not
* specify an {@link HttpMethod}.
* @param antPatterns the ant patterns to create {@link AntPathRequestMatcher}
* from
* @return a {@link List} of {@link AntPathRequestMatcher} instances
*/
static List<RequestMatcher> antMatchers(String... antPatterns) {
return antMatchers(null, antPatterns);
}
static RequestMatcher[] antMatchersAsArray(HttpMethod httpMethod, String... antPatterns) {
String method = (httpMethod != null) ? httpMethod.toString() : null;
RequestMatcher[] matchers = new RequestMatcher[antPatterns.length];
for (int index = 0; index < antPatterns.length; index++) {
matchers[index] = new AntPathRequestMatcher(antPatterns[index], method);
}
return matchers;
}
/**
* Create a {@link List} of {@link RegexRequestMatcher} instances.
* @param httpMethod the {@link HttpMethod} to use or {@code null} for any
* {@link HttpMethod}.
* @param regexPatterns the regular expressions to create
* {@link RegexRequestMatcher} from
* @return a {@link List} of {@link RegexRequestMatcher} instances
*/
static List<RequestMatcher> regexMatchers(HttpMethod httpMethod, String... regexPatterns) {
String method = (httpMethod != null) ? httpMethod.toString() : null;
List<RequestMatcher> matchers = new ArrayList<>();
for (String pattern : regexPatterns) {
matchers.add(new RegexRequestMatcher(pattern, method));
}
return matchers;
}
/**
* Create a {@link List} of {@link RegexRequestMatcher} instances that do not
* specify an {@link HttpMethod}.
* @param regexPatterns the regular expressions to create
* {@link RegexRequestMatcher} from
* @return a {@link List} of {@link RegexRequestMatcher} instances
*/
static List<RequestMatcher> regexMatchers(String... regexPatterns) {
return regexMatchers(null, regexPatterns);
}
}
static class DeferredRequestMatcher implements RequestMatcher {
final Function<ServletContext, RequestMatcher> requestMatcherFactory;
final AtomicReference<String> description = new AtomicReference<>();
final Map<ServletContext, RequestMatcher> requestMatchers = new ConcurrentHashMap<>();
DeferredRequestMatcher(Function<ServletContext, RequestMatcher> resolver, RequestMatcher... candidates) {
this.requestMatcherFactory = (sc) -> this.requestMatchers.computeIfAbsent(sc, resolver);
this.description.set("Deferred " + Arrays.toString(candidates));
}
RequestMatcher requestMatcher(ServletContext servletContext) {
return this.requestMatcherFactory.apply(servletContext);
}
@Override
public boolean matches(HttpServletRequest request) {
return this.requestMatcherFactory.apply(request.getServletContext()).matches(request);
}
@Override
public MatchResult matcher(HttpServletRequest request) {
return this.requestMatcherFactory.apply(request.getServletContext()).matcher(request);
}
@Override
public String toString() {
return this.description.get();
}
}
static class MockMvcRequestMatcher implements RequestMatcher {
@Override
public boolean matches(HttpServletRequest request) {
return request.getAttribute("org.springframework.test.web.servlet.MockMvc.MVC_RESULT_ATTRIBUTE") != null;
}
}
static class DispatcherServletRequestMatcher implements RequestMatcher {
@Override
public boolean matches(HttpServletRequest request) {
String name = request.getHttpServletMapping().getServletName();
ServletRegistration registration = request.getServletContext().getServletRegistration(name);
Assert.notNull(registration,
() -> computeErrorMessage(request.getServletContext().getServletRegistrations().values()));
try {
Class<?> clazz = Class.forName(registration.getClassName());
return DispatcherServlet.class.isAssignableFrom(clazz);
}
catch (ClassNotFoundException ex) {
return false;
}
}
}
static class DispatcherServletDelegatingRequestMatcher implements RequestMatcher {
private final AntPathRequestMatcher ant;
private final MvcRequestMatcher mvc;
private final RequestMatcher dispatcherServlet;
DispatcherServletDelegatingRequestMatcher(AntPathRequestMatcher ant, MvcRequestMatcher mvc) {
this(ant, mvc, new OrRequestMatcher(new MockMvcRequestMatcher(), new DispatcherServletRequestMatcher()));
}
DispatcherServletDelegatingRequestMatcher(AntPathRequestMatcher ant, MvcRequestMatcher mvc,
RequestMatcher dispatcherServlet) {
this.ant = ant;
this.mvc = mvc;
this.dispatcherServlet = dispatcherServlet;
}
RequestMatcher requestMatcher(HttpServletRequest request) {
if (this.dispatcherServlet.matches(request)) {
return this.mvc;
}
return this.ant;
}
@Override
public boolean matches(HttpServletRequest request) {
return requestMatcher(request).matches(request);
}
@Override
public MatchResult matcher(HttpServletRequest request) {
return requestMatcher(request).matcher(request);
}
@Override
public String toString() {
return "DispatcherServletDelegating [" + "ant = " + this.ant + ", mvc = " + this.mvc + "]";
}
}
}
@@ -19,6 +19,7 @@ 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;
/**
@@ -33,8 +34,11 @@ public final class RequestMatcherFactory {
private static PathPatternRequestMatcher.Builder builder;
public static void setApplicationContext(ApplicationContext context) {
builder = context.getBeanProvider(PathPatternRequestMatcher.Builder.class)
.getIfUnique(PathPatternRequestMatcher::withDefaults);
builder = context.getBeanProvider(PathPatternRequestMatcher.Builder.class).getIfUnique();
}
public static boolean usesPathPatterns() {
return builder != null;
}
public static RequestMatcher matcher(String path) {
@@ -42,7 +46,10 @@ public final class RequestMatcherFactory {
}
public static RequestMatcher matcher(HttpMethod method, String path) {
return builder.matcher(method, path);
if (builder != null) {
return builder.matcher(method, path);
}
return new AntPathRequestMatcher(path, (method != null) ? method.name() : null);
}
private RequestMatcherFactory() {
@@ -55,7 +55,6 @@ import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer;
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.PathPatternRequestTransformer;
import org.springframework.security.web.access.RequestMatcherDelegatingWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
@@ -431,7 +430,7 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
this.filterChainDecoratorPostProcessor = postProcessor.getIfUnique(ObjectPostProcessor::identity);
Class<HttpServletRequestTransformer> requestTransformerClass = HttpServletRequestTransformer.class;
this.privilegeEvaluatorRequestTransformer = applicationContext.getBeanProvider(requestTransformerClass)
.getIfUnique(PathPatternRequestTransformer::new);
.getIfUnique();
}
@Override
@@ -46,9 +46,8 @@ import org.springframework.security.web.SecurityFilterChain;
*
* &#064;Bean
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
* http.authorizeHttpRequests((authorize) -&gt; authorize
* .requestMatchers(&quot;/public/**&quot;).permitAll()
* .anyRequest().hasRole(&quot;USER&quot;))
* http.authorizeHttpRequests().requestMatchers(&quot;/public/**&quot;).permitAll().anyRequest()
* .hasRole(&quot;USER&quot;).and()
* // Possibly more configuration ...
* .formLogin() // enable form based log in
* // set permitAll for all URLs associated with Form Login
@@ -26,7 +26,15 @@ 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.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
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.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
@@ -37,6 +45,8 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.HandlerMappingIntrospectorRequestTransformer;
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;
@@ -48,6 +58,7 @@ import org.springframework.web.filter.CompositeFilter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
import org.springframework.web.servlet.support.RequestDataValueProcessor;
/**
@@ -65,6 +76,10 @@ import org.springframework.web.servlet.support.RequestDataValueProcessor;
*/
class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContextAware {
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
@@ -106,6 +121,86 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
}
}
@Bean
static BeanDefinitionRegistryPostProcessor springSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor() {
return new BeanDefinitionRegistryPostProcessor() {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
if (!registry.containsBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
return;
}
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);
}
}
BeanDefinition filterChainProxy = registry
.getBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
if (!filterChainProxy.getResolvableType().isInstance(CompositeFilterChainProxy.class)) {
BeanDefinitionBuilder hmiCacheFilterBldr = BeanDefinitionBuilder
.rootBeanDefinition(HandlerMappingIntrospectorCacheFilterFactoryBean.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
ManagedList<BeanMetadataElement> filters = new ManagedList<>();
filters.add(hmiCacheFilterBldr.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());
}
}
};
}
/**
* {@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> {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
public Filter getObject() throws Exception {
HandlerMappingIntrospector handlerMappingIntrospector = this.applicationContext
.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, HandlerMappingIntrospector.class);
return handlerMappingIntrospector.createCacheFilter();
}
@Override
public Class<?> getObjectType() {
return Filter.class;
}
}
/**
* Extends {@link FilterChainProxy} to provide as much passivity as possible but
* delegates to {@link CompositeFilter} for
@@ -69,6 +69,7 @@ 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
@@ -208,11 +209,12 @@ public class WebSecurityConfiguration implements ImportAware {
/**
* Used to ensure Spring MVC request matching is cached.
*
* Creates a {@link BeanDefinitionRegistryPostProcessor} that moves the
* 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 ServletRequestPathFilter} and the original FilterChainProxy under the
* original Bean name.
* {@link HandlerMappingIntrospector#createCacheFilter()} and the original
* FilterChainProxy under the original Bean name.
* @return
*/
@Bean
@@ -21,7 +21,6 @@ import java.util.UUID;
import org.springframework.security.authentication.AnonymousAuthenticationProvider;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -58,7 +57,7 @@ public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Creates a new instance
* @see HttpSecurity#anonymous(Customizer)
* @see HttpSecurity#anonymous()
*/
public AnonymousConfigurer() {
}
@@ -231,6 +231,17 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
return this;
}
/**
* Return the {@link HttpSecurityBuilder} when done using the
* {@link AuthorizeHttpRequestsConfigurer}. This is useful for method chaining.
* @return the {@link HttpSecurityBuilder} for further customizations
* @deprecated For removal in 7.0. Use the lambda based configuration instead.
*/
@Deprecated(since = "6.1", forRemoval = true)
public H and() {
return AuthorizeHttpRequestsConfigurer.this.and();
}
}
/**
@@ -24,7 +24,10 @@ import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityBuilder;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultRedirectStrategy;
@@ -93,7 +96,7 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Creates a new instance
* @see HttpSecurity#requiresChannel(Customizer)
* @see HttpSecurity#requiresChannel()
*/
public ChannelSecurityConfigurer(ApplicationContext context) {
this.REGISTRY = new ChannelRequestMatcherRegistry(context);
@@ -204,6 +207,18 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
/**
* Return the {@link SecurityBuilder} when done using the
* {@link SecurityConfigurer}. This is useful for method chaining.
* @return the type of {@link HttpSecurityBuilder} that is being configured
* @deprecated For removal in 7.0. Use
* {@link HttpSecurity#requiresChannel(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public H and() {
return ChannelSecurityConfigurer.this.and();
}
}
/**
@@ -18,18 +18,21 @@ package org.springframework.security.config.annotation.web.configurers;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
/**
* Adds {@link CorsFilter} to the Spring Security filter chain. If a bean by the name of
* corsFilter is provided, that {@link CorsFilter} is used. Else if
* corsConfigurationSource is defined, then that {@link CorsConfiguration} is used.
* Otherwise, if Spring MVC is on the classpath a {@link HandlerMappingIntrospector} is
* used.
*
* @param <H> the builder to return.
* @author Rob Winch
@@ -41,12 +44,20 @@ public class CorsConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHt
private static final String CORS_FILTER_BEAN_NAME = "corsFilter";
private static final String HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector";
private static final boolean mvcPresent;
private CorsConfigurationSource configurationSource;
static {
mvcPresent = ClassUtils.isPresent(HANDLER_MAPPING_INTROSPECTOR, CorsConfigurer.class.getClassLoader());
}
/**
* Creates a new instance
*
* @see HttpSecurity#cors(Customizer)
* @see HttpSecurity#cors()
*/
public CorsConfigurer() {
}
@@ -79,7 +90,10 @@ public class CorsConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHt
CorsConfigurationSource.class);
return new CorsFilter(configurationSource);
}
return MvcCorsFilter.getMvcCorsFilter(context);
if (mvcPresent) {
return MvcCorsFilter.getMvcCorsFilter(context);
}
return null;
}
static class MvcCorsFilter {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,15 +19,12 @@ package org.springframework.security.config.annotation.web.configurers;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.function.Supplier;
import io.micrometer.observation.ObservationRegistry;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.context.ApplicationContext;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -37,17 +34,13 @@ import org.springframework.security.web.access.CompositeAccessDeniedHandler;
import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
import org.springframework.security.web.access.ObservationMarkingAccessDeniedHandler;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfAuthenticationStrategy;
import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.security.web.csrf.CsrfLogoutHandler;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
import org.springframework.security.web.csrf.MissingCsrfTokenException;
import org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;
import org.springframework.security.web.session.InvalidSessionAccessDeniedHandler;
import org.springframework.security.web.session.InvalidSessionStrategy;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
@@ -55,7 +48,6 @@ import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Adds
@@ -104,7 +96,7 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Creates a new instance
* @see HttpSecurity#csrf(Customizer)
* @see HttpSecurity#csrf()
*/
public CsrfConfigurer(ApplicationContext context) {
this.context = context;
@@ -164,16 +156,16 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
*
* <pre>
* http
* .csrf((csrf) -&gt; csrf
* .ignoringRequestMatchers((request) -&gt; "XMLHttpRequest".equals(request.getHeader("X-Requested-With"))))
* .csrf()
* .ignoringRequestMatchers((request) -&gt; "XMLHttpRequest".equals(request.getHeader("X-Requested-With")))
* .and()
* ...
* </pre>
*
* @since 5.1
*/
public CsrfConfigurer<H> ignoringRequestMatchers(RequestMatcher... requestMatchers) {
new IgnoreCsrfProtectionRegistry(this.context).requestMatchers(requestMatchers);
return this;
return new IgnoreCsrfProtectionRegistry(this.context).requestMatchers(requestMatchers).and();
}
/**
@@ -192,8 +184,9 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
*
* <pre>
* http
* .csrf((csrf) -&gt; csrf
* .ignoringRequestMatchers("/sockjs/**"))
* .csrf()
* .ignoringRequestMatchers("/sockjs/**")
* .and()
* ...
* </pre>
*
@@ -201,8 +194,7 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
* @see AbstractRequestMatcherRegistry#requestMatchers(String...)
*/
public CsrfConfigurer<H> ignoringRequestMatchers(String... patterns) {
new IgnoreCsrfProtectionRegistry(this.context).requestMatchers(patterns);
return this;
return new IgnoreCsrfProtectionRegistry(this.context).requestMatchers(patterns).and();
}
/**
@@ -222,21 +214,6 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
/**
* <p>
* Sensible CSRF defaults when used in combination with a single page application.
* Creates a cookie-based token repository and a custom request handler to resolve the
* actual token value instead of the encoded token.
* </p>
* @return the {@link CsrfConfigurer} for further customizations
* @since 7.0
*/
public CsrfConfigurer<H> spa() {
this.csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
this.requestHandler = new SpaCsrfTokenRequestHandler();
return this;
}
@SuppressWarnings("unchecked")
@Override
public void configure(H http) {
@@ -386,6 +363,10 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
setApplicationContext(context);
}
CsrfConfigurer<H> and() {
return CsrfConfigurer.this;
}
@Override
protected IgnoreCsrfProtectionRegistry chainRequestMatchers(List<RequestMatcher> requestMatchers) {
CsrfConfigurer.this.ignoredCsrfProtectionMatchers.addAll(requestMatchers);
@@ -394,27 +375,4 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
}
private static final class SpaCsrfTokenRequestHandler implements CsrfTokenRequestHandler {
private final CsrfTokenRequestAttributeHandler plain = new CsrfTokenRequestAttributeHandler();
private final CsrfTokenRequestAttributeHandler xor = new XorCsrfTokenRequestAttributeHandler();
SpaCsrfTokenRequestHandler() {
this.xor.setCsrfRequestAttributeName(null);
}
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, Supplier<CsrfToken> csrfToken) {
this.xor.handle(request, response, csrfToken);
}
@Override
public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
String headerValue = request.getHeader(csrfToken.getHeaderName());
return (StringUtils.hasText(headerValue) ? this.plain : this.xor).resolveCsrfTokenValue(request, csrfToken);
}
}
}
@@ -18,7 +18,6 @@ package org.springframework.security.config.annotation.web.configurers;
import java.util.LinkedHashMap;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.AuthenticationEntryPoint;
@@ -77,7 +76,7 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Creates a new instance
* @see HttpSecurity#exceptionHandling(Customizer)
* @see HttpSecurity#exceptionHandling()
*/
public ExceptionHandlingConfigurer() {
}
@@ -29,7 +29,6 @@ import org.springframework.security.access.SecurityConfig;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -78,7 +77,7 @@ import org.springframework.util.StringUtils;
* @author Yanming Zhou
* @author Ngoc Nhan
* @since 3.2
* @see org.springframework.security.config.annotation.web.builders.HttpSecurity#authorizeRequests(Customizer)
* @see org.springframework.security.config.annotation.web.builders.HttpSecurity#authorizeRequests()
* @deprecated Use {@link AuthorizeHttpRequestsConfigurer} instead
*/
@Deprecated
@@ -105,7 +104,7 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
/**
* Creates a new instance
* @see HttpSecurity#authorizeRequests(Customizer)
* @see HttpSecurity#authorizeRequests()
*/
public ExpressionUrlAuthorizationConfigurer(ApplicationContext context) {
GrantedAuthorityDefaults grantedAuthorityDefaults = context.getBeanProvider(GrantedAuthorityDefaults.class)
@@ -251,7 +250,7 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
}
public H and() {
return ExpressionUrlAuthorizationConfigurer.this.getBuilder();
return ExpressionUrlAuthorizationConfigurer.this.and();
}
}
@@ -17,7 +17,6 @@
package org.springframework.security.config.annotation.web.configurers;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -75,7 +74,7 @@ public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
/**
* Creates a new instance
* @see HttpSecurity#formLogin(Customizer)
* @see HttpSecurity#formLogin()
*/
public FormLoginConfigurer() {
super(new UsernamePasswordAuthenticationFilter(), null);
@@ -111,7 +111,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Creates a new instance
*
* @see HttpSecurity#headers(Customizer)
* @see HttpSecurity#headers()
*/
public HeadersConfigurer() {
}
@@ -127,6 +127,26 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
/**
* Configures the {@link XContentTypeOptionsHeaderWriter} which inserts the
* <a href= "https://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx"
* >X-Content-Type-Options</a>:
*
* <pre>
* X-Content-Type-Options: nosniff
* </pre>
* @return the {@link ContentTypeOptionsConfig} for additional customizations
* @deprecated For removal in 7.0. Use {@link #contentTypeOptions(Customizer)} or
* {@code contentTypeOptions(Customizer.withDefaults())} to stick with defaults. See
* the <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public ContentTypeOptionsConfig contentTypeOptions() {
return this.contentTypeOptions.enable();
}
/**
* Configures the {@link XContentTypeOptionsHeaderWriter} which inserts the
* <a href= "https://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx"
@@ -144,6 +164,26 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return HeadersConfigurer.this;
}
/**
* <strong>Note this is not comprehensive XSS protection!</strong>
*
* <p>
* Allows customizing the {@link XXssProtectionHeaderWriter} which adds the <a href=
* "https://web.archive.org/web/20160201174302/https://blogs.msdn.com/b/ieinternals/archive/2011/01/31/controlling-the-internet-explorer-xss-filter-with-the-x-xss-protection-http-header.aspx"
* >X-XSS-Protection header</a>
* </p>
* @return the {@link XXssConfig} for additional customizations
* @deprecated For removal in 7.0. Use {@link #xssProtection(Customizer)} or
* {@code xssProtection(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public XXssConfig xssProtection() {
return this.xssProtection.enable();
}
/**
* <strong>Note this is not comprehensive XSS protection!</strong>
*
@@ -161,6 +201,26 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return HeadersConfigurer.this;
}
/**
* Allows customizing the {@link CacheControlHeadersWriter}. Specifically it adds the
* following headers:
* <ul>
* <li>Cache-Control: no-cache, no-store, max-age=0, must-revalidate</li>
* <li>Pragma: no-cache</li>
* <li>Expires: 0</li>
* </ul>
* @return the {@link CacheControlConfig} for additional customizations
* @deprecated For removal in 7.0. Use {@link #cacheControl(Customizer)} or
* {@code cacheControl(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public CacheControlConfig cacheControl() {
return this.cacheControl.enable();
}
/**
* Allows customizing the {@link CacheControlHeadersWriter}. Specifically it adds the
* following headers:
@@ -178,6 +238,19 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return HeadersConfigurer.this;
}
/**
* Allows customizing the {@link HstsHeaderWriter} which provides support for
* <a href="https://tools.ietf.org/html/rfc6797">HTTP Strict Transport Security
* (HSTS)</a>.
* @return the {@link HstsConfig} for additional customizations
* @deprecated For removal in 7.0. Use
* {@link #httpStrictTransportSecurity(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HstsConfig httpStrictTransportSecurity() {
return this.hsts.enable();
}
/**
* Allows customizing the {@link HstsHeaderWriter} which provides support for
* <a href="https://tools.ietf.org/html/rfc6797">HTTP Strict Transport Security
@@ -191,6 +264,20 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return HeadersConfigurer.this;
}
/**
* Allows customizing the {@link XFrameOptionsHeaderWriter}.
* @return the {@link FrameOptionsConfig} for additional customizations
* @deprecated For removal in 7.0. Use {@link #frameOptions(Customizer)} or
* {@code frameOptions(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public FrameOptionsConfig frameOptions() {
return this.frameOptions.enable();
}
/**
* Allows customizing the {@link XFrameOptionsHeaderWriter}.
* @param frameOptionsCustomizer the {@link Customizer} to provide more options for
@@ -202,6 +289,21 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return HeadersConfigurer.this;
}
/**
* Allows customizing the {@link HpkpHeaderWriter} which provides support for
* <a href="https://tools.ietf.org/html/rfc7469">HTTP Public Key Pinning (HPKP)</a>.
* @return the {@link HpkpConfig} for additional customizations
*
* @since 4.1
* @deprecated see <a href=
* "https://owasp.org/www-community/controls/Certificate_and_Public_Key_Pinning">Certificate
* and Public Key Pinning</a> for more context
*/
@Deprecated
public HpkpConfig httpPublicKeyPinning() {
return this.hpkp.enable();
}
/**
* Allows customizing the {@link HpkpHeaderWriter} which provides support for
* <a href="https://tools.ietf.org/html/rfc7469">HTTP Public Key Pinning (HPKP)</a>.
@@ -218,6 +320,39 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return HeadersConfigurer.this;
}
/**
* <p>
* Allows configuration for <a href="https://www.w3.org/TR/CSP2/">Content Security
* Policy (CSP) Level 2</a>.
* </p>
*
* <p>
* Calling this method automatically enables (includes) the Content-Security-Policy
* header in the response using the supplied security policy directive(s).
* </p>
*
* <p>
* Configuration is provided to the {@link ContentSecurityPolicyHeaderWriter} which
* supports the writing of the two headers as detailed in the W3C Candidate
* Recommendation:
* </p>
* <ul>
* <li>Content-Security-Policy</li>
* <li>Content-Security-Policy-Report-Only</li>
* </ul>
* @return the {@link ContentSecurityPolicyConfig} for additional configuration
* @throws IllegalArgumentException if policyDirectives is null or empty
* @since 4.1
* @deprecated For removal in 7.0. Use {@link #contentSecurityPolicy(Customizer)}
* instead
* @see ContentSecurityPolicyHeaderWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public ContentSecurityPolicyConfig contentSecurityPolicy(String policyDirectives) {
this.contentSecurityPolicy.writer = new ContentSecurityPolicyHeaderWriter(policyDirectives);
return this.contentSecurityPolicy;
}
/**
* <p>
* Allows configuration for <a href="https://www.w3.org/TR/CSP2/">Content Security
@@ -319,6 +454,71 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
}
}
/**
* <p>
* Allows configuration for <a href="https://www.w3.org/TR/referrer-policy/">Referrer
* Policy</a>.
* </p>
*
* <p>
* Configuration is provided to the {@link ReferrerPolicyHeaderWriter} which support
* the writing of the header as detailed in the W3C Technical Report:
* </p>
* <ul>
* <li>Referrer-Policy</li>
* </ul>
*
* <p>
* Default value is:
* </p>
*
* <pre>
* Referrer-Policy: no-referrer
* </pre>
* @return the {@link ReferrerPolicyConfig} for additional configuration
* @since 4.2
* @deprecated For removal in 7.0. Use {@link #referrerPolicy(Customizer)} or
* {@code referrerPolicy(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
* @see ReferrerPolicyHeaderWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public ReferrerPolicyConfig referrerPolicy() {
this.referrerPolicy.writer = new ReferrerPolicyHeaderWriter();
return this.referrerPolicy;
}
/**
* <p>
* Allows configuration for <a href="https://www.w3.org/TR/referrer-policy/">Referrer
* Policy</a>.
* </p>
*
* <p>
* Configuration is provided to the {@link ReferrerPolicyHeaderWriter} which support
* the writing of the header as detailed in the W3C Technical Report:
* </p>
* <ul>
* <li>Referrer-Policy</li>
* </ul>
* @return the {@link ReferrerPolicyConfig} for additional configuration
* @throws IllegalArgumentException if policy is null or empty
* @since 4.2
* @deprecated For removal in 7.0. Use {@link #referrerPolicy(Customizer)} or
* {@code referrerPolicy(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
* @see ReferrerPolicyHeaderWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public ReferrerPolicyConfig referrerPolicy(ReferrerPolicy policy) {
this.referrerPolicy.writer = new ReferrerPolicyHeaderWriter(policy);
return this.referrerPolicy;
}
/**
* <p>
* Allows configuration for <a href="https://www.w3.org/TR/referrer-policy/">Referrer
@@ -368,6 +568,35 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return this.featurePolicy;
}
/**
* <p>
* Allows configuration for
* <a href="https://w3c.github.io/webappsec-permissions-policy/">Permissions
* Policy</a>.
* </p>
*
* <p>
* Configuration is provided to the {@link PermissionsPolicyHeaderWriter} which
* support the writing of the header as detailed in the W3C Technical Report:
* </p>
* <ul>
* <li>Permissions-Policy</li>
* </ul>
* @return the {@link PermissionsPolicyConfig} for additional configuration
* @since 5.5
* @deprecated For removal in 7.0. Use {@link #permissionsPolicyHeader(Customizer)} or
* {@code permissionsPolicy(Customizer.withDefaults())} to stick with defaults. See
* the <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
* @see PermissionsPolicyHeaderWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public PermissionsPolicyConfig permissionsPolicy() {
this.permissionsPolicy.writer = new PermissionsPolicyHeaderWriter();
return this.permissionsPolicy;
}
/**
* Allows configuration for
* <a href="https://w3c.github.io/webappsec-permissions-policy/"> Permissions
@@ -414,6 +643,26 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
/**
* Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
* Cross-Origin-Opener-Policy</a> header.
* <p>
* Configuration is provided to the {@link CrossOriginOpenerPolicyHeaderWriter} which
* responsible for writing the header.
* </p>
* @return the {@link CrossOriginOpenerPolicyConfig} for additional confniguration
* @since 5.7
* @deprecated For removal in 7.0. Use {@link #crossOriginOpenerPolicy(Customizer)}
* instead
* @see CrossOriginOpenerPolicyHeaderWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public CrossOriginOpenerPolicyConfig crossOriginOpenerPolicy() {
this.crossOriginOpenerPolicy.writer = new CrossOriginOpenerPolicyHeaderWriter();
return this.crossOriginOpenerPolicy;
}
/**
* Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
@@ -438,6 +687,26 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return HeadersConfigurer.this;
}
/**
* Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
* Cross-Origin-Embedder-Policy</a> header.
* <p>
* Configuration is provided to the {@link CrossOriginEmbedderPolicyHeaderWriter}
* which is responsible for writing the header.
* </p>
* @return the {@link CrossOriginEmbedderPolicyConfig} for additional customizations
* @since 5.7
* @deprecated For removal in 7.0. Use {@link #crossOriginEmbedderPolicy(Customizer)}
* instead
* @see CrossOriginEmbedderPolicyHeaderWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public CrossOriginEmbedderPolicyConfig crossOriginEmbedderPolicy() {
this.crossOriginEmbedderPolicy.writer = new CrossOriginEmbedderPolicyHeaderWriter();
return this.crossOriginEmbedderPolicy;
}
/**
* Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
@@ -462,6 +731,26 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return HeadersConfigurer.this;
}
/**
* Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
* Cross-Origin-Resource-Policy</a> header.
* <p>
* Configuration is provided to the {@link CrossOriginResourcePolicyHeaderWriter}
* which is responsible for writing the header:
* </p>
* @return the {@link HeadersConfigurer} for additional customizations
* @since 5.7
* @deprecated For removal in 7.0. Use {@link #crossOriginResourcePolicy(Customizer)}
* instead
* @see CrossOriginResourcePolicyHeaderWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public CrossOriginResourcePolicyConfig crossOriginResourcePolicy() {
this.crossOriginResourcePolicy.writer = new CrossOriginResourcePolicyHeaderWriter();
return this.crossOriginResourcePolicy;
}
/**
* Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
@@ -500,6 +789,17 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
*/
public HeadersConfigurer<H> disable() {
this.writer = null;
return and();
}
/**
* Allows customizing the {@link HeadersConfigurer}
* @return the {@link HeadersConfigurer} for additional customization
* @deprecated For removal in 7.0. Use {@link #contentTypeOptions(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
@@ -564,6 +864,21 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
*/
public HeadersConfigurer<H> disable() {
this.writer = null;
return and();
}
/**
* Allows completing configuration of X-XSS-Protection and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use {@link #xssProtection(Customizer)} or
* {@code xssProtection(Customizer.withDefaults())} to stick with defaults. See
* the <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
@@ -597,6 +912,21 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return HeadersConfigurer.this;
}
/**
* Allows completing configuration of Cache Control and continuing configuration
* of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use {@link #cacheControl(Customizer)} or
* {@code cacheControl(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
/**
* Ensures the Cache Control headers are enabled if they are not already.
* @return the {@link CacheControlConfig} for additional customization
@@ -694,6 +1024,18 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return HeadersConfigurer.this;
}
/**
* Allows completing configuration of Strict Transport Security and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use
* {@link #httpStrictTransportSecurity(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
/**
* Ensures that Strict-Transport-Security is enabled if it is not already
* @return the {@link HstsConfig} for additional customization
@@ -721,7 +1063,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
*/
public HeadersConfigurer<H> deny() {
this.writer = new XFrameOptionsHeaderWriter(XFrameOptionsMode.DENY);
return HeadersConfigurer.this;
return and();
}
/**
@@ -735,7 +1077,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
*/
public HeadersConfigurer<H> sameOrigin() {
this.writer = new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN);
return HeadersConfigurer.this;
return and();
}
/**
@@ -744,6 +1086,20 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
*/
public HeadersConfigurer<H> disable() {
this.writer = null;
return and();
}
/**
* Allows continuing customizing the headers configuration.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use {@link #frameOptions(Customizer)} or
* {@code frameOptions(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
@@ -961,6 +1317,18 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
/**
* Allows completing configuration of Content Security Policy and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use {@link #contentSecurityPolicy(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
}
public final class ReferrerPolicyConfig {
@@ -981,6 +1349,18 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
/**
* @deprecated For removal in 7.0. Use {@link #referrerPolicy(Customizer)} or
* {@code referrerPolicy(Customizer.withDefaults())} to stick with defaults. See
* the <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
}
public final class FeaturePolicyConfig {
@@ -1019,6 +1399,18 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
/**
* Allows completing configuration of Permissions Policy and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use {@link #permissionsPolicy(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
}
public final class CrossOriginOpenerPolicyConfig {
@@ -1040,6 +1432,18 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
/**
* Allows completing configuration of Cross Origin Opener Policy and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use
* {@link #crossOriginOpenerPolicy(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
}
public final class CrossOriginEmbedderPolicyConfig {
@@ -1062,6 +1466,18 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
/**
* Allows completing configuration of Cross-Origin-Embedder-Policy and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use
* {@link #crossOriginEmbedderPolicy(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
}
public final class CrossOriginResourcePolicyConfig {
@@ -1084,6 +1500,18 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
/**
* Allows completing configuration of Cross-Origin-Resource-Policy and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use
* {@link #crossOriginResourcePolicy(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
}
}
@@ -26,7 +26,6 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.AuthenticationEntryPoint;
@@ -99,7 +98,7 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>>
/**
* Creates a new instance
* @see HttpSecurity#httpBasic(Customizer)
* @see HttpSecurity#httpBasic()
*/
public HttpBasicConfigurer() {
realmName(DEFAULT_REALM);
@@ -22,7 +22,6 @@ import java.util.Set;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.authority.mapping.SimpleMappableAttributesRetriever;
@@ -78,7 +77,7 @@ public final class JeeConfigurer<H extends HttpSecurityBuilder<H>> extends Abstr
/**
* Creates a new instance
* @see HttpSecurity#jee(Customizer)
* @see HttpSecurity#jee()
*/
public JeeConfigurer() {
}
@@ -23,7 +23,6 @@ import java.util.List;
import jakarta.servlet.http.HttpSession;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
@@ -93,7 +92,7 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Creates a new instance
* @see HttpSecurity#logout(Customizer)
* @see HttpSecurity#logout()
*/
public LogoutConfigurer() {
}
@@ -151,7 +150,7 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>>
* @param logoutUrl the URL that will invoke logout.
* @return the {@link LogoutConfigurer} for further customization
* @see #logoutRequestMatcher(RequestMatcher)
* @see HttpSecurity#csrf(Customizer)
* @see HttpSecurity#csrf()
*/
public LogoutConfigurer<H> logoutUrl(String logoutUrl) {
this.logoutRequestMatcher = null;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@ import org.springframework.security.web.savedrequest.NullRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
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.RequestHeaderRequestMatcher;
@@ -144,9 +145,6 @@ public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>>
RequestMatcher notFavIcon = new NegatedRequestMatcher(getFaviconRequestMatcher());
RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
RequestMatcher notWebSocket = new NegatedRequestMatcher(
new RequestHeaderRequestMatcher("Upgrade", "websocket"));
boolean isCsrfEnabled = http.getConfigurer(CsrfConfigurer.class) != null;
List<RequestMatcher> matchers = new ArrayList<>();
if (isCsrfEnabled) {
@@ -158,7 +156,6 @@ public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>>
matchers.add(notXRequestedWith);
matchers.add(notMatchingMediaType(http, MediaType.MULTIPART_FORM_DATA));
matchers.add(notMatchingMediaType(http, MediaType.TEXT_EVENT_STREAM));
matchers.add(notWebSocket);
return new AndRequestMatcher(matchers);
}
@@ -173,7 +170,12 @@ public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>>
}
private RequestMatcher getFaviconRequestMatcher() {
return RequestMatcherFactory.matcher("/favicon.*");
if (RequestMatcherFactory.usesPathPatterns()) {
return RequestMatcherFactory.matcher("/favicon.*");
}
else {
return new AntPathRequestMatcher("/**/favicon.*");
}
}
}
@@ -16,7 +16,6 @@
package org.springframework.security.config.annotation.web.configurers;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
@@ -71,7 +70,7 @@ public final class SecurityContextConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Creates a new instance
* @see HttpSecurity#securityContext(Customizer)
* @see HttpSecurity#securityContext()
*/
public SecurityContextConfigurer() {
}
@@ -23,7 +23,6 @@ import jakarta.servlet.http.HttpServletRequest;
import org.springframework.context.ApplicationContext;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
@@ -67,7 +66,7 @@ public final class ServletApiConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Creates a new instance
* @see HttpSecurity#servletApi(Customizer)
* @see HttpSecurity#servletApi()
*/
public ServletApiConfigurer() {
}
@@ -152,7 +152,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Creates a new instance
* @see HttpSecurity#sessionManagement(Customizer)
* @see HttpSecurity#sessionManagement()
*/
public SessionManagementConfigurer() {
}
@@ -775,6 +775,17 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
/**
* Used to chain back to the {@link SessionManagementConfigurer}
* @return the {@link SessionManagementConfigurer} for further customizations
* @deprecated For removal in 7.0. Use {@link #sessionConcurrency(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public SessionManagementConfigurer<H> and() {
return SessionManagementConfigurer.this;
}
}
}
@@ -248,7 +248,7 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
}
public H and() {
return UrlAuthorizationConfigurer.this.getBuilder();
return UrlAuthorizationConfigurer.this.and();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,7 +21,6 @@ import jakarta.servlet.http.HttpServletRequest;
import org.springframework.context.ApplicationContext;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.Authentication;
@@ -91,7 +90,7 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>>
/**
* Creates a new instance
*
* @see HttpSecurity#x509(Customizer)
* @see HttpSecurity#x509()
*/
public X509Configurer() {
}
@@ -162,10 +161,7 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>>
* @param subjectPrincipalRegex the regex to extract the user principal from the
* certificate (i.e. "CN=(.*?)(?:,|$)").
* @return the {@link X509Configurer} for further customizations
* @deprecated Please use {{@link #x509PrincipalExtractor(X509PrincipalExtractor)}
* instead
*/
@Deprecated
public X509Configurer<H> subjectPrincipalRegex(String subjectPrincipalRegex) {
SubjectDnX509PrincipalExtractor principalExtractor = new SubjectDnX509PrincipalExtractor();
principalExtractor.setSubjectDnRegex(subjectPrincipalRegex);
@@ -141,6 +141,18 @@ public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>>
return this;
}
/**
* Returns the {@link AuthorizationCodeGrantConfigurer} for configuring the OAuth 2.0
* Authorization Code Grant.
* @return the {@link AuthorizationCodeGrantConfigurer}
* @deprecated For removal in 7.0. Use {@link #authorizationCodeGrant(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public AuthorizationCodeGrantConfigurer authorizationCodeGrant() {
return this.authorizationCodeGrantConfigurer;
}
/**
* Configures the OAuth 2.0 Authorization Code Grant.
* @param authorizationCodeGrantCustomizer the {@link Customizer} to provide more
@@ -230,6 +242,17 @@ public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>>
return this;
}
/**
* Returns the {@link OAuth2ClientConfigurer} for further configuration.
* @return the {@link OAuth2ClientConfigurer}
* @deprecated For removal in 7.0. Use {@link #authorizationCodeGrant(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2ClientConfigurer<B> and() {
return OAuth2ClientConfigurer.this;
}
private void init(B builder) {
OAuth2AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider = new OAuth2AuthorizationCodeAuthenticationProvider(
getAccessTokenResponseClient());
@@ -155,7 +155,7 @@ import org.springframework.util.ReflectionUtils;
* @author Kazuki Shimizu
* @author Ngoc Nhan
* @since 5.0
* @see HttpSecurity#oauth2Login(Customizer)
* @see HttpSecurity#oauth2Login()
* @see OAuth2AuthorizationRequestRedirectFilter
* @see OAuth2LoginAuthenticationFilter
* @see ClientRegistrationRepository
@@ -181,6 +181,8 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
private OAuth2AuthorizedClientRepository authorizedClientRepository;
private SecurityContextRepository securityContextRepository;
/**
* Sets the repository of client registrations.
* @param clientRegistrationRepository the repository of client registrations
@@ -234,6 +236,17 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
return this;
}
/**
* Sets the {@link SecurityContextRepository} to use.
* @param securityContextRepository the {@link SecurityContextRepository} to use
* @return the {@link OAuth2LoginConfigurer} for further configuration
*/
@Override
public OAuth2LoginConfigurer<B> securityContextRepository(SecurityContextRepository securityContextRepository) {
this.securityContextRepository = securityContextRepository;
return this;
}
/**
* Sets the registry for managing the OIDC client-provider session link
* @param oidcSessionRegistry the {@link OidcSessionRegistry} to use
@@ -246,6 +259,18 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
return this;
}
/**
* Returns the {@link AuthorizationEndpointConfig} for configuring the Authorization
* Server's Authorization Endpoint.
* @return the {@link AuthorizationEndpointConfig}
* @deprecated For removal in 7.0. Use {@link #authorizationEndpoint(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public AuthorizationEndpointConfig authorizationEndpoint() {
return this.authorizationEndpointConfig;
}
/**
* Configures the Authorization Server's Authorization Endpoint.
* @param authorizationEndpointCustomizer the {@link Customizer} to provide more
@@ -258,6 +283,21 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
return this;
}
/**
* Returns the {@link TokenEndpointConfig} for configuring the Authorization Server's
* Token Endpoint.
* @return the {@link TokenEndpointConfig}
* @deprecated For removal in 7.0. Use {@link #tokenEndpoint(Customizer)} or
* {@code tokenEndpoint(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public TokenEndpointConfig tokenEndpoint() {
return this.tokenEndpointConfig;
}
/**
* Configures the Authorization Server's Token Endpoint.
* @param tokenEndpointCustomizer the {@link Customizer} to provide more options for
@@ -270,6 +310,18 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
return this;
}
/**
* Returns the {@link RedirectionEndpointConfig} for configuring the Client's
* Redirection Endpoint.
* @return the {@link RedirectionEndpointConfig}
* @deprecated For removal in 7.0. Use {@link #redirectionEndpoint(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public RedirectionEndpointConfig redirectionEndpoint() {
return this.redirectionEndpointConfig;
}
/**
* Configures the Client's Redirection Endpoint.
* @param redirectionEndpointCustomizer the {@link Customizer} to provide more options
@@ -282,6 +334,21 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
return this;
}
/**
* Returns the {@link UserInfoEndpointConfig} for configuring the Authorization
* Server's UserInfo Endpoint.
* @return the {@link UserInfoEndpointConfig}
* @deprecated For removal in 7.0. Use {@link #userInfoEndpoint(Customizer)} or
* {@code userInfoEndpoint(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public UserInfoEndpointConfig userInfoEndpoint() {
return this.userInfoEndpointConfig;
}
/**
* Configures the Authorization Server's UserInfo Endpoint.
* @param userInfoEndpointCustomizer the {@link Customizer} to provide more options
@@ -300,6 +367,9 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
RequestMatcher processUri = RequestMatcherFactory.matcher(this.loginProcessingUrl);
authenticationFilter.setRequiresAuthenticationRequestMatcher(processUri);
authenticationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
if (this.securityContextRepository != null) {
authenticationFilter.setSecurityContextRepository(this.securityContextRepository);
}
this.setAuthenticationFilter(authenticationFilter);
super.loginProcessingUrl(this.loginProcessingUrl);
if (this.loginPage != null) {
@@ -668,6 +738,17 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
return this;
}
/**
* Returns the {@link OAuth2LoginConfigurer} for further configuration.
* @return the {@link OAuth2LoginConfigurer}
* @deprecated For removal in 7.0. Use {@link #authorizationEndpoint(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2LoginConfigurer<B> and() {
return OAuth2LoginConfigurer.this;
}
}
/**
@@ -694,6 +775,20 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
return this;
}
/**
* Returns the {@link OAuth2LoginConfigurer} for further configuration.
* @return the {@link OAuth2LoginConfigurer}
* @deprecated For removal in 7.0. Use {@link #tokenEndpoint(Customizer)} or
* {@code tokenEndpoint(Customizer.withDefaults())} to stick with defaults. See
* the <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2LoginConfigurer<B> and() {
return OAuth2LoginConfigurer.this;
}
}
/**
@@ -718,6 +813,17 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
return this;
}
/**
* Returns the {@link OAuth2LoginConfigurer} for further configuration.
* @return the {@link OAuth2LoginConfigurer}
* @deprecated For removal in 7.0. Use {@link #redirectionEndpoint(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2LoginConfigurer<B> and() {
return OAuth2LoginConfigurer.this;
}
}
/**
@@ -772,6 +878,17 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
return this;
}
/**
* Returns the {@link OAuth2LoginConfigurer} for further configuration.
* @return the {@link OAuth2LoginConfigurer}
* @deprecated For removal in 7.0. Use {@link #userInfoEndpoint(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2LoginConfigurer<B> and() {
return OAuth2LoginConfigurer.this;
}
}
private static class OidcAuthenticationRequestChecker implements AuthenticationProvider {
@@ -18,6 +18,11 @@ package org.springframework.security.config.annotation.web.configurers.oauth2.cl
import java.util.function.Function;
import com.nimbusds.jose.JOSEObjectType;
import com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier;
import com.nimbusds.jose.proc.JOSEObjectTypeVerifier;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
@@ -33,7 +38,6 @@ import org.springframework.security.oauth2.jwt.BadJwtException;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
import org.springframework.security.oauth2.jwt.JwtTypeValidator;
import org.springframework.security.oauth2.jwt.JwtValidators;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.util.Assert;
@@ -63,10 +67,8 @@ final class OidcBackChannelLogoutAuthenticationProvider implements Authenticatio
* Construct an {@link OidcBackChannelLogoutAuthenticationProvider}
*/
OidcBackChannelLogoutAuthenticationProvider() {
JwtTypeValidator type = new JwtTypeValidator("JWT", "logout+jwt");
type.setAllowEmpty(true);
Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidator = (clientRegistration) -> JwtValidators
.createDefaultWithValidators(type, new OidcBackChannelLogoutTokenValidator(clientRegistration));
.createDefaultWithValidators(new OidcBackChannelLogoutTokenValidator(clientRegistration));
this.logoutTokenDecoderFactory = (clientRegistration) -> {
String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
if (!StringUtils.hasText(jwkSetUri)) {
@@ -77,7 +79,11 @@ final class OidcBackChannelLogoutAuthenticationProvider implements Authenticatio
null);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
}
NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build();
JOSEObjectTypeVerifier<SecurityContext> typeVerifier = new DefaultJOSEObjectTypeVerifier<>(null,
JOSEObjectType.JWT, new JOSEObjectType("logout+jwt"));
NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri)
.jwtProcessorCustomizer((processor) -> processor.setJWSTypeVerifier(typeVerifier))
.build();
decoder.setJwtValidator(jwtValidator.apply(clientRegistration));
decoder.setClaimSetConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverter());
return decoder;
@@ -111,6 +111,11 @@ public final class OidcLogoutConfigurer<B extends HttpSecurityBuilder<B>>
return this;
}
@Deprecated(forRemoval = true, since = "6.2")
public B and() {
return getBuilder();
}
@Override
public void configure(B builder) throws Exception {
if (this.backChannel != null) {
@@ -37,7 +37,6 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHt
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
@@ -50,14 +49,13 @@ import org.springframework.security.oauth2.server.resource.introspection.OpaqueT
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.csrf.CsrfException;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
@@ -158,7 +156,7 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
private AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
private AuthenticationConverter authenticationConverter;
private BearerTokenResolver bearerTokenResolver;
private JwtConfigurer jwtConfigurer;
@@ -198,20 +196,22 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
public OAuth2ResourceServerConfigurer<H> bearerTokenResolver(BearerTokenResolver bearerTokenResolver) {
Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null");
this.authenticationConverter = new BearerTokenResolverHoldingAuthenticationConverter(bearerTokenResolver);
this.bearerTokenResolver = bearerTokenResolver;
return this;
}
/**
* Sets the {@link AuthenticationConverter} to use.
* @param authenticationConverter the authentication converter
* @return the {@link OAuth2ResourceServerConfigurer} for further configuration
* @since 7.0
* @deprecated For removal in 7.0. Use {@link #jwt(Customizer)} or
* {@code jwt(Customizer.withDefaults())} to stick with defaults. See the <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
public OAuth2ResourceServerConfigurer<H> authenticationConverter(AuthenticationConverter authenticationConverter) {
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
this.authenticationConverter = authenticationConverter;
return this;
@Deprecated(since = "6.1", forRemoval = true)
public JwtConfigurer jwt() {
if (this.jwtConfigurer == null) {
this.jwtConfigurer = new JwtConfigurer(this.context);
}
return this.jwtConfigurer;
}
/**
@@ -228,6 +228,21 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
return this;
}
/**
* @deprecated For removal in 7.0. Use {@link #opaqueToken(Customizer)} or
* {@code opaqueToken(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public OpaqueTokenConfigurer opaqueToken() {
if (this.opaqueTokenConfigurer == null) {
this.opaqueTokenConfigurer = new OpaqueTokenConfigurer(this.context);
}
return this.opaqueTokenConfigurer;
}
/**
* Enables opaque bearer token support.
* @param opaqueTokenCustomizer the {@link Customizer} to provide more options for the
@@ -256,15 +271,16 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
@Override
public void configure(H http) {
BearerTokenResolver bearerTokenResolver = getBearerTokenResolver();
this.requestMatcher.setBearerTokenResolver(bearerTokenResolver);
AuthenticationManagerResolver resolver = this.authenticationManagerResolver;
if (resolver == null) {
AuthenticationManager authenticationManager = getAuthenticationManager(http);
resolver = (request) -> authenticationManager;
}
AuthenticationConverter converter = getAuthenticationConverter();
this.requestMatcher.setAuthenticationConverter(converter);
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver, converter);
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver);
filter.setBearerTokenResolver(bearerTokenResolver);
filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
filter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
filter = postProcess(filter);
@@ -351,29 +367,16 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
return this.authenticationManagerResolver;
}
AuthenticationConverter getAuthenticationConverter() {
if (this.authenticationConverter != null) {
return this.authenticationConverter;
}
if (this.context.getBeanNamesForType(AuthenticationConverter.class).length > 0) {
this.authenticationConverter = this.context.getBean(AuthenticationConverter.class);
}
else if (this.context.getBeanNamesForType(BearerTokenResolver.class).length > 0) {
BearerTokenResolver bearerTokenResolver = this.context.getBean(BearerTokenResolver.class);
this.authenticationConverter = new BearerTokenResolverHoldingAuthenticationConverter(bearerTokenResolver);
}
else {
this.authenticationConverter = new BearerTokenAuthenticationConverter();
}
return this.authenticationConverter;
}
BearerTokenResolver getBearerTokenResolver() {
AuthenticationConverter authenticationConverter = getAuthenticationConverter();
if (authenticationConverter instanceof OAuth2ResourceServerConfigurer.BearerTokenResolverHoldingAuthenticationConverter bearer) {
return bearer.bearerTokenResolver;
if (this.bearerTokenResolver == null) {
if (this.context.getBeanNamesForType(BearerTokenResolver.class).length > 0) {
this.bearerTokenResolver = this.context.getBean(BearerTokenResolver.class);
}
else {
this.bearerTokenResolver = new DefaultBearerTokenResolver();
}
}
return null;
return this.bearerTokenResolver;
}
public class JwtConfigurer {
@@ -412,6 +415,17 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
return this;
}
/**
* @deprecated For removal in 7.0. Use {@link #jwt(Customizer)} or
* {@code jwt(Customizer.withDefaults())} to stick with defaults. See the <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2ResourceServerConfigurer<H> and() {
return OAuth2ResourceServerConfigurer.this;
}
Converter<Jwt, ? extends AbstractAuthenticationToken> getJwtAuthenticationConverter() {
if (this.jwtAuthenticationConverter == null) {
if (this.context.getBeanNamesForType(JwtAuthenticationConverter.class).length > 0) {
@@ -550,41 +564,21 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
private static final class BearerTokenRequestMatcher implements RequestMatcher {
private AuthenticationConverter authenticationConverter;
private BearerTokenResolver bearerTokenResolver;
@Override
public boolean matches(HttpServletRequest request) {
try {
return this.authenticationConverter.convert(request) != null;
return this.bearerTokenResolver.resolve(request) != null;
}
catch (OAuth2AuthenticationException ex) {
return false;
}
}
void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
this.authenticationConverter = authenticationConverter;
}
}
private static final class BearerTokenResolverHoldingAuthenticationConverter implements AuthenticationConverter {
private final BearerTokenResolver bearerTokenResolver;
private final AuthenticationConverter authenticationConverter;
BearerTokenResolverHoldingAuthenticationConverter(BearerTokenResolver bearerTokenResolver) {
this.bearerTokenResolver = bearerTokenResolver;
BearerTokenAuthenticationConverter authenticationConverter = new BearerTokenAuthenticationConverter();
authenticationConverter.setBearerTokenResolver(bearerTokenResolver);
this.authenticationConverter = authenticationConverter;
}
@Override
public Authentication convert(HttpServletRequest request) {
return this.authenticationConverter.convert(request);
void setBearerTokenResolver(BearerTokenResolver tokenResolver) {
Assert.notNull(tokenResolver, "resolver cannot be null");
this.bearerTokenResolver = tokenResolver;
}
}
@@ -27,7 +27,6 @@ import org.opensaml.core.Version;
import org.springframework.context.ApplicationContext;
import org.springframework.security.authentication.AuthenticationManager;
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;
@@ -112,7 +111,7 @@ import org.springframework.util.StringUtils;
* </ul>
*
* @since 5.2
* @see HttpSecurity#saml2Login(Customizer)
* @see HttpSecurity#saml2Login()
* @see Saml2WebSsoAuthenticationFilter
* @see Saml2WebSsoAuthenticationRequestFilter
* @see RelyingPartyRegistrationRepository
@@ -34,8 +34,6 @@ import org.springframework.security.config.annotation.web.configurers.LogoutConf
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
import org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertionAccessor;
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml4LogoutRequestValidator;
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml4LogoutResponseValidator;
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml5LogoutRequestValidator;
@@ -135,7 +133,7 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Creates a new instance
* @see HttpSecurity#logout(Customizer)
* @see HttpSecurity#logout()
*/
public Saml2LogoutConfigurer(ApplicationContext context) {
this.context = context;
@@ -158,7 +156,7 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
* @param logoutUrl the URL that will invoke logout
* @return the {@link LogoutConfigurer} for further customizations
* @see LogoutConfigurer#logoutUrl(String)
* @see HttpSecurity#csrf(Customizer)
* @see HttpSecurity#csrf()
*/
public Saml2LogoutConfigurer<H> logoutUrl(String logoutUrl) {
this.logoutUrl = logoutUrl;
@@ -177,6 +175,20 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
/**
* Get configurer for SAML 2.0 Logout Request components
* @return the {@link LogoutRequestConfigurer} for further customizations
* @deprecated For removal in 7.0. Use {@link #logoutRequest(Customizer)} or
* {@code logoutRequest(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public LogoutRequestConfigurer logoutRequest() {
return this.logoutRequestConfigurer;
}
/**
* Configures SAML 2.0 Logout Request components
* @param logoutRequestConfigurerCustomizer the {@link Customizer} to provide more
@@ -189,6 +201,20 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
/**
* Get configurer for SAML 2.0 Logout Response components
* @return the {@link LogoutResponseConfigurer} for further customizations
* @deprecated For removal in 7.0. Use {@link #logoutResponse(Customizer)} or
* {@code logoutResponse(Customizer.withDefaults())} to stick with defaults. See the
* <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public LogoutResponseConfigurer logoutResponse() {
return this.logoutResponseConfigurer;
}
/**
* Configures SAML 2.0 Logout Response components
* @param logoutResponseConfigurerCustomizer the {@link Customizer} to provide more
@@ -378,6 +404,18 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
/**
* @deprecated For removal in 7.0. Use {@link #logoutRequest(Customizer)} or
* {@code logoutRequest(Customizer.withDefaults())} to stick with defaults. See
* the <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public Saml2LogoutConfigurer<H> and() {
return Saml2LogoutConfigurer.this;
}
private Saml2LogoutRequestValidator logoutRequestValidator() {
if (this.logoutRequestValidator != null) {
return this.logoutRequestValidator;
@@ -448,6 +486,18 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
/**
* @deprecated For removal in 7.0. Use {@link #logoutResponse(Customizer)} or
* {@code logoutResponse(Customizer.withDefaults())} to stick with defaults. See
* the <a href=
* "https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl">documentation</a>
* for more details.
*/
@Deprecated(since = "6.1", forRemoval = true)
public Saml2LogoutConfigurer<H> and() {
return Saml2LogoutConfigurer.this;
}
private Saml2LogoutResponseValidator logoutResponseValidator() {
if (this.logoutResponseValidator != null) {
return this.logoutResponseValidator;
@@ -484,13 +534,7 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
if (authentication == null) {
return false;
}
if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal) {
return true;
}
if (authentication.getCredentials() instanceof Saml2ResponseAssertionAccessor) {
return true;
}
return authentication instanceof Saml2Authentication;
return authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal;
}
}
@@ -43,8 +43,6 @@ import org.springframework.security.web.reactive.result.method.annotation.Curren
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer;
import static org.springframework.security.config.Customizer.withDefaults;
/**
* @author Rob Winch
* @author Dan Zheng
@@ -158,8 +156,8 @@ class ServerHttpSecurityConfiguration {
ContextAwareServerHttpSecurity http = new ContextAwareServerHttpSecurity();
// @formatter:off
return http.authenticationManager(authenticationManager())
.headers(withDefaults())
.logout(withDefaults());
.headers().and()
.logout().and();
// @formatter:on
}
@@ -40,8 +40,6 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.reactive.result.view.AbstractView;
import static org.springframework.security.config.Customizer.withDefaults;
/**
* @author Rob Winch
* @since 5.0
@@ -123,13 +121,13 @@ class WebFluxSecurityConfiguration {
* @return
*/
private SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange((authorize) -> authorize.anyExchange().authenticated());
http.authorizeExchange().anyExchange().authenticated();
if (isOAuth2Present && OAuth2ClasspathGuard.shouldConfigure(this.context)) {
OAuth2ClasspathGuard.configure(this.context, http);
}
else {
http.httpBasic(withDefaults());
http.formLogin(withDefaults());
http.httpBasic();
http.formLogin();
}
SecurityWebFilterChain result = http.build();
return result;
@@ -138,8 +136,8 @@ class WebFluxSecurityConfiguration {
private static class OAuth2ClasspathGuard {
static void configure(ApplicationContext context, ServerHttpSecurity http) {
http.oauth2Login(withDefaults());
http.oauth2Client(withDefaults());
http.oauth2Login();
http.oauth2Client();
}
static boolean shouldConfigure(ApplicationContext context) {
@@ -0,0 +1,286 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.messaging.Message;
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
import org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.config.ObjectPostProcessor;
import org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration;
import org.springframework.security.config.annotation.web.messaging.MessageSecurityMetadataSourceRegistry;
import org.springframework.security.messaging.access.expression.DefaultMessageSecurityExpressionHandler;
import org.springframework.security.messaging.access.expression.MessageExpressionVoter;
import org.springframework.security.messaging.access.intercept.ChannelSecurityInterceptor;
import org.springframework.security.messaging.access.intercept.MessageSecurityMetadataSource;
import org.springframework.security.messaging.context.AuthenticationPrincipalArgumentResolver;
import org.springframework.security.messaging.context.SecurityContextChannelInterceptor;
import org.springframework.security.messaging.web.csrf.CsrfChannelInterceptor;
import org.springframework.security.messaging.web.socket.server.CsrfTokenHandshakeInterceptor;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert;
import org.springframework.util.PathMatcher;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.server.HandshakeInterceptor;
import org.springframework.web.socket.server.support.WebSocketHttpRequestHandler;
import org.springframework.web.socket.sockjs.SockJsService;
import org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler;
import org.springframework.web.socket.sockjs.transport.TransportHandlingSockJsService;
/**
* Allows configuring WebSocket Authorization.
*
* <p>
* For example:
* </p>
*
* <pre>
* &#064;Configuration
* public class WebSocketSecurityConfig extends
* AbstractSecurityWebSocketMessageBrokerConfigurer {
*
* &#064;Override
* protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
* messages.simpDestMatchers(&quot;/user/queue/errors&quot;).permitAll()
* .simpDestMatchers(&quot;/admin/**&quot;).hasRole(&quot;ADMIN&quot;).anyMessage()
* .authenticated();
* }
* }
* </pre>
*
* @author Rob Winch
* @since 4.0
* @see WebSocketMessageBrokerSecurityConfiguration
* @deprecated Use {@link EnableWebSocketSecurity} instead
*/
@Order(Ordered.HIGHEST_PRECEDENCE + 100)
@Import(ObjectPostProcessorConfiguration.class)
@Deprecated
public abstract class AbstractSecurityWebSocketMessageBrokerConfigurer
implements WebSocketMessageBrokerConfigurer, SmartInitializingSingleton {
private final WebSocketMessageSecurityMetadataSourceRegistry inboundRegistry = new WebSocketMessageSecurityMetadataSourceRegistry();
private SecurityExpressionHandler<Message<Object>> defaultExpressionHandler = new DefaultMessageSecurityExpressionHandler<>();
private SecurityExpressionHandler<Message<Object>> expressionHandler;
private ApplicationContext context;
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new AuthenticationPrincipalArgumentResolver());
}
@Override
public final void configureClientInboundChannel(ChannelRegistration registration) {
ChannelSecurityInterceptor inboundChannelSecurity = this.context.getBean(ChannelSecurityInterceptor.class);
registration.interceptors(this.context.getBean(SecurityContextChannelInterceptor.class));
if (!sameOriginDisabled()) {
registration.interceptors(this.context.getBean(CsrfChannelInterceptor.class));
}
if (this.inboundRegistry.containsMapping()) {
registration.interceptors(inboundChannelSecurity);
}
customizeClientInboundChannel(registration);
}
private PathMatcher getDefaultPathMatcher() {
try {
return this.context.getBean(SimpAnnotationMethodMessageHandler.class).getPathMatcher();
}
catch (NoSuchBeanDefinitionException ex) {
return new AntPathMatcher();
}
}
/**
* <p>
* Determines if a CSRF token is required for connecting. This protects against remote
* sites from connecting to the application and being able to read/write data over the
* connection. The default is false (the token is required).
* </p>
* <p>
* Subclasses can override this method to disable CSRF protection
* </p>
* @return false if a CSRF token is required for connecting, else true
*/
protected boolean sameOriginDisabled() {
return false;
}
/**
* Allows subclasses to customize the configuration of the {@link ChannelRegistration}
* .
* @param registration the {@link ChannelRegistration} to customize
*/
protected void customizeClientInboundChannel(ChannelRegistration registration) {
}
@Bean
public CsrfChannelInterceptor csrfChannelInterceptor() {
return new CsrfChannelInterceptor();
}
@Bean
public ChannelSecurityInterceptor inboundChannelSecurity(
MessageSecurityMetadataSource messageSecurityMetadataSource) {
ChannelSecurityInterceptor channelSecurityInterceptor = new ChannelSecurityInterceptor(
messageSecurityMetadataSource);
MessageExpressionVoter<Object> voter = new MessageExpressionVoter<>();
voter.setExpressionHandler(getMessageExpressionHandler());
List<AccessDecisionVoter<?>> voters = new ArrayList<>();
voters.add(voter);
AffirmativeBased manager = new AffirmativeBased(voters);
channelSecurityInterceptor.setAccessDecisionManager(manager);
return channelSecurityInterceptor;
}
@Bean
public SecurityContextChannelInterceptor securityContextChannelInterceptor() {
return new SecurityContextChannelInterceptor();
}
@Bean
public MessageSecurityMetadataSource inboundMessageSecurityMetadataSource() {
this.inboundRegistry.expressionHandler(getMessageExpressionHandler());
configureInbound(this.inboundRegistry);
return this.inboundRegistry.createMetadataSource();
}
/**
* @param messages
*/
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
}
@Autowired
public void setApplicationContext(ApplicationContext context) {
this.context = context;
}
@Deprecated
public void setMessageExpessionHandler(List<SecurityExpressionHandler<Message<Object>>> expressionHandlers) {
setMessageExpressionHandler(expressionHandlers);
}
@Autowired(required = false)
public void setMessageExpressionHandler(List<SecurityExpressionHandler<Message<Object>>> expressionHandlers) {
if (expressionHandlers.size() == 1) {
this.expressionHandler = expressionHandlers.get(0);
}
}
@Autowired(required = false)
public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
this.defaultExpressionHandler = objectPostProcessor.postProcess(this.defaultExpressionHandler);
}
private SecurityExpressionHandler<Message<Object>> getMessageExpressionHandler() {
if (this.expressionHandler == null) {
return this.defaultExpressionHandler;
}
return this.expressionHandler;
}
@Override
public void afterSingletonsInstantiated() {
if (sameOriginDisabled()) {
return;
}
String beanName = "stompWebSocketHandlerMapping";
SimpleUrlHandlerMapping mapping = this.context.getBean(beanName, SimpleUrlHandlerMapping.class);
Map<String, Object> mappings = mapping.getHandlerMap();
for (Object object : mappings.values()) {
if (object instanceof SockJsHttpRequestHandler) {
setHandshakeInterceptors((SockJsHttpRequestHandler) object);
}
else if (object instanceof WebSocketHttpRequestHandler) {
setHandshakeInterceptors((WebSocketHttpRequestHandler) object);
}
else {
throw new IllegalStateException("Bean " + beanName + " is expected to contain mappings to either a "
+ "SockJsHttpRequestHandler or a WebSocketHttpRequestHandler but got " + object);
}
}
if (this.inboundRegistry.containsMapping() && !this.inboundRegistry.isSimpDestPathMatcherConfigured()) {
PathMatcher pathMatcher = getDefaultPathMatcher();
this.inboundRegistry.simpDestPathMatcher(pathMatcher);
}
}
private void setHandshakeInterceptors(SockJsHttpRequestHandler handler) {
SockJsService sockJsService = handler.getSockJsService();
Assert.state(sockJsService instanceof TransportHandlingSockJsService,
() -> "sockJsService must be instance of TransportHandlingSockJsService got " + sockJsService);
TransportHandlingSockJsService transportHandlingSockJsService = (TransportHandlingSockJsService) sockJsService;
List<HandshakeInterceptor> handshakeInterceptors = transportHandlingSockJsService.getHandshakeInterceptors();
List<HandshakeInterceptor> interceptorsToSet = new ArrayList<>(handshakeInterceptors.size() + 1);
interceptorsToSet.add(new CsrfTokenHandshakeInterceptor());
interceptorsToSet.addAll(handshakeInterceptors);
transportHandlingSockJsService.setHandshakeInterceptors(interceptorsToSet);
}
private void setHandshakeInterceptors(WebSocketHttpRequestHandler handler) {
List<HandshakeInterceptor> handshakeInterceptors = handler.getHandshakeInterceptors();
List<HandshakeInterceptor> interceptorsToSet = new ArrayList<>(handshakeInterceptors.size() + 1);
interceptorsToSet.add(new CsrfTokenHandshakeInterceptor());
interceptorsToSet.addAll(handshakeInterceptors);
handler.setHandshakeInterceptors(interceptorsToSet);
}
private static class WebSocketMessageSecurityMetadataSourceRegistry extends MessageSecurityMetadataSourceRegistry {
@Override
public MessageSecurityMetadataSource createMetadataSource() {
return super.createMetadataSource();
}
@Override
protected boolean containsMapping() {
return super.containsMapping();
}
@Override
protected boolean isSimpDestPathMatcherConfigured() {
return super.isSimpDestPathMatcherConfigured();
}
}
}
@@ -35,6 +35,10 @@ class WebMvcSecurityConfigurationRuntimeHints implements RuntimeHintsRegistrar {
.registerType(TypeReference
.of("org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy"),
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
hints.reflection()
.registerType(TypeReference
.of("org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$HandlerMappingIntrospectorCacheFilterFactoryBean"),
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
}
}
@@ -521,23 +521,11 @@ final class AuthenticationConfigBuilder {
filterBuilder.addPropertyValue("authenticationManager", authManager);
filterBuilder.addPropertyValue("securityContextHolderStrategy",
authenticationFilterSecurityContextHolderStrategyRef);
String principalExtractorRef = x509Elt.getAttribute("principal-extractor-ref");
String subjectPrincipalRegex = x509Elt.getAttribute("subject-principal-regex");
boolean hasPrincipalExtractorRef = StringUtils.hasText(principalExtractorRef);
boolean hasSubjectPrincipalRegex = StringUtils.hasText(subjectPrincipalRegex);
if (hasPrincipalExtractorRef && hasSubjectPrincipalRegex) {
this.pc.getReaderContext()
.error("The attribute 'principal-extractor-ref' cannot be used together with the 'subject-principal-regex' attribute within <"
+ Elements.X509 + ">", this.pc.extractSource(x509Elt));
}
if (hasPrincipalExtractorRef) {
RuntimeBeanReference principalExtractor = new RuntimeBeanReference(principalExtractorRef);
filterBuilder.addPropertyValue("principalExtractor", principalExtractor);
}
if (hasSubjectPrincipalRegex) {
String regex = x509Elt.getAttribute("subject-principal-regex");
if (StringUtils.hasText(regex)) {
BeanDefinitionBuilder extractor = BeanDefinitionBuilder
.rootBeanDefinition(SubjectDnX509PrincipalExtractor.class);
extractor.addPropertyValue("subjectDnRegex", subjectPrincipalRegex);
extractor.addPropertyValue("subjectDnRegex", regex);
filterBuilder.addPropertyValue("principalExtractor", extractor.getBeanDefinition());
}
injectAuthenticationDetailsSource(x509Elt, filterBuilder);
@@ -153,7 +153,7 @@ class AuthorizationFilterParser implements BeanDefinitionParser {
if (!StringUtils.hasText(servletPath)) {
servletPath = null;
}
else if (!MatcherType.path.equals(matcherType)) {
else if (!MatcherType.mvc.equals(matcherType)) {
parserContext.getReaderContext()
.error(ATT_SERVLET_PATH + " is not applicable for request-matcher: '" + matcherType.name() + "'",
urlElt);
@@ -24,6 +24,7 @@ import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.CorsFilter;
@@ -39,6 +40,15 @@ public class CorsBeanDefinitionParser {
private static final String ATT_REF = "ref";
private static final String HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector";
private static final boolean mvcPresent;
static {
mvcPresent = ClassUtils.isPresent(HANDLER_MAPPING_INTROSPECTOR,
CorsBeanDefinitionParser.class.getClassLoader());
}
public BeanMetadataElement parse(Element element, ParserContext parserContext) {
if (element == null) {
return null;
@@ -61,7 +71,10 @@ public class CorsBeanDefinitionParser {
if (StringUtils.hasText(configurationSourceRef)) {
return new RuntimeBeanReference(configurationSourceRef);
}
return new RootBeanDefinition(CorsConfigurationSourceFactoryBean.class);
if (!mvcPresent) {
return null;
}
return new RootBeanDefinition(HandlerMappingIntrospectorFactoryBean.class);
}
}
@@ -40,9 +40,7 @@ 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.AuthorizationManagerWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.access.PathPatternRequestTransformer;
import org.springframework.security.web.access.intercept.AuthorizationFilter;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
@@ -63,8 +61,6 @@ public class DefaultFilterChainValidator implements FilterChainProxy.FilterChain
private final Log logger = LogFactory.getLog(getClass());
private final AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer requestTransformer = new PathPatternRequestTransformer();
@Override
public void validate(FilterChainProxy fcp) {
for (SecurityFilterChain filterChain : fcp.getFilterChains()) {
@@ -192,8 +188,7 @@ public class DefaultFilterChainValidator implements FilterChainProxy.FilterChain
String loginPage = ((LoginUrlAuthenticationEntryPoint) exceptions.getAuthenticationEntryPoint())
.getLoginFormUrl();
this.logger.info("Checking whether login URL '" + loginPage + "' is accessible with your configuration");
FilterInvocation invocation = new FilterInvocation(loginPage, "POST");
HttpServletRequest loginRequest = this.requestTransformer.transform(invocation.getRequest());
FilterInvocation loginRequest = new FilterInvocation(loginPage, "POST");
List<Filter> filters = null;
try {
filters = fcp.getFilters(loginPage);
@@ -242,7 +237,7 @@ public class DefaultFilterChainValidator implements FilterChainProxy.FilterChain
}
}
private boolean checkLoginPageIsPublic(List<Filter> filters, HttpServletRequest loginRequest) {
private boolean checkLoginPageIsPublic(List<Filter> filters, FilterInvocation loginRequest) {
FilterSecurityInterceptor authorizationInterceptor = getFilter(FilterSecurityInterceptor.class, filters);
if (authorizationInterceptor != null) {
FilterInvocationSecurityMetadataSource fids = authorizationInterceptor.getSecurityMetadataSource();
@@ -262,7 +257,7 @@ public class DefaultFilterChainValidator implements FilterChainProxy.FilterChain
AuthorizationManager<HttpServletRequest> authorizationManager = authorizationFilter
.getAuthorizationManager();
try {
AuthorizationResult result = authorizationManager.authorize(() -> TEST, loginRequest);
AuthorizationResult result = authorizationManager.authorize(() -> TEST, loginRequest.getHttpRequest());
return result != null && result.isGranted();
}
catch (Exception ex) {
@@ -272,7 +267,7 @@ public class DefaultFilterChainValidator implements FilterChainProxy.FilterChain
return false;
}
private Supplier<Boolean> deriveAnonymousCheck(List<Filter> filters, HttpServletRequest loginRequest,
private Supplier<Boolean> deriveAnonymousCheck(List<Filter> filters, FilterInvocation loginRequest,
AnonymousAuthenticationToken token) {
FilterSecurityInterceptor authorizationInterceptor = getFilter(FilterSecurityInterceptor.class, filters);
if (authorizationInterceptor != null) {
@@ -293,7 +288,7 @@ public class DefaultFilterChainValidator implements FilterChainProxy.FilterChain
return () -> {
AuthorizationManager<HttpServletRequest> authorizationManager = authorizationFilter
.getAuthorizationManager();
AuthorizationResult result = authorizationManager.authorize(() -> token, loginRequest);
AuthorizationResult result = authorizationManager.authorize(() -> token, loginRequest.getHttpRequest());
return result != null && result.isGranted();
};
}
@@ -159,7 +159,7 @@ public class FilterInvocationSecurityMetadataSourceParser implements BeanDefinit
if (!StringUtils.hasText(servletPath)) {
servletPath = null;
}
else if (!MatcherType.path.equals(matcherType)) {
else if (!MatcherType.mvc.equals(matcherType)) {
parserContext.getReaderContext()
.error(ATT_SERVLET_PATH + " is not applicable for request-matcher: '" + matcherType.name() + "'",
urlElt);
@@ -22,37 +22,38 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.lang.Nullable;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
/**
* Used for creating an instance of {@link CorsConfigurationSource} and autowiring the
* Used for creating an instance of {@link HandlerMappingIntrospector} and autowiring the
* {@link ApplicationContext}.
*
* @author Rob Winch
* @since 4.1.1
*/
class CorsConfigurationSourceFactoryBean implements FactoryBean<CorsConfigurationSource>, ApplicationContextAware {
class HandlerMappingIntrospectorFactoryBean
implements FactoryBean<HandlerMappingIntrospector>, ApplicationContextAware {
private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
private ApplicationContext context;
@Override
public CorsConfigurationSource getObject() {
public HandlerMappingIntrospector getObject() {
if (!this.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 "
+ CorsConfigurationSource.class.getName()
+ " is required to use <cors>. Please ensure Spring Security & Spring "
+ HandlerMappingIntrospector.class.getName()
+ " is required to use MvcRequestMatcher. Please ensure Spring Security & Spring "
+ "MVC are configured in a shared ApplicationContext.");
}
return this.context.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, CorsConfigurationSource.class);
return this.context.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, HandlerMappingIntrospector.class);
}
@Nullable
@Override
public Class<?> getObjectType() {
return CorsConfigurationSource.class;
return HandlerMappingIntrospector.class;
}
@Override
@@ -50,6 +50,7 @@ import org.springframework.security.core.context.SecurityContextHolderStrategy;
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;
@@ -87,6 +88,7 @@ import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
/**
* Stateful class which helps HttpSecurityBDP to create the configuration for the
@@ -98,6 +100,11 @@ import org.springframework.util.xml.DomUtils;
*/
class HttpConfigurationBuilder {
private static final String HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector";
private static final boolean mvcPresent = ClassUtils.isPresent(HANDLER_MAPPING_INTROSPECTOR,
HttpConfigurationBuilder.class.getClassLoader());
private static final String ATT_CREATE_SESSION = "create-session";
private static final String ATT_SESSION_FIXATION_PROTECTION = "session-fixation-protection";
@@ -786,8 +793,10 @@ class HttpConfigurationBuilder {
BeanDefinitionBuilder wipeBldr = BeanDefinitionBuilder
.rootBeanDefinition(AuthorizationManagerWebInvocationPrivilegeEvaluator.class)
.addConstructorArgReference(authorizationFilterParser.getAuthorizationManagerRef());
wipeBldr.addPropertyValue("requestTransformer",
new RootBeanDefinition(PathPatternRequestTransformerFactoryBean.class));
if (mvcPresent) {
wipeBldr.addPropertyValue("requestTransformer",
new RootBeanDefinition(HandlerMappingIntrospectorRequestTransformerFactoryBean.class));
}
BeanDefinition wipe = wipeBldr.getBeanDefinition();
this.pc.registerBeanComponent(
new BeanComponentDefinition(wipe, this.pc.getReaderContext().generateBeanName(wipe)));
@@ -957,7 +966,7 @@ class HttpConfigurationBuilder {
return BeanDefinitionBuilder.rootBeanDefinition(ObservationRegistryFactory.class).getBeanDefinition();
}
static class PathPatternRequestTransformerFactoryBean
static class HandlerMappingIntrospectorRequestTransformerFactoryBean
implements FactoryBean<AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer>,
ApplicationContextAware {
@@ -973,7 +982,10 @@ class HttpConfigurationBuilder {
if (requestTransformer != null) {
return requestTransformer;
}
return new PathPatternRequestTransformer();
HandlerMappingIntrospector hmi = this.applicationContext.getBeanProvider(HandlerMappingIntrospector.class)
.getIfAvailable();
return (hmi != null) ? new HandlerMappingIntrospectorRequestTransformer(hmi)
: new PathPatternRequestTransformer();
}
@Override
@@ -22,10 +22,13 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.http.HttpMethod;
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;
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
@@ -36,12 +39,21 @@ import org.springframework.util.StringUtils;
*/
public enum MatcherType {
path(PathPatternRequestMatcher.class), regex(RegexRequestMatcher.class), ciRegex(RegexRequestMatcher.class);
ant(AntPathRequestMatcher.class), regex(RegexRequestMatcher.class), ciRegex(RegexRequestMatcher.class),
mvc(MvcRequestMatcher.class);
private static final String HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector";
private static final boolean mvcPresent;
private static final String ATT_MATCHER_TYPE = "request-matcher";
final Class<? extends RequestMatcher> type;
static {
mvcPresent = ClassUtils.isPresent(HANDLER_MAPPING_INTROSPECTOR, MatcherType.class.getClassLoader());
}
MatcherType(Class<? extends RequestMatcher> type) {
this.type = type;
}
@@ -54,17 +66,18 @@ public enum MatcherType {
if (("/**".equals(path) || "**".equals(path)) && method == null) {
return new RootBeanDefinition(AnyRequestMatcher.class);
}
BeanDefinitionBuilder matcherBldr;
if (this == MatcherType.path) {
matcherBldr = BeanDefinitionBuilder.rootBeanDefinition(PathPatternRequestMatcherFactoryBean.class);
matcherBldr.addConstructorArgValue(path);
matcherBldr.addPropertyValue("basePath", servletPath);
BeanDefinitionBuilder matcherBldr = BeanDefinitionBuilder.rootBeanDefinition(this.type);
if (this == mvc) {
matcherBldr.addConstructorArgValue(new RootBeanDefinition(HandlerMappingIntrospectorFactoryBean.class));
}
matcherBldr.addConstructorArgValue(path);
if (this == mvc) {
matcherBldr.addPropertyValue("method", (StringUtils.hasText(method) ? HttpMethod.valueOf(method) : null));
matcherBldr.addPropertyValue("servletPath", servletPath);
}
else {
matcherBldr = BeanDefinitionBuilder.rootBeanDefinition(this.type);
matcherBldr.addConstructorArgValue(path);
matcherBldr.addConstructorArgValue(method);
}
matcherBldr.addConstructorArgValue(method);
if (this == ciRegex) {
matcherBldr.addConstructorArgValue(true);
}
@@ -76,10 +89,14 @@ public enum MatcherType {
return valueOf(elt.getAttribute(ATT_MATCHER_TYPE));
}
return path;
return ant;
}
static MatcherType fromElementOrMvc(Element elt) {
String matcherTypeName = elt.getAttribute(ATT_MATCHER_TYPE);
if (!StringUtils.hasText(matcherTypeName) && mvcPresent) {
return MatcherType.mvc;
}
return MatcherType.fromElement(elt);
}
@@ -0,0 +1,78 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.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();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,7 +23,6 @@ import jakarta.servlet.http.HttpServletRequest;
import org.w3c.dom.Element;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference;
@@ -44,10 +43,9 @@ import org.springframework.security.oauth2.server.resource.authentication.Opaque
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -66,8 +64,6 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
static final String BEARER_TOKEN_RESOLVER_REF = "bearer-token-resolver-ref";
static final String AUTHENTICATION_CONVERTER_REF = "authentication-converter-ref";
static final String ENTRY_POINT_REF = "entry-point-ref";
static final String BEARER_TOKEN_RESOLVER = "bearerTokenResolver";
@@ -128,16 +124,11 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
pc.getReaderContext().registerWithGeneratedName(opaqueTokenAuthenticationProvider)));
}
BeanMetadataElement bearerTokenResolver = getBearerTokenResolver(oauth2ResourceServer);
BeanMetadataElement authenticationConverter = getAuthenticationConverter(oauth2ResourceServer);
if (bearerTokenResolver != null && authenticationConverter != null) {
throw new BeanDefinitionStoreException(
"You cannot use bearer-token-ref and authentication-converter-ref in the same oauth2-resource-server element");
}
if (bearerTokenResolver == null && authenticationConverter == null) {
authenticationConverter = new RootBeanDefinition(BearerTokenAuthenticationConverter.class);
}
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
.rootBeanDefinition(BearerTokenRequestMatcher.class);
requestMatcherBuilder.addConstructorArgValue(bearerTokenResolver);
BeanDefinition requestMatcher = requestMatcherBuilder.getBeanDefinition();
BeanMetadataElement authenticationEntryPoint = getEntryPoint(oauth2ResourceServer);
BeanDefinition requestMatcher = buildRequestMatcher(bearerTokenResolver, authenticationConverter);
this.entryPoints.put(requestMatcher, authenticationEntryPoint);
this.deniedHandlers.put(requestMatcher, this.accessDeniedHandler);
this.ignoreCsrfRequestMatchers.add(requestMatcher);
@@ -145,35 +136,13 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
.rootBeanDefinition(BearerTokenAuthenticationFilter.class);
BeanMetadataElement authenticationManagerResolver = getAuthenticationManagerResolver(oauth2ResourceServer);
filterBuilder.addConstructorArgValue(authenticationManagerResolver);
filterBuilder.addPropertyValue(BEARER_TOKEN_RESOLVER, bearerTokenResolver);
filterBuilder.addPropertyValue(AUTHENTICATION_ENTRY_POINT, authenticationEntryPoint);
filterBuilder.addPropertyValue("securityContextHolderStrategy",
this.authenticationFilterSecurityContextHolderStrategy);
if (authenticationConverter != null) {
filterBuilder.addConstructorArgValue(authenticationConverter);
}
if (bearerTokenResolver != null) {
filterBuilder.addPropertyValue(BEARER_TOKEN_RESOLVER, bearerTokenResolver);
}
return filterBuilder.getBeanDefinition();
}
private BeanDefinition buildRequestMatcher(BeanMetadataElement bearerTokenResolver,
BeanMetadataElement authenticationConverter) {
if (bearerTokenResolver != null) {
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
.rootBeanDefinition(BearerTokenRequestMatcher.class);
requestMatcherBuilder.addConstructorArgValue(bearerTokenResolver);
return requestMatcherBuilder.getBeanDefinition();
}
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
.rootBeanDefinition(BearerTokenAuthenticationRequestMatcher.class);
if (authenticationConverter != null) {
requestMatcherBuilder.addConstructorArgValue(authenticationConverter);
}
return requestMatcherBuilder.getBeanDefinition();
}
void validateConfiguration(Element oauth2ResourceServer, Element jwt, Element opaqueToken, ParserContext pc) {
if (!oauth2ResourceServer.hasAttribute(AUTHENTICATION_MANAGER_RESOLVER_REF)) {
if (jwt == null && opaqueToken == null) {
@@ -209,19 +178,11 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
BeanMetadataElement getBearerTokenResolver(Element element) {
String bearerTokenResolverRef = element.getAttribute(BEARER_TOKEN_RESOLVER_REF);
if (!StringUtils.hasLength(bearerTokenResolverRef)) {
return null;
return new RootBeanDefinition(DefaultBearerTokenResolver.class);
}
return new RuntimeBeanReference(bearerTokenResolverRef);
}
BeanMetadataElement getAuthenticationConverter(Element element) {
String authenticationConverterRef = element.getAttribute(AUTHENTICATION_CONVERTER_REF);
if (!StringUtils.hasLength(authenticationConverterRef)) {
return null;
}
return new RuntimeBeanReference(authenticationConverterRef);
}
BeanMetadataElement getEntryPoint(Element element) {
String entryPointRef = element.getAttribute(ENTRY_POINT_REF);
if (!StringUtils.hasLength(entryPointRef)) {
@@ -405,29 +366,4 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
}
static final class BearerTokenAuthenticationRequestMatcher implements RequestMatcher {
private final AuthenticationConverter authenticationConverter;
BearerTokenAuthenticationRequestMatcher() {
this.authenticationConverter = new BearerTokenAuthenticationConverter();
}
BearerTokenAuthenticationRequestMatcher(AuthenticationConverter authenticationConverter) {
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
this.authenticationConverter = authenticationConverter;
}
@Override
public boolean matches(HttpServletRequest request) {
try {
return this.authenticationConverter.convert(request) != null;
}
catch (OAuth2AuthenticationException ex) {
return false;
}
}
}
}
@@ -1,77 +0,0 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.http;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
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.util.StringUtils;
public final class PathPatternRequestMatcherFactoryBean
implements FactoryBean<PathPatternRequestMatcher>, ApplicationContextAware, InitializingBean {
private final String pattern;
private String basePath;
private HttpMethod method;
private PathPatternRequestMatcher.Builder builder;
PathPatternRequestMatcherFactoryBean(String pattern) {
this.pattern = pattern;
}
PathPatternRequestMatcherFactoryBean(String pattern, String method) {
this.pattern = pattern;
this.method = StringUtils.hasText(method) ? HttpMethod.valueOf(method) : null;
}
@Override
public @Nullable PathPatternRequestMatcher getObject() throws Exception {
return this.builder.matcher(this.method, this.pattern);
}
@Override
public @Nullable Class<?> getObjectType() {
return PathPatternRequestMatcher.class;
}
public void setBasePath(String basePath) {
this.basePath = basePath;
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.builder = context.getBeanProvider(PathPatternRequestMatcher.Builder.class)
.getIfUnique(PathPatternRequestMatcher::withDefaults);
}
@Override
public void afterPropertiesSet() throws Exception {
if (this.basePath != null) {
this.builder.basePath(this.basePath);
}
}
}
@@ -22,6 +22,7 @@ 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
@@ -44,7 +45,10 @@ public final class RequestMatcherFactoryBean implements FactoryBean<RequestMatch
@Override
public RequestMatcher getObject() throws Exception {
return this.builder.matcher(this.method, this.path);
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
@@ -54,8 +58,7 @@ public final class RequestMatcherFactoryBean implements FactoryBean<RequestMatch
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.builder = context.getBeanProvider(PathPatternRequestMatcher.Builder.class)
.getIfUnique(PathPatternRequestMatcher::withDefaults);
this.builder = context.getBeanProvider(PathPatternRequestMatcher.Builder.class).getIfUnique();
}
}
@@ -32,8 +32,6 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
import org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertionAccessor;
import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter;
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseFilter;
@@ -241,13 +239,7 @@ final class Saml2LogoutBeanDefinitionParser implements BeanDefinitionParser {
if (authentication == null) {
return false;
}
if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal) {
return true;
}
if (authentication.getCredentials() instanceof Saml2ResponseAssertionAccessor) {
return true;
}
return authentication instanceof Saml2Authentication;
return authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal;
}
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
@@ -32,6 +32,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.security.config.BeanIds;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@@ -46,7 +47,7 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
private static final String CONTEXT_SOURCE_CLASS = "org.springframework.security.ldap.DefaultSpringSecurityContextSource";
/**
* Defines the Url of the ldap server to use. If not specified, an embedded UnboundID
* Defines the Url of the ldap server to use. If not specified, an embedded apache DS
* instance will be created
*/
private static final String ATT_URL = "url";
@@ -77,15 +78,22 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
private static final int DEFAULT_PORT = 33389;
private static final String APACHEDS_CLASSNAME = "org.apache.directory.server.core.DefaultDirectoryService";
private static final String UNBOUNID_CLASSNAME = "com.unboundid.ldap.listener.InMemoryDirectoryServer";
private static final String APACHEDS_CONTAINER_CLASSNAME = "org.springframework.security.ldap.server.ApacheDSContainer";
private static final String UNBOUNDID_CONTAINER_CLASSNAME = "org.springframework.security.ldap.server.UnboundIdContainer";
private static final boolean unboundIdPresent;
private static final boolean apacheDsPresent;
static {
ClassLoader classLoader = LdapServerBeanDefinitionParser.class.getClassLoader();
unboundIdPresent = ClassUtils.isPresent(UNBOUNID_CLASSNAME, classLoader);
apacheDsPresent = ClassUtils.isPresent(APACHEDS_CLASSNAME, classLoader);
}
@Override
@@ -120,9 +128,10 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
/**
* Will be called if no url attribute is supplied.
*
* Registers beans to create an embedded UnboundID Server.
* Registers beans to create an embedded apache directory server.
* @return the BeanDefinition for the ContextSource for the embedded server.
*
* @see ApacheDSContainer
* @see UnboundIdContainer
*/
private RootBeanDefinition createEmbeddedServer(Element element, ParserContext parserContext) {
@@ -153,7 +162,8 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
}
ldapContainer.getConstructorArgumentValues().addGenericArgumentValue(ldifs);
ldapContainer.getPropertyValues().addPropertyValue("port", getPort(element));
if (parserContext.getRegistry().containsBeanDefinition(BeanIds.EMBEDDED_UNBOUNDID)) {
if (parserContext.getRegistry().containsBeanDefinition(BeanIds.EMBEDDED_APACHE_DS)
|| parserContext.getRegistry().containsBeanDefinition(BeanIds.EMBEDDED_UNBOUNDID)) {
parserContext.getReaderContext()
.error("Only one embedded server bean is allowed per application context", element);
}
@@ -165,6 +175,9 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
}
private RootBeanDefinition getRootBeanDefinition(String mode) {
if (isApacheDsEnabled(mode)) {
return new RootBeanDefinition(APACHEDS_CONTAINER_CLASSNAME, null, null);
}
if (isUnboundidEnabled(mode)) {
return new RootBeanDefinition(UNBOUNDID_CONTAINER_CLASSNAME, null, null);
}
@@ -172,12 +185,19 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
}
private String resolveBeanId(String mode) {
if (isApacheDsEnabled(mode)) {
return BeanIds.EMBEDDED_APACHE_DS;
}
if (isUnboundidEnabled(mode)) {
return BeanIds.EMBEDDED_UNBOUNDID;
}
return null;
}
private boolean isApacheDsEnabled(String mode) {
return "apacheds".equals(mode) || apacheDsPresent;
}
private boolean isUnboundidEnabled(String mode) {
return "unboundid".equals(mode) || unboundIdPresent;
}
@@ -213,6 +233,10 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
}
private int getPort() {
if (apacheDsPresent) {
ApacheDSContainer apacheDSContainer = this.applicationContext.getBean(ApacheDSContainer.class);
return apacheDSContainer.getLocalPort();
}
if (unboundIdPresent) {
UnboundIdContainer unboundIdContainer = this.applicationContext.getBean(UnboundIdContainer.class);
return unboundIdContainer.getPort();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
package org.springframework.security.config.oauth2.client;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistration.Builder;
@@ -28,7 +27,7 @@ import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;
* Common OAuth2 Providers that can be used to create
* {@link org.springframework.security.oauth2.client.registration.ClientRegistration.Builder
* builders} pre-configured with sensible defaults for the
* {@link HttpSecurity#oauth2Login(Customizer)} flow.
* {@link HttpSecurity#oauth2Login()} flow.
*
* @author Phillip Webb
* @since 5.0
@@ -88,23 +87,6 @@ public enum CommonOAuth2Provider {
},
X {
@Override
public Builder getBuilder(String registrationId) {
ClientRegistration.Builder builder = getBuilder(registrationId,
ClientAuthenticationMethod.CLIENT_SECRET_POST, DEFAULT_REDIRECT_URL);
builder.scope("users.read", "tweet.read");
builder.authorizationUri("https://x.com/i/oauth2/authorize");
builder.tokenUri("https://api.x.com/2/oauth2/token");
builder.userInfoUri("https://api.x.com/2/users/me");
builder.userNameAttributeName("username");
builder.clientName("X");
return builder;
}
},
OKTA {
@Override
@@ -19,6 +19,7 @@ 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
@@ -31,9 +32,30 @@ import org.springframework.security.messaging.util.matcher.PathPatternMessageMat
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 {
return PathPatternMessageMatcher.withDefaults();
if (this.parser == null) {
return PathPatternMessageMatcher.withDefaults();
}
return PathPatternMessageMatcher.withPathPatternParser(this.parser);
}
@Override
@@ -18,6 +18,10 @@ package org.springframework.security.config.web.server;
import java.util.function.Function;
import com.nimbusds.jose.JOSEObjectType;
import com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier;
import com.nimbusds.jose.proc.JOSEObjectTypeVerifier;
import com.nimbusds.jose.proc.JWKSecurityContext;
import reactor.core.publisher.Mono;
import org.springframework.security.authentication.AuthenticationProvider;
@@ -37,7 +41,6 @@ import org.springframework.security.oauth2.jwt.BadJwtException;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
import org.springframework.security.oauth2.jwt.JwtTypeValidator;
import org.springframework.security.oauth2.jwt.JwtValidators;
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
@@ -69,10 +72,8 @@ final class OidcBackChannelLogoutReactiveAuthenticationManager implements Reacti
* Construct an {@link OidcBackChannelLogoutReactiveAuthenticationManager}
*/
OidcBackChannelLogoutReactiveAuthenticationManager() {
JwtTypeValidator type = new JwtTypeValidator("JWT", "logout+jwt");
type.setAllowEmpty(true);
Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidator = (clientRegistration) -> JwtValidators
.createDefaultWithValidators(type, new OidcBackChannelLogoutTokenValidator(clientRegistration));
.createDefaultWithValidators(new OidcBackChannelLogoutTokenValidator(clientRegistration));
this.logoutTokenDecoderFactory = (clientRegistration) -> {
String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
if (!StringUtils.hasText(jwkSetUri)) {
@@ -83,7 +84,11 @@ final class OidcBackChannelLogoutReactiveAuthenticationManager implements Reacti
null);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
}
NimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri).build();
JOSEObjectTypeVerifier<JWKSecurityContext> typeVerifier = new DefaultJOSEObjectTypeVerifier<>(null,
JOSEObjectType.JWT, new JOSEObjectType("logout+jwt"));
NimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri)
.jwtProcessorCustomizer((processor) -> processor.setJWSTypeVerifier(typeVerifier))
.build();
decoder.setJwtValidator(jwtValidator.apply(clientRegistration));
decoder.setClaimSetConverter(
new ClaimTypeConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverters()));
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,13 +16,9 @@
package org.springframework.security.config.websocket;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import org.w3c.dom.Element;
@@ -54,6 +50,7 @@ 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;
@@ -67,7 +64,6 @@ 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;
@@ -274,25 +270,18 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
matcher.addConstructorArgValue(messageType);
return matcher.getBeanDefinition();
}
String factoryName = null;
if (hasPattern && hasMessageType) {
BeanDefinitionBuilder matcher = BeanDefinitionBuilder.rootBeanDefinition(MessageMatcherFactoryBean.class);
matcher.addConstructorArgValue(matcherPattern);
if (hasMessageType) {
SimpMessageType type = SimpMessageType.valueOf(messageType);
if (SimpMessageType.MESSAGE == type) {
factoryName = "createMessageMatcher";
}
else if (SimpMessageType.SUBSCRIBE == type) {
factoryName = "createSubscribeMatcher";
}
else {
matcher.addConstructorArgValue(type);
if (SimpMessageType.SUBSCRIBE != type && SimpMessageType.MESSAGE != type) {
parserContext.getReaderContext()
.error("Cannot use intercept-websocket@message-type=" + messageType
+ " with a pattern because the type does not have a destination.", interceptMessage);
}
}
BeanDefinitionBuilder matcher = BeanDefinitionBuilder.rootBeanDefinition(SimpDestinationMessageMatcher.class);
matcher.setFactoryMethod(factoryName);
matcher.addConstructorArgValue(matcherPattern);
matcher.addConstructorArgValue(new RuntimeBeanReference("springSecurityMessagePathMatcher"));
matcher.addPropertyValue("pathMatcher", new RuntimeBeanReference("springSecurityMessagePathMatcher"));
return matcher.getBeanDefinition();
}
@@ -305,17 +294,14 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
private static final String CLIENT_INBOUND_CHANNEL_BEAN_ID = "clientInboundChannel";
private static final String CSRF_CHANNEL_INTERCEPTOR_BEAN_ID = "csrfChannelInterceptor";
private static final String INTERCEPTORS_PROP = "interceptors";
private static final String CUSTOM_ARG_RESOLVERS_PROP = "customArgumentResolvers";
private static final String TEMPLATE_EXPRESSION_BEAN_ID = "annotationExpressionTemplateDefaults";
private static final Set<String> CSRF_HANDSHAKE_HANDLER_CLASSES = Collections.unmodifiableSet(
new HashSet<>(Arrays.asList("org.springframework.web.socket.server.support.WebSocketHttpRequestHandler",
"org.springframework.web.socket.sockjs.transport.TransportHandlingSockJsService",
"org.springframework.web.socket.sockjs.transport.handler.DefaultSockJsService")));
private final String inboundSecurityInterceptorId;
private final boolean sameOriginDisabled;
@@ -354,7 +340,16 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
}
}
}
else if (CSRF_HANDSHAKE_HANDLER_CLASSES.contains(beanClassName)) {
else if ("org.springframework.web.socket.server.support.WebSocketHttpRequestHandler"
.equals(beanClassName)) {
addCsrfTokenHandshakeInterceptor(bd);
}
else if ("org.springframework.web.socket.sockjs.transport.TransportHandlingSockJsService"
.equals(beanClassName)) {
addCsrfTokenHandshakeInterceptor(bd);
}
else if ("org.springframework.web.socket.sockjs.transport.handler.DefaultSockJsService"
.equals(beanClassName)) {
addCsrfTokenHandshakeInterceptor(bd);
}
}
@@ -364,7 +359,12 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
ManagedList<Object> interceptors = new ManagedList();
interceptors.add(new RootBeanDefinition(SecurityContextChannelInterceptor.class));
if (!this.sameOriginDisabled) {
interceptors.add(new RootBeanDefinition(CsrfChannelInterceptor.class));
if (!registry.containsBeanDefinition(CSRF_CHANNEL_INTERCEPTOR_BEAN_ID)) {
interceptors.add(new RootBeanDefinition(CsrfChannelInterceptor.class));
}
else {
interceptors.add(new RuntimeBeanReference(CSRF_CHANNEL_INTERCEPTOR_BEAN_ID));
}
}
interceptors.add(registry.getBeanDefinition(this.inboundSecurityInterceptorId));
BeanDefinition inboundChannel = registry.getBeanDefinition(CLIENT_INBOUND_CHANNEL_BEAN_ID);
@@ -58,6 +58,6 @@ abstract class AbstractRequestMatcherDsl {
protected abstract class AuthorizationManagerRule(open val rule: AuthorizationManager<RequestAuthorizationContext>)
protected enum class PatternType {
PATH;
ANT, MVC
}
}
@@ -31,9 +31,11 @@ import org.springframework.security.core.Authentication
import org.springframework.security.web.access.IpAddressAuthorizationManager
import org.springframework.security.web.access.intercept.AuthorizationFilter
import org.springframework.security.web.access.intercept.RequestAuthorizationContext
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher
import org.springframework.security.web.util.matcher.AnyRequestMatcher
import org.springframework.security.web.util.matcher.RequestMatcher
import org.springframework.util.ClassUtils
import org.springframework.web.servlet.handler.HandlerMappingIntrospector
import java.util.function.Supplier
/**
@@ -67,7 +69,12 @@ class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl {
private val rolePrefix: String
private val roleHierarchy: RoleHierarchy
private val PATTERN_TYPE = PatternType.PATH
private val HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector"
private val HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector"
private val MVC_PRESENT = ClassUtils.isPresent(
HANDLER_MAPPING_INTROSPECTOR,
AuthorizeHttpRequestsDsl::class.java.classLoader)
private val PATTERN_TYPE = if (MVC_PRESENT) PatternType.MVC else PatternType.ANT
/**
* Adds a request authorization rule.
@@ -281,13 +288,17 @@ class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl {
when (rule) {
is MatcherAuthorizationManagerRule -> requests.requestMatchers(rule.matcher).access(rule.rule)
is PatternAuthorizationManagerRule -> {
var builder = requests.applicationContext.getBeanProvider(
PathPatternRequestMatcher.Builder::class.java)
.getIfUnique(PathPatternRequestMatcher::withDefaults)
if (rule.servletPath != null) {
builder = builder.basePath(rule.servletPath)
when (rule.patternType) {
PatternType.ANT -> requests.requestMatchers(rule.httpMethod, rule.pattern).access(rule.rule)
PatternType.MVC -> {
val introspector = requests.applicationContext.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, HandlerMappingIntrospector::class.java)
val mvcMatcher = MvcRequestMatcher.Builder(introspector)
.servletPath(rule.servletPath)
.pattern(rule.pattern)
mvcMatcher.setMethod(rule.httpMethod)
requests.requestMatchers(mvcMatcher).access(rule.rule)
}
}
requests.requestMatchers(builder.matcher(rule.httpMethod, rule.pattern)).access(rule.rule)
}
}
}
@@ -19,9 +19,11 @@ package org.springframework.security.config.annotation.web
import org.springframework.http.HttpMethod
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher
import org.springframework.security.web.util.matcher.AnyRequestMatcher
import org.springframework.security.web.util.matcher.RequestMatcher
import org.springframework.util.ClassUtils
import org.springframework.web.servlet.handler.HandlerMappingIntrospector
/**
* A Kotlin DSL to configure [HttpSecurity] request authorization using idiomatic Kotlin code.
@@ -31,7 +33,13 @@ import org.springframework.security.web.util.matcher.RequestMatcher
*/
class AuthorizeRequestsDsl : AbstractRequestMatcherDsl() {
private val authorizationRules = mutableListOf<AuthorizationRule>()
private val PATTERN_TYPE = PatternType.PATH;
private val HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector"
private val HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector"
private val MVC_PRESENT = ClassUtils.isPresent(
HANDLER_MAPPING_INTROSPECTOR,
AuthorizeRequestsDsl::class.java.classLoader)
private val PATTERN_TYPE = if (MVC_PRESENT) PatternType.MVC else PatternType.ANT
/**
* Adds a request authorization rule.
@@ -218,13 +226,17 @@ class AuthorizeRequestsDsl : AbstractRequestMatcherDsl() {
when (rule) {
is MatcherAuthorizationRule -> requests.requestMatchers(rule.matcher).access(rule.rule)
is PatternAuthorizationRule -> {
var builder = requests.applicationContext.getBeanProvider(
PathPatternRequestMatcher.Builder::class.java)
.getIfUnique(PathPatternRequestMatcher::withDefaults);
if (rule.servletPath != null) {
builder = builder.basePath(rule.servletPath)
when (rule.patternType) {
PatternType.ANT -> requests.requestMatchers(rule.httpMethod, rule.pattern).access(rule.rule)
PatternType.MVC -> {
val introspector = requests.applicationContext.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, HandlerMappingIntrospector::class.java)
val mvcMatcher = MvcRequestMatcher.Builder(introspector)
.servletPath(rule.servletPath)
.pattern(rule.pattern)
mvcMatcher.setMethod(rule.httpMethod)
requests.requestMatchers(mvcMatcher).access(rule.rule)
}
}
requests.requestMatchers(builder.matcher(rule.httpMethod, rule.pattern)).access(rule.rule)
}
}
}
@@ -73,6 +73,7 @@ operator fun HttpSecurity.invoke(httpConfiguration: HttpSecurityDsl.() -> Unit)
*/
@SecurityMarker
class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecurityDsl.() -> Unit) {
private val HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector"
var authenticationManager: AuthenticationManager? = null
val context: ApplicationContext = http.getSharedObject(ApplicationContext::class.java)
@@ -20,9 +20,11 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configurers.ChannelSecurityConfigurer
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl
import org.springframework.security.web.access.channel.ChannelProcessor
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher
import org.springframework.security.web.util.matcher.AnyRequestMatcher
import org.springframework.security.web.util.matcher.RequestMatcher
import org.springframework.util.ClassUtils
import org.springframework.web.servlet.handler.HandlerMappingIntrospector
/**
* A Kotlin DSL to configure [HttpSecurity] channel security using idiomatic
@@ -36,7 +38,12 @@ import org.springframework.security.web.util.matcher.RequestMatcher
class RequiresChannelDsl : AbstractRequestMatcherDsl() {
private val channelSecurityRules = mutableListOf<AuthorizationRule>()
private val PATTERN_TYPE = PatternType.PATH
private val HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector"
private val HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector"
private val MVC_PRESENT = ClassUtils.isPresent(
HANDLER_MAPPING_INTROSPECTOR,
RequiresChannelDsl::class.java.classLoader)
private val PATTERN_TYPE = if (MVC_PRESENT) PatternType.MVC else PatternType.ANT
var channelProcessors: List<ChannelProcessor>? = null
@@ -114,13 +121,17 @@ class RequiresChannelDsl : AbstractRequestMatcherDsl() {
when (rule) {
is MatcherAuthorizationRule -> channelSecurity.requestMatchers(rule.matcher).requires(rule.rule)
is PatternAuthorizationRule -> {
var builder = channelSecurity.applicationContext.getBeanProvider(
PathPatternRequestMatcher.Builder::class.java)
.getIfUnique(PathPatternRequestMatcher::withDefaults);
if (rule.servletPath != null) {
builder = builder.basePath(rule.servletPath)
when (rule.patternType) {
PatternType.ANT -> channelSecurity.requestMatchers(rule.pattern).requires(rule.rule)
PatternType.MVC -> {
val introspector = channelSecurity.applicationContext.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, HandlerMappingIntrospector::class.java)
val mvcMatcher = MvcRequestMatcher.Builder(introspector)
.servletPath(rule.servletPath)
.pattern(rule.pattern)
mvcMatcher.setMethod(rule.httpMethod)
channelSecurity.requestMatchers(mvcMatcher).requires(rule.rule)
}
}
channelSecurity.requestMatchers(builder.matcher(rule.httpMethod, rule.pattern)).requires(rule.rule)
}
}
}
@@ -51,7 +51,6 @@ class X509Dsl {
var authenticationDetailsSource: AuthenticationDetailsSource<HttpServletRequest, PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails>? = null
var userDetailsService: UserDetailsService? = null
var authenticationUserDetailsService: AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken>? = null
@Deprecated("Use x509PrincipalExtractor instead")
var subjectPrincipalRegex: String? = null
@@ -14,8 +14,7 @@
# limitations under the License.
#
http\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-7.0.xsd
http\://www.springframework.org/schema/security/spring-security-7.0.xsd=org/springframework/security/config/spring-security-7.0.xsd
http\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-6.5.xsd
http\://www.springframework.org/schema/security/spring-security-6.5.xsd=org/springframework/security/config/spring-security-6.5.xsd
http\://www.springframework.org/schema/security/spring-security-6.4.xsd=org/springframework/security/config/spring-security-6.4.xsd
http\://www.springframework.org/schema/security/spring-security-6.3.xsd=org/springframework/security/config/spring-security-6.3.xsd
@@ -42,8 +41,7 @@ http\://www.springframework.org/schema/security/spring-security-2.0.xsd=org/spri
http\://www.springframework.org/schema/security/spring-security-2.0.1.xsd=org/springframework/security/config/spring-security-2.0.1.xsd
http\://www.springframework.org/schema/security/spring-security-2.0.2.xsd=org/springframework/security/config/spring-security-2.0.2.xsd
http\://www.springframework.org/schema/security/spring-security-2.0.4.xsd=org/springframework/security/config/spring-security-2.0.4.xsd
https\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-7.0.xsd
https\://www.springframework.org/schema/security/spring-security-7.0.xsd=org/springframework/security/config/spring-security-7.0.xsd
https\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-6.5.xsd
https\://www.springframework.org/schema/security/spring-security-6.5.xsd=org/springframework/security/config/spring-security-6.5.xsd
https\://www.springframework.org/schema/security/spring-security-6.4.xsd=org/springframework/security/config/spring-security-6.4.xsd
https\://www.springframework.org/schema/security/spring-security-6.3.xsd=org/springframework/security/config/spring-security-6.3.xsd

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