Compare commits

...

108 Commits

Author SHA1 Message Date
blake_bauman
a4f813ab29 Support Multiple ServerLogoutHandlers
This commit adds support to ServerHttpSecurity for registering
multiple ServerLogoutHandlers. This is handy so that an application
does not need to re-supply any handlers already configured by
the DSL.

Signed-off-by: blake_bauman <blake_bauman@apple.com>
2025-09-05 11:47:54 -06:00
Rob Winch
686f8398dd
Merge branch '6.5.x' 2025-09-04 22:40:45 -05:00
Rob Winch
653f22d4a1
Merge branch '6.4.x' into 6.5.x 2025-09-04 22:40:08 -05:00
Rob Winch
f54c293078
Bump org.springframework.data:spring-data-bom from 2024.1.8 to 2024.1.9 2025-09-04 22:39:33 -05:00
Rob Winch
34fccf45c2
Bump com.webauthn4j:webauthn4j-core from 0.29.5.RELEASE to 0.29.6.RELEASE 2025-09-04 22:39:31 -05:00
Rob Winch
f840ee06eb
Bump org.hibernate.orm:hibernate-core from 6.6.26.Final to 6.6.28.Final 2025-09-04 22:39:29 -05:00
Rob Winch
8429c23108
Bump io.micrometer:micrometer-observation from 1.14.9 to 1.14.10 2025-09-04 22:38:50 -05:00
Rob Winch
97f3567702
Bump org.hibernate.orm:hibernate-core from 6.6.23.Final to 6.6.28.Final 2025-09-04 22:38:46 -05:00
dependabot[bot]
2cfdcb9d95 Bump org-opensaml5 from 5.1.5 to 5.1.6
Bumps `org-opensaml5` from 5.1.5 to 5.1.6.

Updates `org.opensaml:opensaml-saml-api` from 5.1.5 to 5.1.6

Updates `org.opensaml:opensaml-saml-impl` from 5.1.5 to 5.1.6

---
updated-dependencies:
- dependency-name: org.opensaml:opensaml-saml-api
  dependency-version: 5.1.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.opensaml:opensaml-saml-impl
  dependency-version: 5.1.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-04 22:37:50 -05:00
dependabot[bot]
3c344ff491 Bump com.webauthn4j:webauthn4j-core
Bumps [com.webauthn4j:webauthn4j-core](https://github.com/webauthn4j/webauthn4j) from 0.29.5.RELEASE to 0.29.6.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.5.RELEASE...0.29.6.RELEASE)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-04 22:37:36 -05:00
Josh Cummings
f30cc9c5a9
Update to PropertySourcesPlaceholderConfigurer
This commit replaces deprecated usage of PropertyPlaceholderConfigurer
in favor of PropertySourcesPlaceholderConfigurer
2025-09-04 11:32:04 -06:00
Josh Cummings
c64b086878
Add SecurityAssertions
This commit introduces a simple, internal test API for
verifying aspects of an Authentication, like its name
and authorities.

Closes gh-17844
2025-09-03 17:53:42 -06:00
Josh Cummings
de10e08348
Make withRoles Check Only Roles
This commit clarifies the semantics of withRoles,
which is to check the role-based authorities in an
authentication.

Closes gh-17843
2025-09-03 17:53:41 -06:00
Josh Cummings
bd119ac411
Implement Equals and HashCode
Internally, RequestMatcher is sometimes used as a key to a
HashMap. Accordingly, each implementation should implement
equals and hashCode.

Closes gh-17842
2025-09-03 17:48:50 -06:00
Rob Winch
24ffda28d8
Fixes for webauthn tests after JSpecify
Issue gh-17839
2025-09-03 14:44:58 -05:00
Rob Winch
6a84f96930
Enable Null checking in spring-security-test via JSpecify
Closes gh-17840
2025-09-03 12:59:46 -05:00
Rob Winch
194be8ffb6
Checkstyle fixes for webauthn JSpecify
Issue gh-17839
2025-09-03 12:58:27 -05:00
Rob Winch
47b4b155da
Add security-nullability to webauthn
Issue gh-17839
2025-09-03 12:17:56 -05:00
Rob Winch
0a991a91ce
Enable Null checking in spring-security-webauthn via JSpecify
Closes gh-17839
2025-09-03 12:06:53 -05:00
dependabot[bot]
d2e934ca54
Bump org.hibernate.orm:hibernate-core from 6.6.26.Final to 6.6.28.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.26.Final to 6.6.28.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.28/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.26...6.6.28)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-03 00:33:27 +00:00
dependabot[bot]
fee4d08de3
Bump com.webauthn4j:webauthn4j-core
Bumps [com.webauthn4j:webauthn4j-core](https://github.com/webauthn4j/webauthn4j) from 0.29.5.RELEASE to 0.29.6.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.5.RELEASE...0.29.6.RELEASE)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-02 23:19:43 +00:00
Josh Cummings
3dbcf266e9
Merge branch '6.5.x' 2025-09-02 16:45:30 -06:00
Josh Cummings
eeb67650ee
Deprecate RequiresChannelDsl
Issue gh-16680
2025-09-02 16:41:39 -06:00
dependabot[bot]
b4fc01194f
Bump org.hibernate.orm:hibernate-core from 6.6.23.Final to 6.6.28.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.23.Final to 6.6.28.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.28/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.23...6.6.28)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-02 22:32:54 +00:00
Josh Cummings
3534b74945
Replace InteractiveAuthenticationSuccessEvent 7.0.x Sample
Given that 7e3bf9662cd6829982f3198d3049f4012df17395 changes
the InteractiveAuthenticationSuccessEvent serialization sample,
this commit syncs up the 7.0.x version to match.

Closes gh-16276
2025-09-02 14:18:25 -06:00
Josh Cummings
dc0ab4c805
Merge branch '6.5.x' 2025-09-02 14:15:20 -06:00
Josh Cummings
c982753d46
Replace InteractiveAuthenticationSuccessEvent 6.5.x Sample
Given that 7e3bf9662cd6829982f3198d3049f4012df17395 changes
the InteractiveAuthenticationSuccessEvent serialization sample,
this commit syncs up the 6.5.x version to match.

Issue gh-16276
2025-09-02 14:14:13 -06:00
Fridolin Jackstadt
910df479be Provider Default Timeouts For JWK Retrieval
Issue gh-14269

Signed-off-by: Fridolin Jackstadt <fridolin.jackstadt@unic.com>
2025-09-02 08:51:10 -06:00
Rob Winch
9866435946
Fix security-nullability plugin in taglibs
Issue gh-17828
2025-08-30 20:44:29 -05:00
Rob Winch
5370f1190f
Enable Null checking in spring-security-taglibs via JSpecify
Closes gh-17828
2025-08-30 20:40:34 -05:00
Rob Winch
f13d8d5c75
Fix Nullability in WebInvocationPrivilegeEvaluator
Issue gh-17535
2025-08-30 20:38:58 -05:00
Rob Winch
1216ee598f
Enable Null checking in spring-security-rsocket via JSpecify
Closes gh-16882
2025-08-30 20:04:32 -05:00
Rob Winch
a4a4908d71
Enable Null checking in spring-security-cas via JSpecify
Closes gh-16882
2025-08-30 11:22:30 -05:00
Josh Cummings
0ff9f10696
Merge branch '6.4.x' into 6.5.x 2025-08-30 10:00:45 -06:00
Josh Cummings
7e3bf9662c
Polish InteractiveAuthenticationSuccessEvent Sample
The sample better matches a value that would be used in the constructor

Issue gh-16276
2025-08-30 10:00:24 -06:00
Rob Winch
be64c67af5
Enable Null checking in spring-security-web via JSpecify
Closes gh-16882
2025-08-29 16:17:49 -05:00
Rob Winch
a58f3282d9
Fix config/src/test/kotlin nullability for web
Issue gh-17535
2025-08-29 15:46:08 -05:00
Rob Winch
c2ba662b91
Enable Null checking in spring-security-web via JSpecify
Closes gh-17535
2025-08-29 15:06:48 -05:00
Rob Winch
49f308adb0
Use Supplier<? extends @Nullable Authentication>
Previously Supplier<@Nullable Authentication> was used. This prevented
Supplier<Authentication> from being used. The code now uses
Supplier<? extends @Nullable Authentication> which allows for both
Supplier<@Nullable Authentication> and Supplier<Authentication>.

Closes gh-17814
2025-08-29 09:46:58 -05:00
Josh Cummings
4cbe8de7ea Polish RSocket Anonymous Support
Changed the DSL method name to anonymous to align with jwt.
Since basicAuthenication is deprecated, we don't need to
align with its naming convention.

Also added a since attribute to the method.

Issue gh-17132
2025-08-26 17:33:40 -06:00
Andrey Litvitski
559b73b39f Add Disabling Anonymous Authentication in RSocketSecurity
Closes: gh-17132

Signed-off-by: Andrey Litvitski <andrey1010102008@gmail.com>

1

Signed-off-by: Andrey Litvitski <andrey1010102008@gmail.com>

1

Signed-off-by: Andrey Litvitski <andrey1010102008@gmail.com>
2025-08-26 17:33:40 -06:00
Andrey Litvitski
3278f3a410 Add discoverJwsAlgorithms() in NimbusJwtDecoder
Closes: gh-17785
Signed-off-by: Andrey Litvitski <andrey1010102008@gmail.com>
2025-08-26 17:07:47 -06:00
Josh Cummings
36f1de945f
Add OneTimeTokenAuthentication
Closes gh-17799
2025-08-22 15:46:54 -06:00
Josh Cummings
6663eea65f
Polish OTT Tests
Improve tests so that they do not rely on OneTimeTokenAuthenticationToken
as the concrete type.

Issue gh-17799
2025-08-22 15:46:53 -06:00
Josh Cummings
89b2f9cf54
Improve Test Runnability in IDE
In some configurations, Configuration classes with static elements
may cause a test to hang. This commit changes JeeConfigurerTests
test configuration classes to use mock beans instead of referencing
them as static fields.
2025-08-22 15:46:53 -06:00
Josh Cummings
0e39685b9c Merge branch '6.5.x' 2025-08-22 12:40:41 -06:00
Josh Cummings
9d64880ea9 Merge branch '6.4.x' into 6.5.x 2025-08-22 12:40:12 -06:00
Josh Cummings
8b2a453301 Advise Favoring PostAuthorize on Reads
Closes gh-17797
2025-08-22 12:39:51 -06:00
Josh Cummings
d1962201b5 Merge branch '6.5.x' 2025-08-22 11:07:59 -06:00
Josh Cummings
857ca9c412 Merge remote-tracking branch 'origin/6.4.x' into 6.5.x 2025-08-22 11:07:37 -06:00
Nikita Konev
894105aab5 Fix traceId discrepancy in case error in servlet web
Signed-off-by: Nikita Konev <nikit.cpp@yandex.ru>
2025-08-22 11:06:37 -06:00
Rob Winch
f7f41ba6c4
Add missing @NullMarked to spring-data package-info
Issue gh-17789
2025-08-22 12:03:16 -05:00
Rob Winch
f496ded4e5
AuthorizationManager allows null Authentication
It is possible to have a null Authentication and so the
AuthorizationManager APIs should allow for passing it in.

Closes gh-17795
2025-08-22 12:03:16 -05:00
Josh Cummings
583e668c6b Remove opensaml5Test Task
Issue gh-17707
2025-08-22 09:19:20 -06:00
Rob Winch
d6a0e3bf78
Fix Nullability Imports
Issue gh-17789
2025-08-22 09:00:15 -05:00
dependabot[bot]
312c2049f3
Bump io.micrometer:micrometer-observation from 1.14.9 to 1.14.10
Bumps [io.micrometer:micrometer-observation](https://github.com/micrometer-metrics/micrometer) from 1.14.9 to 1.14.10.
- [Release notes](https://github.com/micrometer-metrics/micrometer/releases)
- [Commits](https://github.com/micrometer-metrics/micrometer/compare/v1.14.9...v1.14.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-22 04:10:44 +00:00
dependabot[bot]
3dd4686feb
Bump org.springframework.data:spring-data-bom from 2024.1.8 to 2024.1.9
Bumps [org.springframework.data:spring-data-bom](https://github.com/spring-projects/spring-data-bom) from 2024.1.8 to 2024.1.9.
- [Release notes](https://github.com/spring-projects/spring-data-bom/releases)
- [Commits](https://github.com/spring-projects/spring-data-bom/compare/2024.1.8...2024.1.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-22 03:38:28 +00:00
Rob Winch
29bb4919ca
Add Nullability to spring-security-data
Closes gh-17789
2025-08-21 13:42:27 -05:00
Rob Winch
d9210c6596
Fix Nullability 2025-08-21 13:41:02 -05:00
Rob Winch
b8b1a92ad4
Revert "Apply Nullability to spring-security-data"
This reverts commit bbcdb236984960416489b4f9d923f83d3a4cba39.
2025-08-21 13:35:39 -05:00
Rob Winch
bbcdb23698
Apply Nullability to spring-security-data 2025-08-21 13:27:47 -05:00
Rob Winch
9bbf837c7c
Merge branch '6.5.x' 2025-08-21 12:44:42 -05:00
Rob Winch
8a1e2a22f9
Merge branch 'gh-16226-servlet-test-method' into 6.5.x 2025-08-21 12:44:27 -05:00
Rob Winch
0404996f87 import Assertions.assertThat
This adds a static import for assertThat in the Kotlin docs code
2025-08-21 12:35:13 -05:00
Rob Winch
0f63d98c84 Use @EnableMethodSecurity in docs tests
Previously parameters were passed in unnecessarily. This removes
the unnecessary paramaters.
2025-08-21 12:35:13 -05:00
Rob Winch
fbfbb1e571 Use 2004-present for Copyright
Spring Security migrated the copyright to use -present to simplify
the headers. This commit aligns the header.
2025-08-21 12:35:13 -05:00
Joe Kuhel
d002e68231 Update servlet test method docs to use include-code
References gh-16226

Signed-off-by: Joe Kuhel <4983938+jkuhel@users.noreply.github.com>
2025-08-21 12:35:13 -05:00
Yanming Zhou
41162aa7e3 Polish WebFluxSecurityConfiguration
Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2025-08-21 11:16:12 -06:00
Yanming Zhou
d86f2c957d Change @Bean method signature to return RsaKeyConversionServicePostProcessor instead of BeanFactoryPostProcessor
It's friendly for Spring Boot's `@ConditionalOnMissingBean`, and:

>> When defining a Spring `@Bean` method, it is generally recommended to declare the most specific type possible as the method's return type. This means returning the concrete class of the bean, or the most specific interface that the bean implements and through which it will be referenced in the application.

Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2025-08-21 11:16:12 -06:00
Rob Winch
62b5b1a77c
import Assertions.assertThat
This adds a static import for assertThat in the Kotlin docs code
2025-08-21 11:19:05 -05:00
Rob Winch
523222c24d
Use @EnableMethodSecurity in docs tests
Previously parameters were passed in unnecessarily. This removes
the unnecessary paramaters.
2025-08-21 11:15:42 -05:00
Rob Winch
69f38d4933
Use 2004-present for Copyright
Spring Security migrated the copyright to use -present to simplify
the headers. This commit aligns the header.
2025-08-21 11:13:45 -05:00
Joe Kuhel
0179a811c7
Update servlet test method docs to use include-code
References gh-16226

Signed-off-by: Joe Kuhel <4983938+jkuhel@users.noreply.github.com>
2025-08-21 11:12:42 -05:00
Rob Winch
7ce2bdd701
Merge branch '6.5.x' 2025-08-21 08:55:57 -05:00
Rob Winch
de4ceffc4f
Merge branch '6.4.x' into 6.5.x 2025-08-21 08:55:48 -05:00
Rob Winch
8c920a7ee7
Bump org.springframework.data:spring-data-bom from 2024.1.8 to 2024.1.9 2025-08-21 08:55:15 -05:00
Rob Winch
b9653346a1
Bump org.hibernate.orm:hibernate-core from 6.6.23.Final to 6.6.26.Final 2025-08-21 08:54:19 -05:00
Rob Winch
9e6bcbd1d0
Bump io.micrometer:micrometer-observation from 1.14.9 to 1.14.10 2025-08-21 08:54:18 -05:00
dependabot[bot]
8d888edc71 Bump io.spring.nullability:io.spring.nullability.gradle.plugin
Bumps [io.spring.nullability:io.spring.nullability.gradle.plugin](https://github.com/spring-gradle-plugins/nullability-plugin) from 0.0.3 to 0.0.4.
- [Release notes](https://github.com/spring-gradle-plugins/nullability-plugin/releases)
- [Commits](https://github.com/spring-gradle-plugins/nullability-plugin/compare/v0.0.3...v0.0.4)

---
updated-dependencies:
- dependency-name: io.spring.nullability:io.spring.nullability.gradle.plugin
  dependency-version: 0.0.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-20 17:10:31 -05:00
Rob Winch
f82fe9c8c6
Remove stray modular from the documentation
Issue gh-16258
2025-08-20 12:24:33 -05:00
Rob Winch
a8f045eb50
Add Modular Spring Security Configuration
Closes gh-16258
2025-08-20 12:16:08 -05:00
Rob Winch
5c5efc9092
SpringTestContext registers WebTestClient Bean
Closes gh-17780
2025-08-20 12:15:58 -05:00
dependabot[bot]
5453224377
Bump io.micrometer:micrometer-observation from 1.14.9 to 1.14.10
Bumps [io.micrometer:micrometer-observation](https://github.com/micrometer-metrics/micrometer) from 1.14.9 to 1.14.10.
- [Release notes](https://github.com/micrometer-metrics/micrometer/releases)
- [Commits](https://github.com/micrometer-metrics/micrometer/compare/v1.14.9...v1.14.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-20 06:23:02 +00:00
dependabot[bot]
a14ad770ab
Bump org.hibernate.orm:hibernate-core from 6.6.23.Final to 6.6.26.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.23.Final to 6.6.26.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.26/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.23...6.6.26)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-20 04:41:59 +00:00
Jaehwan Lee
806297da23 Fix misleading variable name in authentication filter
Rename DEFAULT_ANT_PATH_REQUEST_MATCHER to DEFAULT_PATH_REQUEST_MATCHER
to reflect PathPatternRequestMatcher usage instead of legacy Ant
pattern terminology.

Signed-off-by: Jaehwan Lee <jhrick0129@gmail.com>
2025-08-19 22:21:35 -05:00
dependabot[bot]
8846b44b81
Bump org.springframework.data:spring-data-bom from 2024.1.8 to 2024.1.9
Bumps [org.springframework.data:spring-data-bom](https://github.com/spring-projects/spring-data-bom) from 2024.1.8 to 2024.1.9.
- [Release notes](https://github.com/spring-projects/spring-data-bom/releases)
- [Commits](https://github.com/spring-projects/spring-data-bom/compare/2024.1.8...2024.1.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-20 03:21:13 +00:00
Rob Winch
7f103b2d0a
Merge branch '6.5.x' 2025-08-19 22:19:46 -05:00
dependabot[bot]
c06ff59747
Bump org.hibernate.orm:hibernate-core from 6.6.23.Final to 6.6.26.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.23.Final to 6.6.26.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.26/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.23...6.6.26)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-19 22:19:01 -05:00
dependabot[bot]
f1194de45e
Bump org.assertj:assertj-core from 3.27.3 to 3.27.4
Bumps [org.assertj:assertj-core](https://github.com/assertj/assertj) from 3.27.3 to 3.27.4.
- [Release notes](https://github.com/assertj/assertj/releases)
- [Commits](https://github.com/assertj/assertj/compare/assertj-build-3.27.3...assertj-build-3.27.4)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-version: 3.27.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-19 22:19:01 -05:00
dependabot[bot]
1f16009c8d
Bump org.springframework.ldap:spring-ldap-core from 3.2.13 to 3.2.14
Bumps [org.springframework.ldap:spring-ldap-core](https://github.com/spring-projects/spring-ldap) from 3.2.13 to 3.2.14.
- [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.13...3.2.14)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-19 22:19:01 -05:00
Rob Winch
3e13437b1a
Merge branch '6.4.x' into 6.5.x 2025-08-19 22:16:16 -05:00
Rob Winch
20a6863e07
Bump org.hibernate.orm:hibernate-core from 6.6.23.Final to 6.6.26.Final 2025-08-19 22:15:40 -05:00
Rob Winch
e145c07d5b
Bump io.micrometer:micrometer-observation from 1.14.9 to 1.14.10 2025-08-19 22:15:38 -05:00
Rob Winch
68a7f1702f
Merge branch '6.5.x' 2025-08-19 22:15:14 -05:00
Rob Winch
3e8b606aac
Merge branch '6.4.x' into 6.5.x 2025-08-19 22:14:37 -05:00
Tran Ngoc Nhan
ef5c703010 Remove unused import
Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2025-08-19 22:05:25 -05:00
Andrey Litvitski
47be93e694 Annotate AuthenticationTrustResolver methods with @Nullable
Since AuthenticationTrustResolver can handle null arguments (this is
also stated in the implementation of this interface), we should mark
these arguments as `@Nullable`.

Closes: gh-17764

Signed-off-by: Andrey Litvitski <andrey1010102008@gmail.com>
2025-08-19 22:02:59 -05:00
dependabot[bot]
9310153d16 Bump io.spring.nullability:io.spring.nullability.gradle.plugin
Bumps [io.spring.nullability:io.spring.nullability.gradle.plugin](https://github.com/spring-gradle-plugins/nullability-plugin) from 0.0.2 to 0.0.3.
- [Release notes](https://github.com/spring-gradle-plugins/nullability-plugin/releases)
- [Commits](https://github.com/spring-gradle-plugins/nullability-plugin/compare/v0.0.2...v0.0.3)

---
updated-dependencies:
- dependency-name: io.spring.nullability:io.spring.nullability.gradle.plugin
  dependency-version: 0.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-19 21:41:13 -05:00
dependabot[bot]
a933089ec2
Bump io.micrometer:micrometer-observation from 1.14.9 to 1.14.10
Bumps [io.micrometer:micrometer-observation](https://github.com/micrometer-metrics/micrometer) from 1.14.9 to 1.14.10.
- [Release notes](https://github.com/micrometer-metrics/micrometer/releases)
- [Commits](https://github.com/micrometer-metrics/micrometer/compare/v1.14.9...v1.14.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-19 14:41:53 +00:00
dependabot[bot]
a8da9ec7f7
Bump org.hibernate.orm:hibernate-core from 6.6.23.Final to 6.6.26.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.23.Final to 6.6.26.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.26/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.23...6.6.26)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-19 14:40:42 +00:00
Josh Cummings
3396890d8b
Propagate AccessDeniedException Only to ExceptionTranslationFilter
Closes gh-17761
2025-08-18 17:04:19 -06:00
Josh Cummings
c45bc384da
Interpret Expression Templates by Default
Closes gh-17763
2025-08-18 15:45:57 -06:00
Josh Cummings
4da98dde2b
Update What's New
Issue gh-17707
2025-08-18 15:31:03 -06:00
Rob Winch
7575e4ef1c
Next development version 2025-08-18 15:17:59 -05:00
github-actions[bot]
acaed1ad96 Next development version 2025-08-18 18:15:02 +00:00
github-actions[bot]
0549bf566b Next development version 2025-08-18 18:06:07 +00:00
github-actions[bot]
44037c0ea4 Release 6.5.3 2025-08-18 17:37:38 +00:00
github-actions[bot]
01c8cea00f Release 6.4.9 2025-08-18 17:37:33 +00:00
585 changed files with 9203 additions and 1722 deletions

View File

@ -17,8 +17,6 @@ package io.spring.gradle;
import org.apache.commons.io.FileUtils;
import org.gradle.testkit.runner.GradleRunner;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import java.io.File;
import java.io.IOException;

View File

@ -23,8 +23,6 @@ import org.gradle.testfixtures.ProjectBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import java.io.File;
import static org.assertj.core.api.Assertions.assertThat;
/**

View File

@ -5,7 +5,6 @@ import org.apache.commons.io.FileUtils;
import org.gradle.testkit.runner.BuildResult;
import org.gradle.testkit.runner.TaskOutcome;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

View File

@ -1,3 +1,7 @@
plugins {
id 'security-nullability'
}
apply plugin: 'io.spring.convention.spring-module'
dependencies {

View File

@ -16,6 +16,8 @@
package org.springframework.security.cas;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
@ -34,7 +36,7 @@ public class ServiceProperties implements InitializingBean {
public static final String DEFAULT_CAS_SERVICE_PARAMETER = "service";
private String service;
private @Nullable String service;
private boolean authenticateAllArtifacts;
@ -62,7 +64,7 @@ public class ServiceProperties implements InitializingBean {
* </pre>
* @return the URL of the service the user is authenticating to
*/
public final String getService() {
public final @Nullable String getService() {
return this.service;
}

View File

@ -21,6 +21,8 @@ import org.apache.commons.logging.LogFactory;
import org.apereo.cas.client.validation.Assertion;
import org.apereo.cas.client.validation.TicketValidationException;
import org.apereo.cas.client.validation.TicketValidator;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
@ -62,6 +64,7 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
private static final Log logger = LogFactory.getLog(CasAuthenticationProvider.class);
@SuppressWarnings("NullAway.Init")
private AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService;
private UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
@ -70,11 +73,13 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
private StatelessTicketCache statelessTicketCache = new NullStatelessTicketCache();
@SuppressWarnings("NullAway.Init")
private String key;
@SuppressWarnings("NullAway.Init")
private TicketValidator ticketValidator;
private ServiceProperties serviceProperties;
private @Nullable ServiceProperties serviceProperties;
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
@ -89,7 +94,7 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
public @Nullable Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (!supports(authentication.getClass())) {
return null;
}
@ -129,11 +134,14 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
private CasAuthenticationToken authenticateNow(final Authentication authentication) throws AuthenticationException {
try {
Assertion assertion = this.ticketValidator.validate(authentication.getCredentials().toString(),
getServiceUrl(authentication));
Object credentials = authentication.getCredentials();
if (credentials == null) {
throw new BadCredentialsException("Authentication.getCredentials() cannot be null");
}
Assertion assertion = this.ticketValidator.validate(credentials.toString(), getServiceUrl(authentication));
UserDetails userDetails = loadUserByAssertion(assertion);
this.userDetailsChecker.check(userDetails);
return new CasAuthenticationToken(this.key, userDetails, authentication.getCredentials(),
return new CasAuthenticationToken(this.key, userDetails, credentials,
this.authoritiesMapper.mapAuthorities(userDetails.getAuthorities()), userDetails, assertion);
}
catch (TicketValidationException ex) {
@ -149,7 +157,8 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
* @param authentication
* @return
*/
private String getServiceUrl(Authentication authentication) {
@NullUnmarked
private @Nullable String getServiceUrl(Authentication authentication) {
String serviceUrl;
if (authentication.getDetails() instanceof ServiceAuthenticationDetails) {
return ((ServiceAuthenticationDetails) authentication.getDetails()).getServiceUrl();
@ -215,7 +224,7 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
return this.statelessTicketCache;
}
protected TicketValidator getTicketValidator() {
protected @Nullable TicketValidator getTicketValidator() {
return this.ticketValidator;
}

View File

@ -19,6 +19,8 @@ package org.springframework.security.cas.authentication;
import java.io.Serial;
import java.util.Collection;
import org.jspecify.annotations.Nullable;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.util.Assert;
@ -41,7 +43,7 @@ public class CasServiceTicketAuthenticationToken extends AbstractAuthenticationT
private final String identifier;
private Object credentials;
private @Nullable Object credentials;
/**
* This constructor can be safely used by any code that wishes to create a
@ -86,7 +88,7 @@ public class CasServiceTicketAuthenticationToken extends AbstractAuthenticationT
}
@Override
public Object getCredentials() {
public @Nullable Object getCredentials() {
return this.credentials;
}

View File

@ -16,6 +16,8 @@
package org.springframework.security.cas.authentication;
import org.jspecify.annotations.Nullable;
/**
* Implementation of @link {@link StatelessTicketCache} that has no backing cache. Useful
* in instances where storing of tickets for stateless session management is not required.
@ -33,7 +35,7 @@ public final class NullStatelessTicketCache implements StatelessTicketCache {
* @return null since we are not storing any tickets.
*/
@Override
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
public @Nullable CasAuthenticationToken getByTicketId(final String serviceTicket) {
return null;
}

View File

@ -18,6 +18,7 @@ package org.springframework.security.cas.authentication;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
import org.springframework.cache.Cache;
import org.springframework.core.log.LogMessage;
@ -42,7 +43,7 @@ public class SpringCacheBasedTicketCache implements StatelessTicketCache {
}
@Override
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
public @Nullable CasAuthenticationToken getByTicketId(final String serviceTicket) {
final Cache.ValueWrapper element = (serviceTicket != null) ? this.cache.get(serviceTicket) : null;
logger.debug(LogMessage.of(() -> "Cache hit: " + (element != null) + "; service ticket: " + serviceTicket));
return (element != null) ? (CasAuthenticationToken) element.get() : null;

View File

@ -16,6 +16,8 @@
package org.springframework.security.cas.authentication;
import org.jspecify.annotations.Nullable;
/**
* Caches CAS service tickets and CAS proxy tickets for stateless connections.
*
@ -69,7 +71,7 @@ public interface StatelessTicketCache {
* </p>
* @return the fully populated authentication token
*/
CasAuthenticationToken getByTicketId(String serviceTicket);
@Nullable CasAuthenticationToken getByTicketId(String serviceTicket);
/**
* Adds the specified <code>CasAuthenticationToken</code> to the cache.

View File

@ -18,4 +18,7 @@
* An {@code AuthenticationProvider} that can process CAS service tickets and proxy
* tickets.
*/
@NullMarked
package org.springframework.security.cas.authentication;
import org.jspecify.annotations.NullMarked;

View File

@ -0,0 +1,23 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Jackson support for CAS.
*/
@NullMarked
package org.springframework.security.cas.jackson2;
import org.jspecify.annotations.NullMarked;

View File

@ -18,4 +18,7 @@
* Spring Security support for Apereo's Central Authentication Service
* (<a href="https://github.com/apereo/cas">CAS</a>).
*/
@NullMarked
package org.springframework.security.cas;
import org.jspecify.annotations.NullMarked;

View File

@ -0,0 +1,23 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* {@link org.springframework.security.core.userdetails.UserDetails} abstractions for CAS.
*/
@NullMarked
package org.springframework.security.cas.userdetails;
import org.jspecify.annotations.NullMarked;

View File

@ -22,6 +22,7 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apereo.cas.client.util.CommonUtils;
import org.apereo.cas.client.util.WebUtils;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.cas.ServiceProperties;
@ -47,9 +48,10 @@ import org.springframework.util.Assert;
*/
public class CasAuthenticationEntryPoint implements AuthenticationEntryPoint, InitializingBean {
@SuppressWarnings("NullAway.Init")
private ServiceProperties serviceProperties;
private String loginUrl;
private @Nullable String loginUrl;
/**
* Determines whether the Service URL should include the session id for the specific
@ -117,7 +119,7 @@ public class CasAuthenticationEntryPoint implements AuthenticationEntryPoint, In
* <code>https://www.mycompany.com/cas/login</code>.
* @return the enterprise-wide CAS login URL
*/
public final String getLoginUrl() {
public final @Nullable String getLoginUrl() {
return this.loginUrl;
}

View File

@ -26,6 +26,7 @@ import jakarta.servlet.http.HttpSession;
import org.apereo.cas.client.proxy.ProxyGrantingTicketStorage;
import org.apereo.cas.client.util.WebUtils;
import org.apereo.cas.client.validation.TicketValidator;
import org.jspecify.annotations.Nullable;
import org.springframework.core.log.LogMessage;
import org.springframework.security.authentication.AuthenticationDetailsSource;
@ -190,12 +191,12 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
/**
* The last portion of the receptor url, i.e. /proxy/receptor
*/
private RequestMatcher proxyReceptorMatcher;
private @Nullable RequestMatcher proxyReceptorMatcher;
/**
* The backing storage to store ProxyGrantingTicket requests.
*/
private ProxyGrantingTicketStorage proxyGrantingTicketStorage;
private @Nullable ProxyGrantingTicketStorage proxyGrantingTicketStorage;
private String artifactParameter = ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER;
@ -244,7 +245,7 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
public @Nullable Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException {
// if the request is a proxy request process it and return null to indicate the
// request has been processed
@ -422,6 +423,7 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
* @param request
* @return
*/
@SuppressWarnings("NullAway") // Dataflow analysis limitation
private boolean proxyReceptorRequest(HttpServletRequest request) {
final boolean result = proxyReceptorConfigured() && this.proxyReceptorMatcher.matches(request);
this.logger.debug(LogMessage.format("proxyReceptorRequest = %s", result));

View File

@ -21,6 +21,7 @@ import java.net.URL;
import java.util.regex.Pattern;
import jakarta.servlet.http.HttpServletRequest;
import org.jspecify.annotations.Nullable;
import org.springframework.security.cas.authentication.ServiceAuthenticationDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
@ -49,8 +50,8 @@ final class DefaultServiceAuthenticationDetails extends WebAuthenticationDetails
* string from containing the artifact name and value. This can be created using
* {@link #createArtifactPattern(String)}.
*/
DefaultServiceAuthenticationDetails(String casService, HttpServletRequest request, Pattern artifactPattern)
throws MalformedURLException {
DefaultServiceAuthenticationDetails(@Nullable String casService, HttpServletRequest request,
Pattern artifactPattern) throws MalformedURLException {
super(request);
URL casServiceUrl = new URL(casService);
int port = getServicePort(casServiceUrl);
@ -104,7 +105,7 @@ final class DefaultServiceAuthenticationDetails extends WebAuthenticationDetails
* @return the query String minus the artifactParameterName and the corresponding
* value.
*/
private String getQueryString(final HttpServletRequest request, final Pattern artifactPattern) {
private @Nullable String getQueryString(final HttpServletRequest request, final Pattern artifactPattern) {
final String query = request.getQueryString();
if (query == null) {
return null;

View File

@ -18,4 +18,7 @@
* Authentication processing mechanisms which respond to the submission of authentication
* credentials using CAS.
*/
@NullMarked
package org.springframework.security.cas.web.authentication;
import org.jspecify.annotations.NullMarked;

View File

@ -17,4 +17,7 @@
/**
* Authenticates standard web browser users via CAS.
*/
@NullMarked
package org.springframework.security.cas.web;
import org.jspecify.annotations.NullMarked;

View File

@ -25,7 +25,6 @@ dependencies {
optional project(':spring-security-ldap')
optional project(':spring-security-messaging')
optional project(path: ':spring-security-saml2-service-provider')
opensaml5 project(path: ':spring-security-saml2-service-provider', configuration: 'opensamlFiveMain')
optional project(':spring-security-oauth2-client')
optional project(':spring-security-oauth2-jose')
optional project(':spring-security-oauth2-resource-server')
@ -170,15 +169,3 @@ test {
}
}
}
tasks.register("opensaml5Test", Test) {
filter {
includeTestsMatching "org.springframework.security.config.annotation.web.configurers.saml2.*"
}
useJUnitPlatform()
classpath = sourceSets.main.output + sourceSets.test.output + configurations.opensaml5
}
tasks.named("check") {
dependsOn opensaml5Test
}

View File

@ -18,7 +18,6 @@ package org.springframework.security.config.annotation.authentication.ldap;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.Collections;
import java.util.List;
import javax.naming.directory.SearchControls;
@ -39,7 +38,6 @@ import org.springframework.security.config.annotation.configuration.ObjectPostPr
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
@ -120,8 +118,7 @@ public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
this.spring.register(BindAuthenticationConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
.andExpect(authenticated().withUsername("bob")
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_DEVELOPERS"))));
.andExpect(authenticated().withUsername("bob").withRoles("DEVELOPERS"));
}
// SEC-2472
@ -130,8 +127,7 @@ public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
this.spring.register(PasswordEncoderConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bcrypt").password("password"))
.andExpect(authenticated().withUsername("bcrypt")
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_DEVELOPERS"))));
.andExpect(authenticated().withUsername("bcrypt").withRoles("DEVELOPERS"));
}
private LdapAuthenticationProvider ldapProvider() {

View File

@ -16,8 +16,6 @@
package org.springframework.security.config.annotation.authentication.ldap;
import java.util.Collections;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ -28,8 +26,6 @@ import org.springframework.security.config.annotation.authentication.ldap.LdapAu
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders;
import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers;
import org.springframework.test.web.servlet.MockMvc;
@ -64,7 +60,7 @@ public class LdapAuthenticationProviderConfigurerTests {
.password("bobspassword");
SecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()
.withUsername("bob")
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_DEVELOPERS")));
.withRoles("DEVELOPERS");
// @formatter:on
this.mockMvc.perform(request).andExpect(expectedUser);
}
@ -79,7 +75,7 @@ public class LdapAuthenticationProviderConfigurerTests {
.password("bobspassword");
SecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()
.withUsername("bob")
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROL_DEVELOPERS")));
.withRoles("ROL_", new String[] { "DEVELOPERS" });
// @formatter:on
this.mockMvc.perform(request).andExpect(expectedUser);
}
@ -108,8 +104,7 @@ public class LdapAuthenticationProviderConfigurerTests {
.password("otherbenspassword");
SecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()
.withUsername("otherben")
.withAuthorities(
AuthorityUtils.createAuthorityList("ROLE_SUBMANAGERS", "ROLE_MANAGERS", "ROLE_DEVELOPERS"));
.withRoles("SUBMANAGERS", "MANAGERS", "DEVELOPERS");
// @formatter:on
this.mockMvc.perform(request).andExpect(expectedUser);
}

View File

@ -16,7 +16,6 @@
package org.springframework.security.config.annotation.authentication.ldap;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@ -34,7 +33,6 @@ import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders;
@ -79,7 +77,7 @@ public class NamespaceLdapAuthenticationProviderTests {
.user("bob")
.password("bobspassword");
SecurityMockMvcResultMatchers.AuthenticatedMatcher user = authenticated()
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("PREFIX_DEVELOPERS")));
.withRoles("PREFIX_", new String[] { "DEVELOPERS" });
// @formatter:on
this.mockMvc.perform(request).andExpect(user);
}
@ -103,7 +101,7 @@ public class NamespaceLdapAuthenticationProviderTests {
.user("bob")
.password("bobspassword");
SecurityMockMvcResultMatchers.AuthenticatedMatcher user = authenticated()
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_EXTRA")));
.withRoles("EXTRA");
// @formatter:on
this.mockMvc.perform(request).andExpect(user);
}

View File

@ -0,0 +1,147 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.rsocket;
import java.util.ArrayList;
import java.util.List;
import io.rsocket.core.RSocketServer;
import io.rsocket.exceptions.RejectedSetupException;
import io.rsocket.frame.decoder.PayloadDecoder;
import io.rsocket.transport.netty.server.CloseableChannel;
import io.rsocket.transport.netty.server.TcpServerTransport;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.rsocket.RSocketRequester;
import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.rsocket.core.PayloadSocketAcceptorInterceptor;
import org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor;
import org.springframework.security.rsocket.util.matcher.PayloadExchangeAuthorizationContext;
import org.springframework.stereotype.Controller;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* @author Andrey Litvitski
*/
@ContextConfiguration
@ExtendWith(SpringExtension.class)
public class AnonymousAuthenticationITests {
@Autowired
RSocketMessageHandler handler;
@Autowired
SecuritySocketAcceptorInterceptor interceptor;
@Autowired
ServerController controller;
private CloseableChannel server;
private RSocketRequester requester;
@BeforeEach
public void setup() {
// @formatter:off
this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
)
.acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0))
.block();
// @formatter:on
}
@AfterEach
public void dispose() {
this.requester.rsocket().dispose();
this.server.dispose();
this.controller.payloads.clear();
}
@Test
public void requestWhenAnonymousDisabledThenRespondsWithForbidden() {
this.requester = RSocketRequester.builder()
.rsocketStrategies(this.handler.getRSocketStrategies())
.connectTcp("localhost", this.server.address().getPort())
.block();
String data = "andrew";
assertThatExceptionOfType(RejectedSetupException.class).isThrownBy(
() -> this.requester.route("secure.retrieve-mono").data(data).retrieveMono(String.class).block());
assertThat(this.controller.payloads).isEmpty();
}
@Configuration
@EnableRSocketSecurity
static class Config {
@Bean
ServerController controller() {
return new ServerController();
}
@Bean
RSocketMessageHandler messageHandler() {
return new RSocketMessageHandler();
}
@Bean
PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
ReactiveAuthorizationManager<PayloadExchangeAuthorizationContext> anonymous = (authentication,
exchange) -> authentication.map(trustResolver::isAnonymous).map(AuthorizationDecision::new);
rsocket.authorizePayload((authorize) -> authorize.anyExchange().access(anonymous));
rsocket.anonymous((anonymousAuthentication) -> anonymousAuthentication.disable());
return rsocket.build();
}
}
@Controller
static class ServerController {
private List<String> payloads = new ArrayList<>();
@MessageMapping("**")
String retrieveMono(String payload) {
add(payload);
return "Hi " + payload;
}
private void add(String p) {
this.payloads.add(p);
}
}
}

View File

@ -169,7 +169,7 @@ public class LdapProviderBeanDefinitionParserTests {
this.appCtx = new InMemoryXmlApplicationContext("<ldap-server />" + "<authentication-manager>"
+ " <ldap-authentication-provider user-dn-pattern='uid={0},ou=${udp}' group-search-filter='${gsf}={0}' />"
+ "</authentication-manager>"
+ "<b:bean id='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer' class='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer' />");
+ "<b:bean id='org.springframework.context.support.PropertySourcesPlaceholderConfigurer' class='org.springframework.context.support.PropertySourcesPlaceholderConfigurer' />");
ProviderManager providerManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, ProviderManager.class);
assertThat(providerManager.getProviders()).hasSize(1);

View File

@ -0,0 +1,52 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config;
/**
* A {@link Customizer} that allows invocation of code that throws a checked exception.
*
* @param <T> The type of input.
*/
@FunctionalInterface
public interface ThrowingCustomizer<T> extends Customizer<T> {
/**
* Default {@link Customizer#customize(Object)} that wraps any thrown checked
* exceptions (by default in a {@link RuntimeException}).
* @param t the object to customize
*/
default void customize(T t) {
try {
customizeWithException(t);
}
catch (RuntimeException ex) {
throw ex;
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
/**
* Performs the customization on the given object, possibly throwing a checked
* exception.
* @param t the object to customize
* @throws Exception on error
*/
void customizeWithException(T t) throws Exception;
}

View File

@ -102,9 +102,7 @@ class AuthorizationProxyWebConfiguration implements WebMvcConfigurer {
Throwable accessDeniedException = this.throwableAnalyzer
.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
if (accessDeniedException != null) {
return new ModelAndView((model, req, res) -> {
throw ex;
});
throw (AccessDeniedException) accessDeniedException;
}
return null;
}

View File

@ -109,6 +109,7 @@ import org.springframework.security.rsocket.util.matcher.RoutePayloadExchangeMat
* @author Manuel Tejeda
* @author Ebert Toribio
* @author Ngoc Nhan
* @author Andrey Litvitski
* @since 5.2
*/
public class RSocketSecurity {
@ -119,6 +120,8 @@ public class RSocketSecurity {
private SimpleAuthenticationSpec simpleAuthSpec;
private AnonymousAuthenticationSpec anonymousAuthSpec = new AnonymousAuthenticationSpec(this);
private JwtSpec jwtSpec;
private AuthorizePayloadsSpec authorizePayload;
@ -164,6 +167,20 @@ public class RSocketSecurity {
return this;
}
/**
* Adds anonymous authentication
* @param anonymous a customizer
* @return this instance
* @since 7.0
*/
public RSocketSecurity anonymous(Customizer<AnonymousAuthenticationSpec> anonymous) {
if (this.anonymousAuthSpec == null) {
this.anonymousAuthSpec = new AnonymousAuthenticationSpec(this);
}
anonymous.customize(this.anonymousAuthSpec);
return this;
}
/**
* Adds authentication with BasicAuthenticationPayloadExchangeConverter.
* @param basic
@ -214,7 +231,9 @@ public class RSocketSecurity {
if (this.jwtSpec != null) {
result.addAll(this.jwtSpec.build());
}
result.add(anonymous());
if (this.anonymousAuthSpec != null) {
result.add(this.anonymousAuthSpec.build());
}
if (this.authorizePayload != null) {
result.add(this.authorizePayload.build());
}
@ -222,12 +241,6 @@ public class RSocketSecurity {
return result;
}
private AnonymousPayloadInterceptor anonymous() {
AnonymousPayloadInterceptor result = new AnonymousPayloadInterceptor("anonymousUser");
result.setOrder(PayloadInterceptorOrder.ANONYMOUS.getOrder());
return result;
}
private <T> T getBean(Class<T> beanClass) {
if (this.context == null) {
return null;
@ -283,6 +296,26 @@ public class RSocketSecurity {
}
public final class AnonymousAuthenticationSpec {
private RSocketSecurity parent;
private AnonymousAuthenticationSpec(RSocketSecurity parent) {
this.parent = parent;
}
protected AnonymousPayloadInterceptor build() {
AnonymousPayloadInterceptor result = new AnonymousPayloadInterceptor("anonymousUser");
result.setOrder(PayloadInterceptorOrder.ANONYMOUS.getOrder());
return result;
}
public void disable() {
this.parent.anonymousAuthSpec = null;
}
}
public final class BasicAuthenticationSpec {
private ReactiveAuthenticationManager authenticationManager;

View File

@ -16,19 +16,24 @@
package org.springframework.security.config.annotation.web.configuration;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
@ -46,6 +51,7 @@ import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.function.ThrowingSupplier;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
@ -131,6 +137,8 @@ class HttpSecurityConfiguration {
// @formatter:on
applyCorsIfAvailable(http);
applyDefaultConfigurers(http);
applyHttpSecurityCustomizers(this.context, http);
applyTopLevelCustomizers(this.context, http);
return http;
}
@ -160,6 +168,73 @@ class HttpSecurityConfiguration {
}
}
/**
* Applies all {@code Customizer<HttpSecurity>} Bean instances to the
* {@link HttpSecurity} instance.
* @param applicationContext the {@link ApplicationContext} to lookup Bean instances
* @param http the {@link HttpSecurity} to apply the Beans to.
*/
private void applyHttpSecurityCustomizers(ApplicationContext applicationContext, HttpSecurity http) {
ResolvableType httpSecurityCustomizerType = ResolvableType.forClassWithGenerics(Customizer.class,
HttpSecurity.class);
ObjectProvider<Customizer<HttpSecurity>> customizerProvider = this.context
.getBeanProvider(httpSecurityCustomizerType);
// @formatter:off
customizerProvider.orderedStream().forEach((customizer) ->
customizer.customize(http)
);
// @formatter:on
}
/**
* Applies all {@link Customizer} Beans to {@link HttpSecurity}. For each public,
* non-static method in HttpSecurity that accepts a Customizer
* <ul>
* <li>Use the {@link MethodParameter} (this preserves generics) to resolve all Beans
* for that type</li>
* <li>For each {@link Customizer} Bean invoke the {@link java.lang.reflect.Method}
* with the {@link Customizer} Bean as the argument</li>
* </ul>
* @param context the {@link ApplicationContext}
* @param http the {@link HttpSecurity}
* @throws Exception
*/
private void applyTopLevelCustomizers(ApplicationContext context, HttpSecurity http) {
ReflectionUtils.MethodFilter isCustomizerMethod = (method) -> {
if (Modifier.isStatic(method.getModifiers())) {
return false;
}
if (!Modifier.isPublic(method.getModifiers())) {
return false;
}
if (!method.canAccess(http)) {
return false;
}
if (method.getParameterCount() != 1) {
return false;
}
if (method.getParameterTypes()[0] == Customizer.class) {
return true;
}
return false;
};
ReflectionUtils.MethodCallback invokeWithEachCustomizerBean = (customizerMethod) -> {
MethodParameter customizerParameter = new MethodParameter(customizerMethod, 0);
ResolvableType customizerType = ResolvableType.forMethodParameter(customizerParameter);
ObjectProvider<?> customizerProvider = context.getBeanProvider(customizerType);
// @formatter:off
customizerProvider.orderedStream().forEach((customizer) ->
ReflectionUtils.invokeMethod(customizerMethod, http, customizer)
);
// @formatter:on
};
ReflectionUtils.doWithMethods(HttpSecurity.class, invokeWithEachCustomizerBean, isCustomizerMethod);
}
private Map<Class<?>, Object> createSharedObjects() {
Map<Class<?>, Object> sharedObjects = new HashMap<>();
sharedObjects.put(ApplicationContext.class, this.context);

View File

@ -33,7 +33,6 @@ import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
@ -79,6 +78,7 @@ import org.springframework.web.filter.ServletRequestPathFilter;
*
* @author Rob Winch
* @author Keesun Baik
* @author Yanming Zhou
* @since 3.2
* @see EnableWebSecurity
* @see WebSecurity
@ -190,7 +190,7 @@ public class WebSecurityConfiguration implements ImportAware {
}
@Bean
public static BeanFactoryPostProcessor conversionServicePostProcessor() {
public static RsaKeyConversionServicePostProcessor conversionServicePostProcessor() {
return new RsaKeyConversionServicePostProcessor();
}

View File

@ -16,6 +16,7 @@
package org.springframework.security.config.annotation.web.reactive;
import java.lang.reflect.Modifier;
import java.util.Map;
import org.springframework.beans.BeansException;
@ -28,10 +29,13 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.ReactiveAdapterRegistry;
import org.springframework.core.ResolvableType;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
import org.springframework.security.authentication.password.ReactiveCompromisedPasswordChecker;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.ObjectPostProcessor;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
@ -40,6 +44,7 @@ import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.reactive.result.method.annotation.AuthenticationPrincipalArgumentResolver;
import org.springframework.security.web.reactive.result.method.annotation.CurrentSecurityContextArgumentResolver;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer;
@ -154,6 +159,83 @@ class ServerHttpSecurityConfiguration {
@Bean(HTTPSECURITY_BEAN_NAME)
@Scope("prototype")
ServerHttpSecurity httpSecurity(ApplicationContext context) {
ServerHttpSecurity http = httpSecurity();
applyServerHttpSecurityCustomizers(context, http);
applyTopLevelBeanCustomizers(context, http);
return http;
}
/**
* Applies all {@code Custmizer<ServerHttpSecurity>} Beans to
* {@link ServerHttpSecurity}.
* @param context the {@link ApplicationContext}
* @param http the {@link ServerHttpSecurity}
* @throws Exception
*/
private void applyServerHttpSecurityCustomizers(ApplicationContext context, ServerHttpSecurity http) {
ResolvableType httpSecurityCustomizerType = ResolvableType.forClassWithGenerics(Customizer.class,
ServerHttpSecurity.class);
ObjectProvider<Customizer<ServerHttpSecurity>> customizerProvider = context
.getBeanProvider(httpSecurityCustomizerType);
// @formatter:off
customizerProvider.orderedStream().forEach((customizer) ->
customizer.customize(http)
);
// @formatter:on
}
/**
* Applies all {@link Customizer} Beans to top level {@link ServerHttpSecurity}
* method.
*
* For each public, non-static method in ServerHttpSecurity that accepts a Customizer
* <ul>
* <li>Use the {@link MethodParameter} (this preserves generics) to resolve all Beans
* for that type</li>
* <li>For each {@link Customizer} Bean invoke the {@link java.lang.reflect.Method}
* with the {@link Customizer} Bean as the argument</li>
* </ul>
* @param context the {@link ApplicationContext}
* @param http the {@link ServerHttpSecurity}
* @throws Exception
*/
private void applyTopLevelBeanCustomizers(ApplicationContext context, ServerHttpSecurity http) {
ReflectionUtils.MethodFilter isCustomizerMethod = (method) -> {
if (Modifier.isStatic(method.getModifiers())) {
return false;
}
if (!Modifier.isPublic(method.getModifiers())) {
return false;
}
if (!method.canAccess(http)) {
return false;
}
if (method.getParameterCount() != 1) {
return false;
}
if (method.getParameterTypes()[0] == Customizer.class) {
return true;
}
return false;
};
ReflectionUtils.MethodCallback invokeWithEachCustomizerBean = (customizerMethod) -> {
MethodParameter customizerParameter = new MethodParameter(customizerMethod, 0);
ResolvableType customizerType = ResolvableType.forMethodParameter(customizerParameter);
ObjectProvider<?> customizerProvider = context.getBeanProvider(customizerType);
// @formatter:off
customizerProvider.orderedStream().forEach((customizer) ->
ReflectionUtils.invokeMethod(customizerMethod, http, customizer)
);
// @formatter:on
};
ReflectionUtils.doWithMethods(ServerHttpSecurity.class, invokeWithEachCustomizerBean, isCustomizerMethod);
}
ServerHttpSecurity httpSecurity() {
ContextAwareServerHttpSecurity http = new ContextAwareServerHttpSecurity();
// @formatter:off

View File

@ -21,7 +21,6 @@ import java.util.List;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -44,12 +43,13 @@ import static org.springframework.security.config.Customizer.withDefaults;
/**
* @author Rob Winch
* @author Yanming Zhou
* @since 5.0
*/
@Configuration(proxyBeanMethods = false)
class WebFluxSecurityConfiguration {
public static final int WEB_FILTER_CHAIN_FILTER_ORDER = 0 - 100;
public static final int WEB_FILTER_CHAIN_FILTER_ORDER = -100;
private static final String BEAN_NAME_PREFIX = "org.springframework.security.config.annotation.web.reactive.WebFluxSecurityConfiguration.";
@ -100,7 +100,7 @@ class WebFluxSecurityConfiguration {
}
@Bean
static BeanFactoryPostProcessor conversionServicePostProcessor() {
static RsaKeyConversionServicePostProcessor conversionServicePostProcessor() {
return new RsaKeyConversionServicePostProcessor();
}

View File

@ -20,6 +20,7 @@ import java.util.Map;
import java.util.function.Supplier;
import org.aopalliance.intercept.MethodInvocation;
import org.jspecify.annotations.Nullable;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AopUtils;
@ -37,7 +38,8 @@ class PointcutDelegatingAuthorizationManager implements AuthorizationManager<Met
}
@Override
public AuthorizationResult authorize(Supplier<Authentication> authentication, MethodInvocation object) {
public AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,
MethodInvocation object) {
for (Map.Entry<Pointcut, AuthorizationManager<MethodInvocation>> entry : this.managers.entrySet()) {
Class<?> targetClass = (object.getThis() != null) ? AopUtils.getTargetClass(object.getThis()) : null;
if (entry.getKey().getClassFilter().matches(targetClass)

View File

@ -1316,6 +1316,10 @@ public class ServerHttpSecurity {
return this.context.getBeanNamesForType(beanClass);
}
ApplicationContext getApplicationContext() {
return this.context;
}
protected void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
@ -3029,7 +3033,8 @@ public class ServerHttpSecurity {
/**
* Configures the logout handler. Default is
* {@code SecurityContextServerLogoutHandler}
* {@code SecurityContextServerLogoutHandler}. This clears any previous handlers
* configured.
* @param logoutHandler
* @return the {@link LogoutSpec} to configure
*/
@ -3045,6 +3050,18 @@ public class ServerHttpSecurity {
return this;
}
/**
* Allows managing the list of {@link ServerLogoutHandler} instances.
* @param handlersConsumer {@link Consumer} for managing the list of handlers.
* @return the {@link LogoutSpec} to configure
* @since 7.0
*/
public LogoutSpec logoutHandler(Consumer<List<ServerLogoutHandler>> handlersConsumer) {
Assert.notNull(handlersConsumer, "consumer cannot be null");
handlersConsumer.accept(this.logoutHandlers);
return this;
}
/**
* Configures what URL a POST to will trigger a log out.
* @param logoutUrl the url to trigger a log out (i.e. "/signout" would mean a

View File

@ -25,6 +25,7 @@ import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import org.jspecify.annotations.Nullable;
import org.w3c.dom.Element;
import org.springframework.beans.BeansException;
@ -458,7 +459,7 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
}
@Override
public AuthorizationResult authorize(Supplier<Authentication> authentication,
public AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,
MessageAuthorizationContext<?> object) {
EvaluationContext context = this.expressionHandler.createEvaluationContext(authentication, object);
boolean granted = ExpressionUtils.evaluateAsBoolean(this.expression, context);

View File

@ -29,7 +29,6 @@ import org.springframework.security.config.annotation.web.configurers.AuthorizeH
import org.springframework.security.config.core.GrantedAuthorityDefaults
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.util.matcher.AnyRequestMatcher
@ -235,13 +234,13 @@ class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl {
* Specify that URLs are allowed by anyone.
*/
val permitAll: AuthorizationManager<RequestAuthorizationContext> =
AuthorizationManager { _: Supplier<Authentication>, _: RequestAuthorizationContext -> AuthorizationDecision(true) }
AuthorizationManager { _: Supplier<out Authentication>, _: RequestAuthorizationContext -> AuthorizationDecision(true) }
/**
* Specify that URLs are not allowed by anyone.
*/
val denyAll: AuthorizationManager<RequestAuthorizationContext> =
AuthorizationManager { _: Supplier<Authentication>, _: RequestAuthorizationContext -> AuthorizationDecision(false) }
AuthorizationManager { _: Supplier<out Authentication>, _: RequestAuthorizationContext -> AuthorizationDecision(false) }
/**
* Specify that URLs are allowed by any authenticated user.

View File

@ -18,14 +18,22 @@ package org.springframework.security.config.annotation.web
import jakarta.servlet.Filter
import jakarta.servlet.http.HttpServletRequest
import org.springframework.beans.factory.ObjectProvider
import org.springframework.context.ApplicationContext
import org.springframework.core.MethodParameter
import org.springframework.core.ResolvableType
import org.springframework.core.annotation.AnnotationUtils
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.config.Customizer
import org.springframework.security.config.annotation.SecurityConfigurerAdapter
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository
import org.springframework.security.web.DefaultSecurityFilterChain
import org.springframework.security.web.util.matcher.RequestMatcher
import org.springframework.util.ReflectionUtils
import java.lang.reflect.Method
import java.lang.reflect.Modifier
/**
* Configures [HttpSecurity] using a [HttpSecurity Kotlin DSL][HttpSecurityDsl].
@ -77,6 +85,117 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
var authenticationManager: AuthenticationManager? = null
val context: ApplicationContext = http.getSharedObject(ApplicationContext::class.java)
init {
applyFunction1HttpSecurityDslBeans(this.context, this)
applyTopLevelFunction1SecurityDslBeans(this.context, this)
}
/**
* Applies all `Function1<HttpSecurity,Unit>` Beans which
* allows exposing the DSL as Beans to be applied.
*
* ```
* @Bean
* fun httpSecurityDslBean(): HttpSecurityDsl.() -> Unit {
* return {
* headers {
* contentSecurityPolicy {
* policyDirectives = "object-src 'none'"
* }
* }
* redirectToHttps { }
* }
* }
* ```
*/
private fun applyFunction1HttpSecurityDslBeans(context: ApplicationContext, http: HttpSecurityDsl) : Unit {
val httpSecurityDslFnType = ResolvableType.forClassWithGenerics(Function1::class.java,
HttpSecurityDsl::class.java, Unit::class.java)
val httpSecurityDslFnProvider = context
.getBeanProvider<Function1<HttpSecurityDsl,Unit>>(httpSecurityDslFnType)
// @formatter:off
httpSecurityDslFnProvider.orderedStream().forEach { fn -> fn.invoke(http) }
// @formatter:on
}
/**
* Applies all `Function1<T,Unit>` Beans such that `T` is a top level
* DSL on `HttpSecurityDsl`. This allows exposing the top level
* DSLs as Beans to be applied.
*
*
* ```
* @Bean
* fun httpSecurityCustomizer(): ThrowingCustomizer<HttpSecurity> {
* return ThrowingCustomizer { http -> http
* .headers { headers -> headers
* .contentSecurityPolicy { csp -> csp
* .policyDirectives("object-src 'none'")
* }
* }
* .redirectToHttps(Customizer.withDefaults())
* }
* }
* ```
*
* @param context the [ApplicationContext]
* @param http the [HttpSecurity]
* @throws Exception
*/
private fun applyTopLevelFunction1SecurityDslBeans(context: ApplicationContext, http: HttpSecurityDsl) {
val isCustomizerMethod = ReflectionUtils.MethodFilter { method: Method ->
if (Modifier.isStatic(method.modifiers)) {
return@MethodFilter false
}
if (!Modifier.isPublic(method.modifiers)) {
return@MethodFilter false
}
if (!method.canAccess(http)) {
return@MethodFilter false
}
if (method.parameterCount != 1) {
return@MethodFilter false
}
return@MethodFilter extractDslType(method) != null
}
val invokeWithEachDslBean = ReflectionUtils.MethodCallback { dslMethod: Method ->
val dslFunctionType = firstMethodResolvableType(dslMethod)!!
val dslFunctionProvider: ObjectProvider<*> = context.getBeanProvider<Any>(dslFunctionType)
// @formatter:off
dslFunctionProvider.orderedStream().forEach {customizer: Any -> ReflectionUtils.invokeMethod(dslMethod, http, customizer)}
}
ReflectionUtils.doWithMethods(HttpSecurityDsl::class.java, invokeWithEachDslBean, isCustomizerMethod)
}
/**
* From a `Method` with the first argument `Function<T,Unit>` return `T` or `null`
* if the first argument is not a `Function`.
* @return From a `Method` with the first argument `Function<T,Unit>` return `T`.
*/
private fun extractDslType(method: Method): ResolvableType? {
val functionType = firstMethodResolvableType(method)
if (!Function::class.java.isAssignableFrom(functionType.toClass())) {
return null
}
val functionInputType = functionType.getGeneric(0)
val securityMarker = AnnotationUtils.findAnnotation(functionInputType.toClass(), SecurityMarker::class.java)
val isSecurityDsl = securityMarker != null
if (!isSecurityDsl) {
return null
}
return functionInputType
}
private fun firstMethodResolvableType(method: Method): ResolvableType {
val parameter = MethodParameter(
method, 0
)
return ResolvableType.forMethodParameter(parameter)
}
/**
* Applies a [SecurityConfigurerAdapter] to this [HttpSecurity]
*

View File

@ -33,6 +33,7 @@ import org.springframework.security.web.util.matcher.RequestMatcher
* @property channelProcessors the [ChannelProcessor] instances to use in
* [ChannelDecisionManagerImpl]
*/
@Deprecated(message="since 6.5 use redirectToHttps instead")
class RequiresChannelDsl : AbstractRequestMatcherDsl() {
private val channelSecurityRules = mutableListOf<AuthorizationRule>()

View File

@ -16,13 +16,23 @@
package org.springframework.security.config.web.server
import org.springframework.beans.factory.ObjectProvider
import org.springframework.context.ApplicationContext
import org.springframework.core.MethodParameter
import org.springframework.core.ResolvableType
import org.springframework.core.annotation.AnnotationUtils
import org.springframework.security.authentication.ReactiveAuthenticationManager
import org.springframework.security.config.Customizer
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository
import org.springframework.security.web.server.SecurityWebFilterChain
import org.springframework.security.web.server.context.ServerSecurityContextRepository
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher
import org.springframework.util.ReflectionUtils
import org.springframework.web.server.ServerWebExchange
import org.springframework.web.server.WebFilter
import java.lang.reflect.Method
import java.lang.reflect.Modifier
/**
* Configures [ServerHttpSecurity] using a [ServerHttpSecurity Kotlin DSL][ServerHttpSecurityDsl].
@ -68,6 +78,115 @@ class ServerHttpSecurityDsl(private val http: ServerHttpSecurity, private val in
var authenticationManager: ReactiveAuthenticationManager? = null
var securityContextRepository: ServerSecurityContextRepository? = null
init {
applyFunction1HttpSecurityDslBeans(this.http.applicationContext, this)
applyTopLevelFunction1SecurityDslBeans(this.http.applicationContext, this)
}
/**
* Applies all `Function1<ServerHttpSecurityDsl,Unit>` Beans which
* allows exposing the DSL as Beans to be applied.
*
* ```
* @Bean
* @Order(Ordered.LOWEST_PRECEDENCE)
* fun userAuthorization(): ServerHttpSecurityDsl.() -> Unit {
* // @formatter:off
* return {
* authorizeExchange {
* authorize("/user/profile", hasRole("USER"))
* }
* }
* // @formatter:on
* }
* ```
*/
private fun applyFunction1HttpSecurityDslBeans(context: ApplicationContext, http: ServerHttpSecurityDsl) : Unit {
val httpSecurityDslFnType = ResolvableType.forClassWithGenerics(Function1::class.java,
ServerHttpSecurityDsl::class.java, Unit::class.java)
val httpSecurityDslFnProvider = context
.getBeanProvider<Function1<ServerHttpSecurityDsl,Unit>>(httpSecurityDslFnType)
// @formatter:off
httpSecurityDslFnProvider.orderedStream().forEach { fn -> fn.invoke(http) }
// @formatter:on
}
/**
* Applies all `Function1<T,Unit>` Beans such that `T` is a top level
* DSL on `ServerHttpSecurityDsl`. This allows exposing the top level
* DSLs as Beans to be applied.
*
* ```
* @Bean
* fun headersSecurity(): Customizer<ServerHttpSecurity.HeaderSpec> {
* // @formatter:off
* return Customizer { headers -> headers
* .contentSecurityPolicy { csp -> csp
* .policyDirectives("object-src 'none'")
* }
* }
* // @formatter:on
* }
* ```
*
* @param context the [ApplicationContext]
* @param http the [HttpSecurity]
* @throws Exception
*/
private fun applyTopLevelFunction1SecurityDslBeans(context: ApplicationContext, http: ServerHttpSecurityDsl) {
val isCustomizerMethod = ReflectionUtils.MethodFilter { method: Method ->
if (Modifier.isStatic(method.modifiers)) {
return@MethodFilter false
}
if (!Modifier.isPublic(method.modifiers)) {
return@MethodFilter false
}
if (!method.canAccess(http)) {
return@MethodFilter false
}
if (method.parameterCount != 1) {
return@MethodFilter false
}
return@MethodFilter extractDslType(method) != null
}
val invokeWithEachDslBean = ReflectionUtils.MethodCallback { dslMethod: Method ->
val dslFunctionType = firstMethodResolvableType(dslMethod)!!
val dslFunctionProvider: ObjectProvider<*> = context.getBeanProvider<Any>(dslFunctionType)
// @formatter:off
dslFunctionProvider.orderedStream().forEach {customizer: Any -> ReflectionUtils.invokeMethod(dslMethod, http, customizer)}
}
ReflectionUtils.doWithMethods(ServerHttpSecurityDsl::class.java, invokeWithEachDslBean, isCustomizerMethod)
}
/**
* From a `Method` with the first argument `Function<T,Unit>` return `T` or `null`
* if the first argument is not a `Function`.
* @return From a `Method` with the first argument `Function<T,Unit>` return `T`.
*/
private fun extractDslType(method: Method): ResolvableType? {
val functionType = firstMethodResolvableType(method)
if (!Function::class.java.isAssignableFrom(functionType.toClass())) {
return null
}
val functionInputType = functionType.getGeneric(0)
val securityMarker = AnnotationUtils.findAnnotation(functionInputType.toClass(), ServerSecurityMarker::class.java)
val isSecurityDsl = securityMarker != null
if (!isSecurityDsl) {
return null
}
return functionInputType
}
private fun firstMethodResolvableType(method: Method): ResolvableType {
val parameter = MethodParameter(
method, 0
)
return ResolvableType.forMethodParameter(parameter)
}
/**
* Allows configuring the [ServerHttpSecurity] to only be invoked when matching the
* provided [ServerWebExchangeMatcher].

View File

@ -78,6 +78,7 @@ import org.springframework.security.authentication.jaas.event.JaasAuthentication
import org.springframework.security.authentication.jaas.event.JaasAuthenticationSuccessEvent;
import org.springframework.security.authentication.ott.DefaultOneTimeToken;
import org.springframework.security.authentication.ott.InvalidOneTimeTokenException;
import org.springframework.security.authentication.ott.OneTimeTokenAuthentication;
import org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken;
import org.springframework.security.authentication.password.CompromisedPasswordException;
import org.springframework.security.authorization.AuthorityAuthorizationDecision;
@ -189,6 +190,7 @@ import org.springframework.security.saml2.provider.service.registration.OpenSaml
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;
import org.springframework.security.web.PortResolverImpl;
import org.springframework.security.web.authentication.AuthenticationFilter;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException;
@ -400,6 +402,8 @@ final class SerializationSamples {
});
generatorByClassName.put(OneTimeTokenAuthenticationToken.class,
(r) -> applyDetails(new OneTimeTokenAuthenticationToken("username", "token")));
generatorByClassName.put(OneTimeTokenAuthentication.class,
(r) -> applyDetails(new OneTimeTokenAuthentication("username", authentication.getAuthorities())));
generatorByClassName.put(AccessDeniedException.class,
(r) -> new AccessDeniedException("access denied", new RuntimeException()));
generatorByClassName.put(AuthorizationServiceException.class,
@ -453,7 +457,7 @@ final class SerializationSamples {
generatorByClassName.put(AuthenticationSuccessEvent.class,
(r) -> new AuthenticationSuccessEvent(authentication));
generatorByClassName.put(InteractiveAuthenticationSuccessEvent.class,
(r) -> new InteractiveAuthenticationSuccessEvent(authentication, Authentication.class));
(r) -> new InteractiveAuthenticationSuccessEvent(authentication, AuthenticationFilter.class));
generatorByClassName.put(LogoutSuccessEvent.class, (r) -> new LogoutSuccessEvent(authentication));
generatorByClassName.put(JaasAuthenticationFailedEvent.class,
(r) -> new JaasAuthenticationFailedEvent(authentication, new RuntimeException("message")));

View File

@ -32,6 +32,7 @@ import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.SecurityAssertions;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.ObjectPostProcessor;
@ -44,7 +45,6 @@ import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.PasswordEncodedUser;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
@ -107,8 +107,7 @@ public class AuthenticationManagerBuilderTests {
.getAuthenticationManager();
Authentication auth = manager
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("user", "password"));
assertThat(auth.getName()).isEqualTo("user");
assertThat(auth.getAuthorities()).extracting(GrantedAuthority::getAuthority).containsOnly("ROLE_USER");
SecurityAssertions.assertThat(auth).name("user").hasAuthority("ROLE_USER");
}
@Test
@ -119,8 +118,7 @@ public class AuthenticationManagerBuilderTests {
.getAuthenticationManager();
Authentication auth = manager
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("user", "password"));
assertThat(auth.getName()).isEqualTo("user");
assertThat(auth.getAuthorities()).extracting(GrantedAuthority::getAuthority).containsOnly("ROLE_USER");
SecurityAssertions.assertThat(auth).name("user").hasAuthority("ROLE_USER");
}
@Test

View File

@ -33,6 +33,7 @@ import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.ObservationTextPublisher;
import jakarta.annotation.security.DenyAll;
import jakarta.servlet.RequestDispatcher;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
@ -138,6 +139,7 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.hamcrest.Matchers.nullValue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
@ -149,6 +151,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
@ -1279,6 +1282,19 @@ public class PrePostMethodSecurityConfigurationTests {
this.mvc.perform(requestWithUser).andExpect(status().isForbidden());
}
// gh-17761
@Test
void getWhenPostAuthorizeAuthenticationNameNotMatchThenNoExceptionExposedInRequest() throws Exception {
this.spring.register(WebMvcMethodSecurityConfig.class, BasicController.class).autowire();
// @formatter:off
MockHttpServletRequestBuilder requestWithUser = get("/authorized-person")
.param("name", "john")
.with(user("rob"));
// @formatter:on
this.mvc.perform(requestWithUser)
.andExpect(request().attribute(RequestDispatcher.ERROR_EXCEPTION, nullValue()));
}
@Test
void getWhenPostAuthorizeWithinServiceAuthenticationNameMatchesThenRespondsWithOk() throws Exception {
this.spring.register(WebMvcMethodSecurityConfig.class, BasicController.class, BasicService.class).autowire();

View File

@ -25,6 +25,7 @@ import javax.security.auth.login.LoginContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ -310,7 +311,7 @@ public class NamespaceHttpTests {
}
@Override
public AuthorizationResult authorize(Supplier<Authentication> authentication,
public AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,
RequestAuthorizationContext object) {
HttpServletRequest request = object.getRequest();
FilterInvocation invocation = new FilterInvocation(request.getContextPath(), request.getServletPath(),

View File

@ -28,14 +28,20 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.event.EventListener;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.access.AccessDeniedException;
@ -54,6 +60,7 @@ import org.springframework.security.config.annotation.SecurityContextChangedList
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.AnonymousConfigurer;
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
@ -82,12 +89,15 @@ import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.willAnswer;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.withSettings;
import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
@ -425,6 +435,77 @@ public class HttpSecurityConfigurationTests {
.andExpectAll(status().isFound(), redirectedUrl("/"), authenticated());
}
@Test
void authorizeHttpRequestsCustomizerBean() throws Exception {
this.spring.register(AuthorizeRequestsBeanConfiguration.class, UserDetailsConfig.class).autowire();
Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeRequests = this.spring
.getContext()
.getBean("authorizeRequests", Customizer.class);
verify(authorizeRequests).customize(any());
}
@Test
void multiAuthorizeHttpRequestsCustomizerBean() throws Exception {
this.spring.register(MultiAuthorizeRequestsBeanConfiguration.class, UserDetailsConfig.class).autowire();
Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeRequests0 = this.spring
.getContext()
.getBean("authorizeRequests0", Customizer.class);
Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeRequests = this.spring
.getContext()
.getBean("authorizeRequests", Customizer.class);
InOrder inOrder = Mockito.inOrder(authorizeRequests0, authorizeRequests);
ArgumentCaptor<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> arg0 = ArgumentCaptor
.forClass(AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry.class);
ArgumentCaptor<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> arg1 = ArgumentCaptor
.forClass(AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry.class);
inOrder.verify(authorizeRequests0).customize(arg0.capture());
inOrder.verify(authorizeRequests).customize(arg1.capture());
}
@Test
void disableAuthorizeHttpRequestsCustomizerBean() throws Exception {
this.spring.register(AuthorizeRequestsBeanConfiguration.class, UserDetailsConfig.class).autowire();
Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeRequests = this.spring
.getContext()
.getBean("authorizeRequests", Customizer.class);
verify(authorizeRequests).customize(any());
}
@Test
void httpSecurityCustomizerBean() throws Exception {
this.spring.register(HttpSecurityCustomizerBeanConfiguration.class, UserDetailsConfig.class).autowire();
Customizer<HttpSecurity> httpSecurityCustomizer = this.spring.getContext()
.getBean("httpSecurityCustomizer", Customizer.class);
ArgumentCaptor<HttpSecurity> arg0 = ArgumentCaptor.forClass(HttpSecurity.class);
verify(httpSecurityCustomizer).customize(arg0.capture());
}
@Test
void multiHttpSecurityCustomizerBean() throws Exception {
this.spring.register(MultiHttpSecurityCustomizerBeanConfiguration.class, UserDetailsConfig.class).autowire();
Customizer<HttpSecurity> httpSecurityCustomizer = this.spring.getContext()
.getBean("httpSecurityCustomizer", Customizer.class);
Customizer<HttpSecurity> httpSecurityCustomizer0 = this.spring.getContext()
.getBean("httpSecurityCustomizer0", Customizer.class);
InOrder inOrder = Mockito.inOrder(httpSecurityCustomizer0, httpSecurityCustomizer);
ArgumentCaptor<HttpSecurity> arg0 = ArgumentCaptor.forClass(HttpSecurity.class);
ArgumentCaptor<HttpSecurity> arg1 = ArgumentCaptor.forClass(HttpSecurity.class);
inOrder.verify(httpSecurityCustomizer0).customize(arg0.capture());
inOrder.verify(httpSecurityCustomizer).customize(arg1.capture());
}
@RestController
static class NameController {
@ -785,6 +866,134 @@ public class HttpSecurityConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@EnableWebMvc
static class AuthorizeRequestsBeanConfiguration {
@Bean
SecurityFilterChain noAuthorizeSecurity(HttpSecurity http) throws Exception {
http.httpBasic(withDefaults());
return http.build();
}
@Bean
static Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeRequests()
throws Exception {
Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authz = mock(
Customizer.class, withSettings().name("authz"));
// prevent validation errors of no authorization rules being defined
willAnswer(((invocation) -> {
AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry requests = invocation
.getArgument(0);
requests.anyRequest().authenticated();
return null;
})).given(authz).customize(any());
return authz;
}
@RestController
static class PublicController {
@GetMapping("/public")
String permitAll() {
return "public";
}
}
}
@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@EnableWebMvc
static class DisableAuthorizeRequestsBeanConfiguration {
@Bean
SecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
http.httpBasic(withDefaults());
// @formatter:off
http.authorizeHttpRequests((requests) -> requests
.anyRequest().permitAll()
);
// @formatter:on
return http.build();
}
@Bean
static Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeRequests()
throws Exception {
// @formatter:off
return (requests) -> requests
.anyRequest().denyAll();
// @formatter:on
}
@RestController
static class PublicController {
@GetMapping("/public")
String permitAll() {
return "public";
}
}
}
@Configuration(proxyBeanMethods = false)
@Import(AuthorizeRequestsBeanConfiguration.class)
static class MultiAuthorizeRequestsBeanConfiguration {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
static Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeRequests0()
throws Exception {
return mock(Customizer.class, withSettings().name("authz0"));
}
}
@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@EnableWebMvc
static class HttpSecurityCustomizerBeanConfiguration {
@Bean
SecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
http.httpBasic(withDefaults());
return http.build();
}
@Bean
static Customizer<HttpSecurity> httpSecurityCustomizer() {
return mock(Customizer.class, withSettings().name("httpSecurityCustomizer"));
}
@RestController
static class PublicController {
@GetMapping("/public")
String permitAll() {
return "public";
}
}
}
@Configuration(proxyBeanMethods = false)
@Import(HttpSecurityCustomizerBeanConfiguration.class)
static class MultiHttpSecurityCustomizerBeanConfiguration {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
static Customizer<HttpSecurity> httpSecurityCustomizer0() throws Exception {
return mock(Customizer.class, withSettings().name("httpSecurityCustomizer0"));
}
}
private static class TestCompromisedPasswordChecker implements CompromisedPasswordChecker {
@Override

View File

@ -24,6 +24,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.config.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@ -34,6 +35,7 @@ import org.springframework.security.core.userdetails.AuthenticationUserDetailsSe
import org.springframework.security.core.userdetails.User;
import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource;
import org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter;
import org.springframework.test.web.servlet.MockMvc;
@ -64,18 +66,16 @@ public class JeeConfigurerTests {
@Test
public void configureWhenRegisteringObjectPostProcessorThenInvokedOnJ2eePreAuthenticatedProcessingFilter() {
ObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);
this.spring.register(ObjectPostProcessorConfig.class).autowire();
verify(ObjectPostProcessorConfig.objectPostProcessor)
.postProcess(any(J2eePreAuthenticatedProcessingFilter.class));
ObjectPostProcessor<Object> objectPostProcessor = this.spring.getContext().getBean(ObjectPostProcessor.class);
verify(objectPostProcessor).postProcess(any(J2eePreAuthenticatedProcessingFilter.class));
}
@Test
public void configureWhenRegisteringObjectPostProcessorThenInvokedOnJ2eeBasedPreAuthenticatedWebAuthenticationDetailsSource() {
ObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);
this.spring.register(ObjectPostProcessorConfig.class).autowire();
verify(ObjectPostProcessorConfig.objectPostProcessor)
.postProcess(any(J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource.class));
ObjectPostProcessor<Object> objectPostProcessor = this.spring.getContext().getBean(ObjectPostProcessor.class);
verify(objectPostProcessor).postProcess(any(J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource.class));
}
@Test
@ -135,12 +135,14 @@ public class JeeConfigurerTests {
public void requestWhenCustomAuthenticatedUserDetailsServiceInLambdaThenCustomAuthenticatedUserDetailsServiceUsed()
throws Exception {
this.spring.register(JeeCustomAuthenticatedUserDetailsServiceConfig.class).autowire();
AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> userDetailsService = this.spring
.getContext()
.getBean(AuthenticationUserDetailsService.class);
Principal user = mock(Principal.class);
User userDetails = new User("user", "N/A", true, true, true, true,
AuthorityUtils.createAuthorityList("ROLE_USER"));
given(user.getName()).willReturn("user");
given(JeeCustomAuthenticatedUserDetailsServiceConfig.authenticationUserDetailsService.loadUserDetails(any()))
.willReturn(userDetails);
given(userDetailsService.loadUserDetails(any())).willReturn(userDetails);
// @formatter:off
MockHttpServletRequestBuilder authRequest = get("/")
.principal(user)
@ -157,7 +159,7 @@ public class JeeConfigurerTests {
@EnableWebSecurity
static class ObjectPostProcessorConfig {
static ObjectPostProcessor<Object> objectPostProcessor;
ObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
@ -169,8 +171,9 @@ public class JeeConfigurerTests {
}
@Bean
static ObjectPostProcessor<Object> objectPostProcessor() {
return objectPostProcessor;
@Primary
ObjectPostProcessor<Object> objectPostProcessor() {
return this.objectPostProcessor;
}
}
@ -245,7 +248,7 @@ public class JeeConfigurerTests {
@EnableWebSecurity
public static class JeeCustomAuthenticatedUserDetailsServiceConfig {
static AuthenticationUserDetailsService authenticationUserDetailsService = mock(
private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService = mock(
AuthenticationUserDetailsService.class);
@Bean
@ -256,12 +259,17 @@ public class JeeConfigurerTests {
.anyRequest().hasRole("USER")
)
.jee((jee) -> jee
.authenticatedUserDetailsService(authenticationUserDetailsService)
.authenticatedUserDetailsService(this.authenticationUserDetailsService)
);
return http.build();
// @formatter:on
}
@Bean
AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService() {
return this.authenticationUserDetailsService;
}
}
}

View File

@ -300,7 +300,15 @@ public class WebAuthnConfigurerTests {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.formLogin(Customizer.withDefaults()).webAuthn(Customizer.withDefaults()).build();
// @formatter:off
http
.formLogin(Customizer.withDefaults())
.webAuthn((authn) -> authn
.rpId("spring.io")
.rpName("spring")
);
// @formatter:on
return http.build();
}
}
@ -316,7 +324,14 @@ public class WebAuthnConfigurerTests {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.webAuthn(Customizer.withDefaults()).build();
// @formatter:off
http
.webAuthn((authn) -> authn
.rpId("spring.io")
.rpName("spring")
);
// @formatter:on
return http.build();
}
}
@ -332,9 +347,16 @@ public class WebAuthnConfigurerTests {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.formLogin(Customizer.withDefaults())
.webAuthn((webauthn) -> webauthn.disableDefaultRegistrationPage(true))
.build();
// @formatter:off
http
.formLogin(Customizer.withDefaults())
.webAuthn((authn) -> authn
.rpId("spring.io")
.rpName("spring")
.disableDefaultRegistrationPage(true)
);
// @formatter:on
return http.build();
}
}
@ -350,9 +372,18 @@ public class WebAuthnConfigurerTests {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.formLogin((login) -> login.loginPage("/custom-login-page"))
.webAuthn((webauthn) -> webauthn.disableDefaultRegistrationPage(true))
.build();
// @formatter:off
http
.formLogin((login) -> login
.loginPage("/custom-login-page")
)
.webAuthn((authn) -> authn
.rpId("spring.io")
.rpName("spring")
.disableDefaultRegistrationPage(true)
);
// @formatter:on
return http.build();
}
}

View File

@ -45,6 +45,7 @@ import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.SecurityAssertions;
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.ObjectPostProcessor;
@ -217,10 +218,9 @@ public class OAuth2LoginConfigurerTests {
Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(1);
assertThat(authentication.getAuthorities()).first()
.isInstanceOf(OAuth2UserAuthority.class)
.hasToString("OAUTH2_USER");
SecurityAssertions.assertThat(authentication)
.hasAuthority("OAUTH2_USER")
.isInstanceOf(OAuth2UserAuthority.class);
}
@Test
@ -234,10 +234,9 @@ public class OAuth2LoginConfigurerTests {
Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(1);
assertThat(authentication.getAuthorities()).first()
.isInstanceOf(OAuth2UserAuthority.class)
.hasToString("OAUTH2_USER");
SecurityAssertions.assertThat(authentication)
.hasAuthority("OAUTH2_USER")
.isInstanceOf(OAuth2UserAuthority.class);
SecurityContextHolderStrategy strategy = this.context.getBean(SecurityContextHolderStrategy.class);
verify(strategy, atLeastOnce()).getDeferredContext();
SecurityContextChangedListener listener = this.context.getBean(SecurityContextChangedListener.class);
@ -255,10 +254,9 @@ public class OAuth2LoginConfigurerTests {
Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(1);
assertThat(authentication.getAuthorities()).first()
.isInstanceOf(OAuth2UserAuthority.class)
.hasToString("OAUTH2_USER");
SecurityAssertions.assertThat(authentication)
.hasAuthority("OAUTH2_USER")
.isInstanceOf(OAuth2UserAuthority.class);
}
// gh-6009
@ -296,9 +294,7 @@ public class OAuth2LoginConfigurerTests {
Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(2);
assertThat(authentication.getAuthorities()).first().hasToString("OAUTH2_USER");
assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OAUTH2_USER");
SecurityAssertions.assertThat(authentication).hasAuthorities("OAUTH2_USER", "ROLE_OAUTH2_USER");
}
@Test
@ -317,9 +313,7 @@ public class OAuth2LoginConfigurerTests {
Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(2);
assertThat(authentication.getAuthorities()).first().hasToString("OAUTH2_USER");
assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OAUTH2_USER");
SecurityAssertions.assertThat(authentication).hasAuthorities("OAUTH2_USER", "ROLE_OAUTH2_USER");
}
@Test
@ -338,9 +332,7 @@ public class OAuth2LoginConfigurerTests {
Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(2);
assertThat(authentication.getAuthorities()).first().hasToString("OAUTH2_USER");
assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OAUTH2_USER");
SecurityAssertions.assertThat(authentication).hasAuthorities("OAUTH2_USER", "ROLE_OAUTH2_USER");
}
// gh-5488
@ -361,10 +353,9 @@ public class OAuth2LoginConfigurerTests {
Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(1);
assertThat(authentication.getAuthorities()).first()
.isInstanceOf(OAuth2UserAuthority.class)
.hasToString("OAUTH2_USER");
SecurityAssertions.assertThat(authentication)
.hasAuthority("OAUTH2_USER")
.isInstanceOf(OAuth2UserAuthority.class);
}
// gh-5521
@ -570,10 +561,7 @@ public class OAuth2LoginConfigurerTests {
Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(1);
assertThat(authentication.getAuthorities()).first()
.isInstanceOf(OidcUserAuthority.class)
.hasToString("OIDC_USER");
SecurityAssertions.assertThat(authentication).hasAuthority("OIDC_USER").isInstanceOf(OidcUserAuthority.class);
}
@Test
@ -593,9 +581,7 @@ public class OAuth2LoginConfigurerTests {
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(1);
assertThat(authentication.getAuthorities()).first()
.isInstanceOf(OidcUserAuthority.class)
.hasToString("OIDC_USER");
SecurityAssertions.assertThat(authentication).hasAuthority("OIDC_USER").isInstanceOf(OidcUserAuthority.class);
}
@Test
@ -614,9 +600,7 @@ public class OAuth2LoginConfigurerTests {
Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(2);
assertThat(authentication.getAuthorities()).first().hasToString("OIDC_USER");
assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OIDC_USER");
SecurityAssertions.assertThat(authentication).hasAuthorities("OIDC_USER", "ROLE_OIDC_USER");
}
@Test
@ -635,9 +619,7 @@ public class OAuth2LoginConfigurerTests {
Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(2);
assertThat(authentication.getAuthorities()).first().hasToString("OIDC_USER");
assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OIDC_USER");
SecurityAssertions.assertThat(authentication).hasAuthorities("OIDC_USER", "ROLE_OIDC_USER");
}
@Test
@ -690,11 +672,7 @@ public class OAuth2LoginConfigurerTests {
Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(1);
assertThat(authentication.getAuthorities()).first()
.isInstanceOf(OidcUserAuthority.class)
.hasToString("OIDC_USER");
SecurityAssertions.assertThat(authentication).hasAuthority("OIDC_USER").isInstanceOf(OidcUserAuthority.class);
// Ensure shared objects set for OAuth2 Client are not used
ClientRegistrationRepository clientRegistrationRepository = this.spring.getContext()
.getBean(ClientRegistrationRepository.class);

View File

@ -2674,6 +2674,7 @@ public class OAuth2ResourceServerConfigurerTests {
String requiresReadScope(JwtAuthenticationToken token) {
return token.getAuthorities()
.stream()
.filter((ga) -> ga.getAuthority().startsWith("SCOPE_"))
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList())
.toString();

View File

@ -29,12 +29,17 @@ import io.micrometer.observation.ObservationRegistry;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mockito;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authentication.password.CompromisedPasswordDecision;
@ -46,6 +51,7 @@ import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.config.users.ReactiveAuthenticationTestConfiguration;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity.AuthorizeExchangeSpec;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
@ -267,6 +273,47 @@ public class ServerHttpSecurityConfigurationTests {
assertThat(contexts.next().getContextualName()).isEqualTo("security filterchain after");
}
@Test
void authorizeExchangeCustomizerBean() {
this.spring.register(AuthorizeExchangeCustomizerBeanConfig.class).autowire();
Customizer<AuthorizeExchangeSpec> authzCustomizer = this.spring.getContext().getBean("authz", Customizer.class);
ArgumentCaptor<AuthorizeExchangeSpec> arg0 = ArgumentCaptor.forClass(AuthorizeExchangeSpec.class);
verify(authzCustomizer).customize(arg0.capture());
}
@Test
void multiAuthorizeExchangeCustomizerBean() {
this.spring.register(MultiAuthorizeExchangeCustomizerBeanConfig.class).autowire();
Customizer<AuthorizeExchangeSpec> authzCustomizer = this.spring.getContext().getBean("authz", Customizer.class);
ArgumentCaptor<AuthorizeExchangeSpec> arg0 = ArgumentCaptor.forClass(AuthorizeExchangeSpec.class);
verify(authzCustomizer).customize(arg0.capture());
}
@Test
void serverHttpSecurityCustomizerBean() {
this.spring.register(ServerHttpSecurityCustomizerConfig.class).autowire();
Customizer<ServerHttpSecurity> httpSecurityCustomizer = this.spring.getContext()
.getBean("httpSecurityCustomizer", Customizer.class);
ArgumentCaptor<ServerHttpSecurity> arg0 = ArgumentCaptor.forClass(ServerHttpSecurity.class);
verify(httpSecurityCustomizer).customize(arg0.capture());
}
@Test
void multiServerHttpSecurityCustomizerBean() {
this.spring.register(MultiServerHttpSecurityCustomizerConfig.class).autowire();
Customizer<ServerHttpSecurity> httpSecurityCustomizer = this.spring.getContext()
.getBean("httpSecurityCustomizer", Customizer.class);
Customizer<ServerHttpSecurity> httpSecurityCustomizer0 = this.spring.getContext()
.getBean("httpSecurityCustomizer0", Customizer.class);
InOrder inOrder = Mockito.inOrder(httpSecurityCustomizer0, httpSecurityCustomizer);
ArgumentCaptor<ServerHttpSecurity> arg0 = ArgumentCaptor.forClass(ServerHttpSecurity.class);
inOrder.verify(httpSecurityCustomizer0).customize(arg0.capture());
inOrder.verify(httpSecurityCustomizer).customize(arg0.capture());
}
@Configuration
static class SubclassConfig extends ServerHttpSecurityConfiguration {
@ -474,4 +521,64 @@ public class ServerHttpSecurityConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableWebFlux
@EnableWebFluxSecurity
@Import(UserDetailsConfig.class)
static class AuthorizeExchangeCustomizerBeanConfig {
@Bean
SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
return http.build();
}
@Bean
static Customizer<AuthorizeExchangeSpec> authz() {
return mock(Customizer.class);
}
}
@Configuration(proxyBeanMethods = false)
@Import(AuthorizeExchangeCustomizerBeanConfig.class)
static class MultiAuthorizeExchangeCustomizerBeanConfig {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
Customizer<AuthorizeExchangeSpec> authz0() {
return mock(Customizer.class);
}
}
@Configuration(proxyBeanMethods = false)
@EnableWebFlux
@EnableWebFluxSecurity
@Import(UserDetailsConfig.class)
static class ServerHttpSecurityCustomizerConfig {
@Bean
SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
return http.build();
}
@Bean
static Customizer<ServerHttpSecurity> httpSecurityCustomizer() {
return mock(Customizer.class);
}
}
@Configuration(proxyBeanMethods = false)
@Import(ServerHttpSecurityCustomizerConfig.class)
static class MultiServerHttpSecurityCustomizerConfig {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
static Customizer<ServerHttpSecurity> httpSecurityCustomizer0() {
return mock(Customizer.class);
}
}
}

View File

@ -68,7 +68,7 @@ public class UserServiceBeanDefinitionParserTests {
System.setProperty("principal.pass", "joespassword");
System.setProperty("principal.authorities", "ROLE_A,ROLE_B");
// @formatter:off
setContext("<b:bean class='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer'/>"
setContext("<b:bean class='org.springframework.context.support.PropertySourcesPlaceholderConfigurer'/>"
+ "<user-service id='service'>"
+ " <user name='${principal.name}' password='${principal.pass}' authorities='${principal.authorities}'/>"
+ "</user-service>");

View File

@ -92,7 +92,7 @@ public class FilterSecurityMetadataSourceBeanDefinitionParserTests {
public void interceptUrlsSupportPropertyPlaceholders() {
System.setProperty("secure.url", "/secure");
System.setProperty("secure.role", "ROLE_A");
setContext("<b:bean class='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer'/>"
setContext("<b:bean class='org.springframework.context.support.PropertySourcesPlaceholderConfigurer'/>"
+ "<filter-security-metadata-source id='fids' use-expressions='false'>"
+ " <intercept-url pattern='${secure.url}' access='${secure.role}'/>"
+ "</filter-security-metadata-source>");

View File

@ -24,11 +24,11 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
@ -125,11 +125,11 @@ public class CustomHttpSecurityConfigurerTests {
}
@Bean
static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
// Typically externalize this as a properties file
Properties properties = new Properties();
properties.setProperty("permitAllPattern", "/public/**");
PropertyPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertyPlaceholderConfigurer();
PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertyPlaceholderConfigurer.setProperties(properties);
return propertyPlaceholderConfigurer;
}
@ -153,11 +153,11 @@ public class CustomHttpSecurityConfigurerTests {
}
@Bean
static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
// Typically externalize this as a properties file
Properties properties = new Properties();
properties.setProperty("permitAllPattern", "/public/**");
PropertyPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertyPlaceholderConfigurer();
PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertyPlaceholderConfigurer.setProperties(properties);
return propertyPlaceholderConfigurer;
}

View File

@ -464,7 +464,9 @@ public class MethodSecurityBeanDefinitionParserTests {
static class MyAuthorizationManager implements AuthorizationManager<MethodInvocation> {
@Override
public AuthorizationResult authorize(Supplier<Authentication> authentication, MethodInvocation object) {
public AuthorizationResult authorize(
Supplier<? extends @org.jspecify.annotations.Nullable Authentication> authentication,
MethodInvocation object) {
return new AuthorizationDecision("bob".equals(authentication.get().getName()));
}

View File

@ -30,8 +30,11 @@ import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostP
import org.springframework.mock.web.MockServletConfig;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.util.InMemoryXmlWebApplicationContext;
import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers;
import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
import org.springframework.security.web.servlet.MockServletContext;
import org.springframework.test.context.web.GenericXmlWebContextLoader;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;
@ -42,6 +45,7 @@ import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.context.support.XmlWebApplicationContext;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.server.WebFilter;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
@ -156,6 +160,18 @@ public class SpringTestContext implements Closeable {
// @formatter:on
this.context.getBeanFactory().registerResolvableDependency(MockMvc.class, mockMvc);
}
String webFluxSecurityBean = "org.springframework.security.config.annotation.web.reactive.WebFluxSecurityConfiguration.WebFilterChainFilter";
if (this.context.containsBean(webFluxSecurityBean)) {
WebFilter springSecurityFilter = this.context.getBean(webFluxSecurityBean, WebFilter.class);
// @formatter:off
WebTestClient webTest = WebTestClient
.bindToController(new WebTestClientBuilder.Http200RestController())
.webFilter(springSecurityFilter)
.apply(SecurityMockServerConfigurers.springSecurity())
.build();
// @formatter:on
this.context.getBeanFactory().registerResolvableDependency(WebTestClient.class, webTest);
}
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(this.context.getBeanFactory());
bpp.processInjection(this.test);

View File

@ -16,18 +16,27 @@
package org.springframework.security.config.web.server;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.WebDriver;
import reactor.core.publisher.Mono;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.web.reactive.ServerHttpSecurityConfigurationBuilder;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.htmlunit.server.WebTestClientHtmlUnitDriverBuilder;
import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.logout.ServerLogoutHandler;
import org.springframework.security.web.server.context.ServerSecurityContextRepository;
import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ServerWebExchange;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.security.config.Customizer.withDefaults;
@ -210,6 +219,84 @@ public class LogoutSpecTests {
FormLoginTests.HomePage.to(driver, FormLoginTests.DefaultLoginPage.class).assertAt();
}
@Test
public void multipleLogoutHandlers() {
InMemorySecurityContextRepository repository = new InMemorySecurityContextRepository();
MultiValueMap<String, String> logoutData = new LinkedMultiValueMap<>();
ServerLogoutHandler handler1 = (exchange, authentication) -> {
logoutData.add("handler-header", "value1");
return Mono.empty();
};
ServerLogoutHandler handler2 = (exchange, authentication) -> {
logoutData.add("handler-header", "value2");
return Mono.empty();
};
// @formatter:off
SecurityWebFilterChain securityWebFilter = this.http
.securityContextRepository(repository)
.authorizeExchange((authorize) -> authorize
.anyExchange().authenticated())
.formLogin(withDefaults())
.logout((logoutSpec) -> logoutSpec.logoutHandler((handlers) -> {
handlers.add(handler1);
handlers.add(0, handler2);
}))
.build();
WebTestClient webTestClient = WebTestClientBuilder
.bindToWebFilters(securityWebFilter)
.build();
WebDriver driver = WebTestClientHtmlUnitDriverBuilder
.webTestClientSetup(webTestClient)
.build();
// @formatter:on
FormLoginTests.DefaultLoginPage loginPage = FormLoginTests.HomePage
.to(driver, FormLoginTests.DefaultLoginPage.class)
.assertAt();
// @formatter:off
loginPage = loginPage.loginForm()
.username("user")
.password("invalid")
.submit(FormLoginTests.DefaultLoginPage.class)
.assertError();
FormLoginTests.HomePage homePage = loginPage.loginForm()
.username("user")
.password("password")
.submit(FormLoginTests.HomePage.class);
// @formatter:on
homePage.assertAt();
SecurityContext savedContext = repository.getSavedContext();
assertThat(savedContext).isNotNull();
assertThat(savedContext.getAuthentication()).isInstanceOf(UsernamePasswordAuthenticationToken.class);
loginPage = FormLoginTests.DefaultLogoutPage.to(driver).assertAt().logout();
loginPage.assertAt().assertLogout();
assertThat(logoutData).hasSize(1);
assertThat(logoutData.get("handler-header")).containsExactly("value2", "value1");
savedContext = repository.getSavedContext();
assertThat(savedContext).isNull();
}
private static class InMemorySecurityContextRepository implements ServerSecurityContextRepository {
@Nullable private SecurityContext savedContext;
@Override
public Mono<Void> save(ServerWebExchange exchange, SecurityContext context) {
this.savedContext = context;
return Mono.empty();
}
@Override
public Mono<SecurityContext> load(ServerWebExchange exchange) {
return Mono.justOrEmpty(this.savedContext);
}
@Nullable private SecurityContext getSavedContext() {
return this.savedContext;
}
}
@RestController
public static class HomeController {

View File

@ -26,6 +26,7 @@ import java.util.Map;
import java.util.function.Supplier;
import org.assertj.core.api.ThrowableAssert;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ -735,7 +736,7 @@ public class WebSocketMessageBrokerConfigTests {
}
@Override
public EvaluationContext createEvaluationContext(Supplier<Authentication> authentication,
public EvaluationContext createEvaluationContext(Supplier<? extends @Nullable Authentication> authentication,
Message<Object> message) {
return new StandardEvaluationContext(new MessageSecurityExpressionRoot(authentication, message) {
public boolean denyNile() {

View File

@ -193,7 +193,7 @@ class AuthorizeHttpRequestsDslTests {
open class MvcMatcherPathVariablesConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
val access = AuthorizationManager { _: Supplier<Authentication>, context: RequestAuthorizationContext ->
val access = AuthorizationManager { _: Supplier<out Authentication>, context: RequestAuthorizationContext ->
AuthorizationDecision(context.variables["userName"] == "user")
}
http {

View File

@ -367,7 +367,7 @@ class HttpSecurityDslTests {
this.spring.register(CustomFilterConfig::class.java).autowire()
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
val filters: List<Filter> = filterChain.getFilters("/")
val filters: List<Filter>? = filterChain.getFilters("/")
assertThat(filters).anyMatch { it is CustomFilter }
}
@ -390,7 +390,7 @@ class HttpSecurityDslTests {
this.spring.register(CustomFilterConfigReified::class.java).autowire()
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
val filters: List<Filter> = filterChain.getFilters("/")
val filters: List<Filter>? = filterChain.getFilters("/")
assertThat(filters).anyMatch { it is CustomFilter }
}
@ -413,7 +413,7 @@ class HttpSecurityDslTests {
this.spring.register(CustomFilterAfterConfig::class.java).autowire()
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
val filters: List<Class<out Filter>> = filterChain.getFilters("/").map { it.javaClass }
val filters: List<Class<out Filter>> = filterChain.getFilters("/")!!.map { it.javaClass }
assertThat(filters).containsSubsequence(
UsernamePasswordAuthenticationFilter::class.java,
@ -440,7 +440,7 @@ class HttpSecurityDslTests {
this.spring.register(CustomFilterAfterConfigReified::class.java).autowire()
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/").map { it.javaClass }
val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/")!!.map { it.javaClass }
assertThat(filterClasses).containsSubsequence(
UsernamePasswordAuthenticationFilter::class.java,
@ -467,7 +467,7 @@ class HttpSecurityDslTests {
this.spring.register(CustomFilterBeforeConfig::class.java).autowire()
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
val filters: List<Class<out Filter>> = filterChain.getFilters("/").map { it.javaClass }
val filters: List<Class<out Filter>> = filterChain.getFilters("/")!!.map { it.javaClass }
assertThat(filters).containsSubsequence(
CustomFilter::class.java,
@ -494,7 +494,7 @@ class HttpSecurityDslTests {
this.spring.register(CustomFilterBeforeConfigReified::class.java).autowire()
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/").map { it.javaClass }
val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/")!!.map { it.javaClass }
assertThat(filterClasses).containsSubsequence(
CustomFilter::class.java,
@ -523,7 +523,7 @@ class HttpSecurityDslTests {
this.spring.register(CustomSecurityConfigurerConfig::class.java).autowire()
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/").map { it.javaClass }
val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/")!!.map { it.javaClass }
assertThat(filterClasses).contains(
CustomFilter::class.java
@ -535,7 +535,7 @@ class HttpSecurityDslTests {
this.spring.register(CustomSecurityConfigurerConfig::class.java).autowire()
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/").map { it.javaClass }
val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/")!!.map { it.javaClass }
assertThat(filterClasses).contains(
CustomFilter::class.java
@ -588,7 +588,7 @@ class HttpSecurityDslTests {
this.spring.register(CustomDslUsingWithConfig::class.java).autowire()
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/").map { it.javaClass }
val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/")!!.map { it.javaClass }
assertThat(filterClasses).contains(
UsernamePasswordAuthenticationFilter::class.java
@ -623,5 +623,38 @@ class HttpSecurityDslTests {
}
@Test
fun `HTTP security when Dsl Bean`() {
this.spring.register(DslBeanConfig::class.java).autowire()
this.mockMvc.get("/")
.andExpect {
header {
string("Content-Security-Policy", "object-src 'none'")
}
}
}
@Configuration
@EnableWebSecurity
@EnableWebMvc
open class DslBeanConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
httpBasic { }
}
return http.build()
}
@Bean
open fun headersDsl(): HeadersDsl.() -> Unit {
return {
contentSecurityPolicy {
policyDirectives = "object-src 'none'"
}
}
}
}
}

View File

@ -350,8 +350,8 @@ class LogoutDslTests {
class NoopLogoutHandler: LogoutHandler {
override fun logout(
request: HttpServletRequest?,
response: HttpServletResponse?,
request: HttpServletRequest,
response: HttpServletResponse,
authentication: Authentication?
) { }

View File

@ -22,7 +22,6 @@ import io.mockk.mockk
import io.mockk.verify
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
@ -44,7 +43,6 @@ import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequ
import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers
import org.springframework.security.web.SecurityFilterChain
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler
import org.springframework.security.web.authentication.ott.DefaultGenerateOneTimeTokenRequestResolver
import org.springframework.security.web.authentication.ott.GenerateOneTimeTokenRequestResolver
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler
import org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler
@ -53,7 +51,6 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
import java.time.Duration
import java.time.Instant
import java.time.ZoneOffset
/**
* Tests for [OneTimeTokenLoginDsl]
@ -267,7 +264,7 @@ class OneTimeTokenLoginDslTests {
)
}
constructor(redirectUrl: String?) {
constructor(redirectUrl: String) {
this.delegate =
RedirectOneTimeTokenGenerationSuccessHandler(
redirectUrl

View File

@ -132,8 +132,8 @@ class RequiresChannelDslTests {
companion object {
val CHANNEL_PROCESSOR: ChannelProcessor = object : ChannelProcessor {
override fun decide(invocation: FilterInvocation?, config: MutableCollection<ConfigAttribute>?) {}
override fun supports(attribute: ConfigAttribute?): Boolean = true
override fun decide(invocation: FilterInvocation, config: MutableCollection<ConfigAttribute>) {}
override fun supports(attribute: ConfigAttribute): Boolean = true
}
}

View File

@ -93,7 +93,7 @@ class SecurityContextDslTests {
testContext.autowire()
val filterChainProxy = testContext.context.getBean(FilterChainProxy::class.java)
// @formatter:off
val filterTypes = filterChainProxy.getFilters("/").toList()
val filterTypes = filterChainProxy.getFilters("/")!!.toList()
assertThat(filterTypes)
.anyMatch { it is SecurityContextHolderFilter }

View File

@ -1,4 +1,5 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -125,6 +126,8 @@ class WebAuthnDslTests {
http{
formLogin { }
webAuthn {
rpId = "spring.io"
rpName = "spring"
disableDefaultRegistrationPage = true
}
}
@ -144,7 +147,10 @@ class WebAuthnDslTests {
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http{
formLogin { }
webAuthn { }
webAuthn {
rpId = "spring.io"
rpName = "spring"
}
}
return http.build()
}

View File

@ -270,8 +270,8 @@ class ServerHttpBasicDslTests {
open class MockServerAuthenticationFailureHandler: ServerAuthenticationFailureHandler {
override fun onAuthenticationFailure(
webFilterExchange: WebFilterExchange?,
exception: AuthenticationException?
webFilterExchange: WebFilterExchange,
exception: AuthenticationException
): Mono<Void> {
return Mono.empty()
}

View File

@ -175,8 +175,8 @@ class ServerOAuth2ResourceServerDslTests {
open class MockServerAuthenticationFailureHandler: ServerAuthenticationFailureHandler {
override fun onAuthenticationFailure(
webFilterExchange: WebFilterExchange?,
exception: AuthenticationException?
webFilterExchange: WebFilterExchange,
exception: AuthenticationException
): Mono<Void> {
return Mono.empty()
}

View File

@ -280,11 +280,11 @@ class ServerOneTimeTokenLoginDslTests {
this.delegate = ServerRedirectOneTimeTokenGenerationSuccessHandler("/login/ott")
}
constructor(redirectUrl: String?) {
constructor(redirectUrl: String) {
this.delegate = ServerRedirectOneTimeTokenGenerationSuccessHandler(redirectUrl)
}
override fun handle(exchange: ServerWebExchange?, oneTimeToken: OneTimeToken?): Mono<Void> {
override fun handle(exchange: ServerWebExchange, oneTimeToken: OneTimeToken): Mono<Void> {
lastToken = oneTimeToken
return delegate!!.handle(exchange, oneTimeToken)
}

View File

@ -29,7 +29,7 @@
<intercept-url pattern="/**" access="permitAll"/>
</http>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>

View File

@ -29,7 +29,7 @@
<intercept-url pattern="/**" access="permitAll"/>
</http>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>

View File

@ -31,7 +31,7 @@
<custom-filter ref="userFilter" after="LOGOUT_FILTER"/>
</http>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
<b:bean name="userFilter" class="org.mockito.Mockito" factory-method="mock">
<b:constructor-arg value="jakarta.servlet.Filter" type="java.lang.Class"/>

View File

@ -29,7 +29,7 @@
<intercept-url pattern="/**" access="authenticated"/>
</http>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
<b:import resource="MiscHttpConfigTests-controllers.xml"/>
<b:import resource="userservice.xml"/>

View File

@ -35,7 +35,7 @@
</b:constructor-arg>
</b:bean>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
<b:import resource="MiscHttpConfigTests-controllers.xml"/>
<b:import resource="userservice.xml"/>

View File

@ -29,7 +29,7 @@
<access-denied-handler error-page="${accessDenied}"/>
</http>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
<b:bean name="sc" class="org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController"/>

View File

@ -29,7 +29,7 @@
<access-denied-handler error-page="#{'/go' + '-away'}"/>
</http>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
<b:bean name="sc" class="org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController"/>

View File

@ -34,7 +34,7 @@
<csrf disabled="true"/>
</http>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
<b:bean name="unsecured" class="org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController"/>

View File

@ -37,7 +37,7 @@
<csrf disabled="true"/>
</http>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
<b:bean name="sc" class="org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController"/>

View File

@ -33,7 +33,7 @@
</port-mappings>
</http>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
<b:bean name="sc" class="org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController"/>

View File

@ -28,7 +28,7 @@
<intercept-url pattern="${secure.url}" access="ROLE_USER" requires-channel="${required.channel}"/>
</http>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
<b:bean name="sc" class="org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController"/>

View File

@ -30,7 +30,7 @@
<intercept-url pattern="/**" access="ROLE_NUNYA"/>
</http>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
<b:bean name="unsecured" class="org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController"/>

View File

@ -31,7 +31,7 @@
token-validity-seconds="${security.rememberme.ttl}"/>
</http>
<b:bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<b:bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<b:property name="properties" value="security.rememberme.ttl=30"/>
</b:bean>

View File

@ -28,7 +28,7 @@
<b:bean name="transactionManager" class="org.springframework.security.config.MockTransactionManager" />
<b:bean class='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer'/>
<b:bean class='org.springframework.context.support.PropertySourcesPlaceholderConfigurer'/>
<b:bean id="transactionalTarget" class="org.springframework.security.config.TransactionalTestBusinessBean">
<intercept-methods use-authorization-manager="false">

View File

@ -110,7 +110,7 @@
</sec:filter-chain-map>
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer" />
<bean id="sec1235FilterChainProxy" class="org.springframework.security.web.FilterChainProxy">
<constructor-arg>

View File

@ -18,6 +18,8 @@ package org.springframework.security.access;
import java.io.Serializable;
import org.jspecify.annotations.NullUnmarked;
import org.springframework.security.access.intercept.RunAsManager;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.annotation.SecurityAnnotationScanner;
@ -45,6 +47,7 @@ import org.springframework.security.core.annotation.SecurityAnnotationScanner;
* {@link AuthorizationManager}.
*/
@Deprecated
@NullUnmarked
public interface ConfigAttribute extends Serializable {
/**

View File

@ -70,7 +70,7 @@ public abstract class AbstractSecurityExpressionHandler<T>
* suitable root object.
*/
@Override
public final EvaluationContext createEvaluationContext(Authentication authentication, T invocation) {
public final EvaluationContext createEvaluationContext(@Nullable Authentication authentication, T invocation) {
SecurityExpressionOperations root = createSecurityExpressionRoot(authentication, invocation);
StandardEvaluationContext ctx = createEvaluationContextInternal(authentication, invocation);
if (this.beanResolver != null) {
@ -91,7 +91,8 @@ public abstract class AbstractSecurityExpressionHandler<T>
* @return A {@code StandardEvaluationContext} or potentially a custom subclass if
* overridden.
*/
protected StandardEvaluationContext createEvaluationContextInternal(Authentication authentication, T invocation) {
protected StandardEvaluationContext createEvaluationContextInternal(@Nullable Authentication authentication,
T invocation) {
return new StandardEvaluationContext();
}
@ -102,8 +103,8 @@ public abstract class AbstractSecurityExpressionHandler<T>
* @param invocation the invocation (filter, method, channel)
* @return the object
*/
protected abstract SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
T invocation);
protected abstract SecurityExpressionOperations createSecurityExpressionRoot(
@Nullable Authentication authentication, T invocation);
protected @Nullable RoleHierarchy getRoleHierarchy() {
return this.roleHierarchy;

View File

@ -18,6 +18,8 @@ package org.springframework.security.access.expression;
import java.util.function.Supplier;
import org.jspecify.annotations.Nullable;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
@ -42,7 +44,7 @@ public interface SecurityExpressionHandler<T> extends AopInfrastructureBean {
* Provides an evaluation context in which to evaluate security expressions for the
* invocation type.
*/
EvaluationContext createEvaluationContext(Authentication authentication, T invocation);
EvaluationContext createEvaluationContext(@Nullable Authentication authentication, T invocation);
/**
* Provides an evaluation context in which to evaluate security expressions for the
@ -55,7 +57,8 @@ public interface SecurityExpressionHandler<T> extends AopInfrastructureBean {
* @return the {@link EvaluationContext} to use
* @since 5.8
*/
default EvaluationContext createEvaluationContext(Supplier<Authentication> authentication, T invocation) {
default EvaluationContext createEvaluationContext(Supplier<? extends @Nullable Authentication> authentication,
T invocation) {
return createEvaluationContext(authentication.get(), invocation);
}

View File

@ -78,7 +78,7 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
* Creates a new instance
* @param authentication the {@link Authentication} to use. Cannot be null.
*/
public SecurityExpressionRoot(Authentication authentication) {
public SecurityExpressionRoot(@Nullable Authentication authentication) {
this(() -> authentication);
}
@ -89,7 +89,7 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
* Cannot be null.
* @since 5.8
*/
public SecurityExpressionRoot(Supplier<Authentication> authentication) {
public SecurityExpressionRoot(Supplier<? extends @Nullable Authentication> authentication) {
this.authentication = SingletonSupplier.of(() -> {
Authentication value = authentication.get();
Assert.notNull(value, "Authentication object cannot be null");
@ -177,7 +177,7 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
this.trustResolver = trustResolver;
}
public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
public void setRoleHierarchy(@Nullable RoleHierarchy roleHierarchy) {
this.roleHierarchy = roleHierarchy;
}

View File

@ -79,12 +79,15 @@ public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpr
* implementation.
*/
@Override
public StandardEvaluationContext createEvaluationContextInternal(Authentication auth, MethodInvocation mi) {
public StandardEvaluationContext createEvaluationContextInternal(@Nullable Authentication auth,
MethodInvocation mi) {
return new MethodSecurityEvaluationContext(auth, mi, getParameterNameDiscoverer());
}
@Override
public EvaluationContext createEvaluationContext(Supplier<Authentication> authentication, MethodInvocation mi) {
@SuppressWarnings("NullAway") // FIXME: Dataflow analysis limitation
public EvaluationContext createEvaluationContext(Supplier<? extends @Nullable Authentication> authentication,
MethodInvocation mi) {
MethodSecurityExpressionOperations root = createSecurityExpressionRoot(authentication, mi);
MethodSecurityEvaluationContext ctx = new MethodSecurityEvaluationContext(root, mi,
getParameterNameDiscoverer());
@ -96,13 +99,13 @@ public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpr
* Creates the root object for expression evaluation.
*/
@Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(@Nullable Authentication authentication,
MethodInvocation invocation) {
return createSecurityExpressionRoot(() -> authentication, invocation);
}
private MethodSecurityExpressionOperations createSecurityExpressionRoot(Supplier<Authentication> authentication,
MethodInvocation invocation) {
private MethodSecurityExpressionOperations createSecurityExpressionRoot(
Supplier<? extends @Nullable Authentication> authentication, MethodInvocation invocation) {
MethodSecurityExpressionRoot root = new MethodSecurityExpressionRoot(authentication);
root.setThis(invocation.getThis());
root.setPermissionEvaluator(getPermissionEvaluator());

View File

@ -38,11 +38,11 @@ class MethodSecurityExpressionRoot extends SecurityExpressionRoot implements Met
private @Nullable Object target;
MethodSecurityExpressionRoot(Authentication a) {
MethodSecurityExpressionRoot(@Nullable Authentication a) {
super(a);
}
MethodSecurityExpressionRoot(Supplier<Authentication> authentication) {
MethodSecurityExpressionRoot(Supplier<? extends @Nullable Authentication> authentication) {
super(authentication);
}

View File

@ -18,6 +18,8 @@ package org.springframework.security.access.vote;
import java.util.Collection;
import org.jspecify.annotations.NullUnmarked;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.Authentication;
@ -53,6 +55,7 @@ import org.springframework.security.core.GrantedAuthority;
* instead
*/
@Deprecated
@NullUnmarked
public class RoleVoter implements AccessDecisionVoter<Object> {
private String rolePrefix = "ROLE_";

View File

@ -16,6 +16,9 @@
package org.springframework.security.authentication;
import org.jspecify.annotations.Nullable;
import org.springframework.lang.Contract;
import org.springframework.security.core.Authentication;
/**
@ -37,7 +40,7 @@ public interface AuthenticationTrustResolver {
* @return <code>true</code> the passed authentication token represented an anonymous
* principal, <code>false</code> otherwise
*/
boolean isAnonymous(Authentication authentication);
boolean isAnonymous(@Nullable Authentication authentication);
/**
* Indicates whether the passed <code>Authentication</code> token represents user that
@ -51,7 +54,7 @@ public interface AuthenticationTrustResolver {
* @return <code>true</code> the passed authentication token represented a principal
* authenticated using a remember-me token, <code>false</code> otherwise
*/
boolean isRememberMe(Authentication authentication);
boolean isRememberMe(@Nullable Authentication authentication);
/**
* Indicates whether the passed <code>Authentication</code> token represents a fully
@ -66,7 +69,7 @@ public interface AuthenticationTrustResolver {
* {@link #isRememberMe(Authentication)}, <code>false</code> otherwise
* @since 6.1
*/
default boolean isFullyAuthenticated(Authentication authentication) {
default boolean isFullyAuthenticated(@Nullable Authentication authentication) {
return isAuthenticated(authentication) && !isRememberMe(authentication);
}
@ -78,7 +81,8 @@ public interface AuthenticationTrustResolver {
* {@link Authentication#isAuthenticated()} is true.
* @since 6.1.7
*/
default boolean isAuthenticated(Authentication authentication) {
@Contract("null -> false")
default boolean isAuthenticated(@Nullable Authentication authentication) {
return authentication != null && authentication.isAuthenticated() && !isAnonymous(authentication);
}

View File

@ -16,6 +16,8 @@
package org.springframework.security.authentication;
import org.jspecify.annotations.Nullable;
import org.springframework.security.core.Authentication;
/**
@ -44,7 +46,7 @@ public class AuthenticationTrustResolverImpl implements AuthenticationTrustResol
}
@Override
public boolean isAnonymous(Authentication authentication) {
public boolean isAnonymous(@Nullable Authentication authentication) {
if ((this.anonymousClass == null) || (authentication == null)) {
return false;
}
@ -52,7 +54,7 @@ public class AuthenticationTrustResolverImpl implements AuthenticationTrustResol
}
@Override
public boolean isRememberMe(Authentication authentication) {
public boolean isRememberMe(@Nullable Authentication authentication) {
if ((this.rememberMeClass == null) || (authentication == null)) {
return false;
}

View File

@ -18,6 +18,8 @@ package org.springframework.security.authentication;
import java.io.Serial;
import org.jspecify.annotations.Nullable;
import org.springframework.security.core.AuthenticationException;
/**
@ -35,7 +37,7 @@ public class BadCredentialsException extends AuthenticationException {
* Constructs a <code>BadCredentialsException</code> with the specified message.
* @param msg the detail message
*/
public BadCredentialsException(String msg) {
public BadCredentialsException(@Nullable String msg) {
super(msg);
}
@ -45,7 +47,7 @@ public class BadCredentialsException extends AuthenticationException {
* @param msg the detail message
* @param cause root cause
*/
public BadCredentialsException(String msg, Throwable cause) {
public BadCredentialsException(@Nullable String msg, Throwable cause) {
super(msg, cause);
}

View File

@ -39,7 +39,7 @@ public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationT
private static final long serialVersionUID = 620L;
private final Object principal;
private final @Nullable Object principal;
private @Nullable Object credentials;
@ -49,7 +49,7 @@ public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationT
* will return <code>false</code>.
*
*/
public UsernamePasswordAuthenticationToken(Object principal, @Nullable Object credentials) {
public UsernamePasswordAuthenticationToken(@Nullable Object principal, @Nullable Object credentials) {
super(null);
this.principal = principal;
this.credentials = credentials;
@ -82,7 +82,8 @@ public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationT
*
* @since 5.7
*/
public static UsernamePasswordAuthenticationToken unauthenticated(Object principal, @Nullable Object credentials) {
public static UsernamePasswordAuthenticationToken unauthenticated(@Nullable Object principal,
@Nullable Object credentials) {
return new UsernamePasswordAuthenticationToken(principal, credentials);
}
@ -106,7 +107,7 @@ public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationT
}
@Override
public Object getPrincipal() {
public @Nullable Object getPrincipal() {
return this.principal;
}

View File

@ -178,8 +178,10 @@ public abstract class AbstractJaasAuthenticationProvider implements Authenticati
// applied.
authorities = getAuthorities(principals);
// Convert the authorities set back to an array and apply it to the token.
JaasAuthenticationToken result = new JaasAuthenticationToken(request.getPrincipal(),
request.getCredentials(), new ArrayList<>(authorities), loginContext);
Object principal = request.getPrincipal();
Assert.notNull(principal, "The principal cannot be null");
JaasAuthenticationToken result = new JaasAuthenticationToken(principal, request.getCredentials(),
new ArrayList<>(authorities), loginContext);
// Publish the success event
publishSuccessEvent(result);
// we're done, return the token.

View File

@ -0,0 +1,56 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.authentication.ott;
import java.io.Serial;
import java.util.Collection;
import org.jspecify.annotations.Nullable;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
/**
* The result of a successful one-time-token authentication
*
* @author Josh Cummings
* @since 7.0
*/
public class OneTimeTokenAuthentication extends AbstractAuthenticationToken {
@Serial
private static final long serialVersionUID = 1195893764725073959L;
private final Object principal;
public OneTimeTokenAuthentication(Object principal, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
setAuthenticated(true);
}
@Override
public Object getPrincipal() {
return this.principal;
}
@Override
public @Nullable Object getCredentials() {
return null;
}
}

View File

@ -56,8 +56,7 @@ public final class OneTimeTokenAuthenticationProvider implements AuthenticationP
}
try {
UserDetails user = this.userDetailsService.loadUserByUsername(consumed.getUsername());
OneTimeTokenAuthenticationToken authenticated = OneTimeTokenAuthenticationToken.authenticated(user,
user.getAuthorities());
OneTimeTokenAuthentication authenticated = new OneTimeTokenAuthentication(user, user.getAuthorities());
authenticated.setDetails(otpAuthenticationToken.getDetails());
return authenticated;
}

View File

@ -40,6 +40,10 @@ public class OneTimeTokenAuthenticationToken extends AbstractAuthenticationToken
private @Nullable String tokenValue;
/**
* @deprecated Please use constructor that takes a {@link String} instead
*/
@Deprecated(forRemoval = true, since = "7.0")
public OneTimeTokenAuthenticationToken(@Nullable Object principal, String tokenValue) {
super(Collections.emptyList());
this.tokenValue = tokenValue;
@ -50,6 +54,10 @@ public class OneTimeTokenAuthenticationToken extends AbstractAuthenticationToken
this(null, tokenValue);
}
/**
* @deprecated Please use {@link OneTimeTokenAuthentication} instead
*/
@Deprecated(forRemoval = true, since = "7.0")
public OneTimeTokenAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
@ -60,9 +68,11 @@ public class OneTimeTokenAuthenticationToken extends AbstractAuthenticationToken
* Creates an unauthenticated token
* @param tokenValue the one-time token value
* @return an unauthenticated {@link OneTimeTokenAuthenticationToken}
* @deprecated Please use constructor that takes a {@link String} instead
*/
public static OneTimeTokenAuthenticationToken unauthenticated(String tokenValue) {
return new OneTimeTokenAuthenticationToken(null, tokenValue);
@Deprecated(forRemoval = true, since = "7.0")
public static OneTimeTokenAuthenticationToken unauthenticated(@Nullable String tokenValue) {
return new OneTimeTokenAuthenticationToken(null, (tokenValue != null) ? tokenValue : "");
}
/**
@ -70,7 +80,9 @@ public class OneTimeTokenAuthenticationToken extends AbstractAuthenticationToken
* @param principal the principal
* @param tokenValue the one-time token value
* @return an unauthenticated {@link OneTimeTokenAuthenticationToken}
* @deprecated Please use constructor that takes a {@link String} instead
*/
@Deprecated(forRemoval = true, since = "7.0")
public static OneTimeTokenAuthenticationToken unauthenticated(Object principal, String tokenValue) {
return new OneTimeTokenAuthenticationToken(principal, tokenValue);
}
@ -80,7 +92,9 @@ public class OneTimeTokenAuthenticationToken extends AbstractAuthenticationToken
* @param principal the principal
* @param authorities the principal authorities
* @return an authenticated {@link OneTimeTokenAuthenticationToken}
* @deprecated Please use {@link OneTimeTokenAuthentication} instead
*/
@Deprecated(forRemoval = true, since = "7.0")
public static OneTimeTokenAuthenticationToken authenticated(Object principal,
Collection<? extends GrantedAuthority> authorities) {
return new OneTimeTokenAuthenticationToken(principal, authorities);

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