1
0
mirror of synced 2026-08-06 18:27:45 +00:00

Compare commits

...

193 Commits

Author SHA1 Message Date
github-actions[bot] 33d83158dd Release 6.4.0-M1 2024-07-15 16:49:35 +00:00
github-actions[bot] 5870a45a3d Merge branch '6.3.x' 2024-07-15 03:46:09 +00:00
github-actions[bot] cdcfb5d6c2 Merge branch '6.3.x' 2024-07-15 03:33:03 +00:00
dependabot[bot] 7e8c179a3f Bump org.springframework.data:spring-data-bom from 2024.0.1 to 2024.0.2
Bumps [org.springframework.data:spring-data-bom](https://github.com/spring-projects/spring-data-bom) from 2024.0.1 to 2024.0.2.
- [Release notes](https://github.com/spring-projects/spring-data-bom/releases)
- [Commits](https://github.com/spring-projects/spring-data-bom/compare/2024.0.1...2024.0.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-14 20:30:48 -07:00
github-actions[bot] 086f2394cb Merge branch '6.3.x' 2024-07-15 01:14:59 +00:00
github-actions[bot] 46c89f1305 Merge branch '6.3.x' 2024-07-15 01:05:14 +00:00
github-actions[bot] a9f5a54916 Merge branch '6.3.x' 2024-07-15 01:02:46 +00:00
github-actions[bot] 3629186b20 Merge branch '6.3.x' 2024-07-15 00:57:13 +00:00
github-actions[bot] a21a840035 Merge branch '6.3.x' 2024-07-15 00:48:21 +00:00
github-actions[bot] 3e9acc89e9 Merge branch '6.3.x' 2024-07-15 00:36:47 +00:00
Josh Cummings 796e4d6b6c Add query parameter support for authn requests
Closes gh-15017
2024-07-13 23:57:57 -06:00
Josh Cummings 587aa495f7 Merge branch '6.3.x' 2024-07-12 11:04:35 -06:00
github-actions[bot] 96b2a4fd35 Merge branch '6.3.x' 2024-07-12 04:07:06 +00:00
dependabot[bot] eee0dac835 Bump org.springframework:spring-framework-bom from 6.2.0-M4 to 6.2.0-M5
Bumps [org.springframework:spring-framework-bom](https://github.com/spring-projects/spring-framework) from 6.2.0-M4 to 6.2.0-M5.
- [Release notes](https://github.com/spring-projects/spring-framework/releases)
- [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.0-M4...v6.2.0-M5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-11 20:34:57 -07:00
github-actions[bot] 2bf0b0a75e Merge branch '6.3.x' 2024-07-12 03:26:26 +00:00
Josh Cummings e4e82d89d9 Add UnboundId Compatibility Check
Closes gh-15400
2024-07-11 18:02:09 -06:00
Josh Cummings 161b0f303d Merge branch '6.3.x' 2024-07-10 17:13:04 -06:00
Josh Cummings dcf5cc9e06 Extract EntityDescriptor to AssertingPartyDetails Logic
Closes gh-15090
2024-07-10 17:08:36 -06:00
Marcus Hert Da Coregio c885cee719 Merge branch '6.3.x' 2024-07-10 12:48:56 -03:00
github-actions[bot] d239806987 Merge branch '6.3.x' 2024-07-10 04:35:51 +00:00
github-actions[bot] 6a88784064 Merge branch '6.3.x' 2024-07-10 04:20:16 +00:00
github-actions[bot] e59f5a9771 Merge branch '6.3.x' 2024-07-10 04:14:26 +00:00
github-actions[bot] 90533d26b5 Merge branch '6.3.x' 2024-07-10 04:09:21 +00:00
dependabot[bot] 5437bf517c Bump io.projectreactor:reactor-bom from 2023.0.7 to 2023.0.8
Bumps [io.projectreactor:reactor-bom](https://github.com/reactor/reactor) from 2023.0.7 to 2023.0.8.
- [Release notes](https://github.com/reactor/reactor/releases)
- [Commits](https://github.com/reactor/reactor/compare/2023.0.7...2023.0.8)

---
updated-dependencies:
- dependency-name: io.projectreactor:reactor-bom
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-09 21:06:01 -07:00
Rob Winch 5bd4db1a13 Use javadoc macro
Closes gh-15386
2024-07-09 22:35:01 -05:00
Mateus Scheper d6874d9048 Fixing URL on README
Changing URL from https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/ to https://docs.spring.io/spring-security/reference/ since the first one doesn't exist.
2024-07-09 07:25:58 -07:00
Antoine Rey f184d13096 Update the OAuth2 jwt and opaque resource server documentation with the Lambda DSL
The OAuth2ResourceServerConfigurer::opaqueToken() and ::jwt() methods are deprecated since Spring Security 6.1
2024-07-09 07:25:13 -07:00
Marcus Hert Da Coregio beff600d95 Merge branch '6.3.x' 2024-07-09 08:17:12 -03:00
github-actions[bot] ec4cad6657 Merge branch '6.3.x' 2024-07-09 04:18:26 +00:00
github-actions[bot] 230dae602f Merge branch '6.3.x' 2024-07-09 04:09:27 +00:00
dependabot[bot] 527f816ff8 Bump io.micrometer:micrometer-observation from 1.12.7 to 1.12.8
Bumps [io.micrometer:micrometer-observation](https://github.com/micrometer-metrics/micrometer) from 1.12.7 to 1.12.8.
- [Release notes](https://github.com/micrometer-metrics/micrometer/releases)
- [Commits](https://github.com/micrometer-metrics/micrometer/compare/v1.12.7...v1.12.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-08 21:03:09 -07:00
Josh Cummings 62c68cb7ac Merge branch '6.3.x' 2024-07-08 20:52:56 -06:00
Seungrae 148e7843bf Fix typos and formatting in documentation 2024-07-08 19:51:06 -07:00
Marcus Hert Da Coregio ce018ff003 Merge branch '6.3.x' 2024-07-08 11:09:41 -03:00
github-actions[bot] 5496eca360 Merge branch '6.3.x' 2024-07-08 04:00:21 +00:00
github-actions[bot] 2e7f61d794 Merge branch '6.3.x' 2024-07-08 03:53:56 +00:00
github-actions[bot] c6017e7c28 Merge branch '6.3.x' 2024-07-08 03:50:26 +00:00
github-actions[bot] 88dc249fde Merge branch '6.3.x' 2024-07-08 03:49:39 +00:00
dependabot[bot] ebb842e654 Bump com.fasterxml.jackson:jackson-bom from 2.17.1 to 2.17.2
Bumps [com.fasterxml.jackson:jackson-bom](https://github.com/FasterXML/jackson-bom) from 2.17.1 to 2.17.2.
- [Commits](https://github.com/FasterXML/jackson-bom/compare/jackson-bom-2.17.1...jackson-bom-2.17.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-07 20:21:50 -07:00
dependabot[bot] 41f1706b06 Bump com.github.spullara.mustache.java:compiler from 0.9.13 to 0.9.14
Bumps [com.github.spullara.mustache.java:compiler](https://github.com/spullara/mustache.java) from 0.9.13 to 0.9.14.
- [Commits](https://github.com/spullara/mustache.java/compare/mustache.java-0.9.13...mustache.java-0.9.14)

---
updated-dependencies:
- dependency-name: com.github.spullara.mustache.java:compiler
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-07 20:19:25 -07:00
dependabot[bot] ba38cdad0e Bump org-apache-maven-resolver from 1.9.20 to 1.9.21
Bumps `org-apache-maven-resolver` from 1.9.20 to 1.9.21.

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

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

Updates `org.apache.maven.resolver:maven-resolver-transport-http` from 1.9.20 to 1.9.21

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-07 20:17:59 -07:00
github-actions[bot] 3b9a2c3def Merge branch '6.3.x' 2024-07-04 04:09:21 +00:00
github-actions[bot] f01ffd136c Merge branch '6.3.x' 2024-07-04 03:33:04 +00:00
dependabot[bot] 298a75ebbd Bump org-eclipse-jetty from 11.0.21 to 11.0.22
Bumps `org-eclipse-jetty` from 11.0.21 to 11.0.22.

Updates `org.eclipse.jetty:jetty-server` from 11.0.21 to 11.0.22

Updates `org.eclipse.jetty:jetty-servlet` from 11.0.21 to 11.0.22

---
updated-dependencies:
- dependency-name: org.eclipse.jetty:jetty-server
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.eclipse.jetty:jetty-servlet
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-03 20:28:51 -07:00
github-actions[bot] b30a1d4cae Merge branch '6.3.x' 2024-07-04 03:28:04 +00:00
Marcus Hert Da Coregio 19cde35bc1 Merge branch '6.3.x' 2024-07-03 13:48:33 -03:00
Josh Cummings 773e86701e Add ParameterRequestMatcher
Closes gh-15342
2024-07-02 15:17:54 -06:00
Marcus Hert Da Coregio 207680ba02 Try check task
Issue gh-15265
2024-07-02 12:07:31 -03:00
Marcus Hert Da Coregio 5b44972e14 Revert "Disable check-samples temporarily"
This reverts commit fd1246a0b0.
2024-07-02 10:18:47 -03:00
Josh Cummings 6bd2f1ca97 Deprecate OpenSamlRelyingPartyRegistration
Closes gh-15343
2024-07-01 19:50:54 -06:00
Josh Cummings 7b39800606 Add CachingRelyingPartyRegistrationRepository
Closes gh-15341
2024-07-01 19:43:09 -06:00
Josh Cummings 1e2900328b Add IterableRelyingPartyRegistrationRepository
Closes gh-15027
2024-07-01 19:43:09 -06:00
Josh Cummings 850c0a4690 Fix Spring Framework Reference Link 2024-07-01 19:22:56 -06:00
Josh Cummings f532807836 Merge branch '6.3.x' 2024-07-01 17:20:52 -06:00
Dumitru Boldureanu 48826201b1 Update architecture.adoc
The list of filters is printed at DEBUG level on the application startup and not INFO level, see DefaultSecurityFilterChain
2024-07-01 15:19:50 -06:00
Stefan Ganzer ceb278c908 Update events.adoc
Changes GenericAuthenticationFailureEvent to AbstractAuthenticationFailureEvent

The class GenericAuthenticationFailureEvent does not exist.
2024-07-01 15:19:00 -06:00
Stefan Ganzer e7212b37f7 Update events.adoc
Changes type to DefaultAuthenticationEventPublisher

Only DefaultAuthenticationEventPublisher has the method
setDefaultAuthenticationFailureEvent, but not the interface
AuthenticationEventPublisher.
2024-07-01 15:19:00 -06:00
Marcono1234 a9aefafb76 Fix malformed list in "Using Method Parameters" documentation 2024-07-01 15:18:17 -06:00
Josh Cummings 9f0b2a29ee Merge branch '6.3.x' 2024-07-01 11:43:19 -06:00
github-actions[bot] e076a4b838 Merge branch '6.3.x' 2024-07-01 04:16:28 +00:00
github-actions[bot] a7a8494ee4 Merge branch '6.3.x' 2024-07-01 04:12:27 +00:00
dependabot[bot] d159a2e8fe Bump org.skyscreamer:jsonassert from 1.5.1 to 1.5.3
Bumps [org.skyscreamer:jsonassert](https://github.com/skyscreamer/JSONassert) from 1.5.1 to 1.5.3.
- [Release notes](https://github.com/skyscreamer/JSONassert/releases)
- [Changelog](https://github.com/skyscreamer/JSONassert/blob/master/CHANGELOG.md)
- [Commits](https://github.com/skyscreamer/JSONassert/compare/jsonassert-1.5.1...jsonassert-1.5.3)

---
updated-dependencies:
- dependency-name: org.skyscreamer:jsonassert
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-30 20:50:42 -07:00
github-actions[bot] 7ea7749dba Merge branch '6.3.x' 2024-07-01 03:44:07 +00:00
github-actions[bot] 133eb89708 Merge branch '6.3.x' 2024-07-01 01:20:50 +00:00
dependabot[bot] d16f2655c9 Bump antora from 3.2.0-alpha.4 to 3.2.0-alpha.5 in /docs
Bumps [antora](https://gitlab.com/antora/antora) from 3.2.0-alpha.4 to 3.2.0-alpha.5.
- [Changelog](https://gitlab.com/antora/antora/blob/main/CHANGELOG.adoc)
- [Commits](https://gitlab.com/antora/antora/compare/v3.2.0-alpha.4...v3.2.0-alpha.5)

---
updated-dependencies:
- dependency-name: antora
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-30 18:13:34 -07:00
github-actions[bot] 37c6e217e9 Merge branch '6.3.x' 2024-07-01 00:56:26 +00:00
github-actions[bot] d880aa95d0 Merge branch '6.3.x' 2024-07-01 00:49:50 +00:00
Marcus Hert Da Coregio 8572764583 Merge branch '6.3.x'
Closes gh-15322
2024-06-28 15:34:28 -03:00
github-actions[bot] a45888958c Merge branch '6.3.x' 2024-06-28 12:45:43 +00:00
dependabot[bot] 694bc038d2 Bump org.junit:junit-bom from 5.10.2 to 5.10.3
Bumps [org.junit:junit-bom](https://github.com/junit-team/junit5) from 5.10.2 to 5.10.3.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.10.2...r5.10.3)

---
updated-dependencies:
- dependency-name: org.junit:junit-bom
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-28 05:44:33 -07:00
Marcus Hert Da Coregio ff0eeaddea Merge branch '6.3.x' 2024-06-28 09:32:45 -03:00
Marcus Hert Da Coregio 8c6658d890 Merge branch '6.3.x'
Closes gh-15311
2024-06-27 14:36:03 -03:00
Marcus Hert Da Coregio dd7472272a Merge branch '6.3.x'
Closes gh-15306
2024-06-26 14:04:27 -03:00
James Howe 902aff451f Update OpenSaml4AuthenticationProvider.java
Fix level of log message.
2024-06-24 14:10:54 -06:00
github-actions[bot] bef63469dd Merge branch '6.3.x' 2024-06-24 19:14:38 +00:00
Josh Cummings d5328df82d Merge branch '6.3.x' 2024-06-24 13:02:15 -06:00
dependabot[bot] 644c5ed1e1 Bump spring-io/spring-doc-actions
Bumps [spring-io/spring-doc-actions](https://github.com/spring-io/spring-doc-actions) from 5a57bcc6a0da2a1474136cf29571b277850432bc to 852920ba3fb1f28b35a2f13201133bc00ef33677.
- [Commits](https://github.com/spring-io/spring-doc-actions/compare/5a57bcc6a0da2a1474136cf29571b277850432bc...852920ba3fb1f28b35a2f13201133bc00ef33677)

---
updated-dependencies:
- dependency-name: spring-io/spring-doc-actions
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-23 17:42:45 -07:00
Josh Cummings 672902a8f3 Add Metadata Content Type Test
Issue gh-15147
2024-06-21 16:01:29 -06:00
Josh Cummings a529607d42 Add samlmetadata Qualifier to Content Type
Closes gh-15147
2024-06-21 12:57:10 -06:00
Josh Cummings 78d2be9bd5 Polish LDAP Module
Issue gh-3834
2024-06-21 12:42:07 -06:00
Josh Cummings 55895f3b08 Update Deprecated Usage
Issue gh-3834
2024-06-21 12:42:06 -06:00
Josh Cummings ef5c1a72db Support Handling Javax-based Bind Exceptions
Closes gh-3834
2024-06-21 12:42:06 -06:00
Marcus Hert Da Coregio fd1246a0b0 Disable check-samples temporarily
Issue gh-15265
2024-06-21 09:49:41 -03:00
Marcus Hert Da Coregio 16f7590740 Merge branch '6.3.x' 2024-06-20 15:48:59 -03:00
github-actions[bot] 70213258ef Update Antora Spring UI to v0.4.16 2024-06-20 15:48:09 -03:00
Marcus Hert Da Coregio caee65b2e4 Add update-antora-ui-spring.yml
Issue gh-15106
2024-06-20 15:45:21 -03:00
github-actions[bot] cbd1340e78 Merge branch '6.3.x' 2024-06-20 03:53:43 +00:00
github-actions[bot] cdd3ebed2c Merge branch '6.3.x' 2024-06-20 03:31:07 +00:00
Marcus Hert Da Coregio 5a6c87d999 Update test against snapshot versions
Issue gh-15155
2024-06-19 09:15:29 -03:00
Marcus Hert Da Coregio aa9c1bab67 Upgrade to Spring Framework 6.2.0-M4
Closes gh-15266
2024-06-18 14:07:05 -03:00
dependabot[bot] 1736a8680e Bump org.jfrog.buildinfo:build-info-extractor-gradle
Bumps [org.jfrog.buildinfo:build-info-extractor-gradle](https://github.com/jfrog/build-info) from 4.33.19 to 4.33.20.
- [Release notes](https://github.com/jfrog/build-info/releases)
- [Changelog](https://github.com/jfrog/build-info/blob/master/RELEASE.md)
- [Commits](https://github.com/jfrog/build-info/compare/build-info-gradle-extractor-4.33.19...build-info-gradle-extractor-4.33.20)

---
updated-dependencies:
- dependency-name: org.jfrog.buildinfo:build-info-extractor-gradle
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-17 20:46:55 -07:00
dependabot[bot] 88f6c5a01c Bump org.apache.maven:maven-resolver-provider from 3.9.7 to 3.9.8
Bumps [org.apache.maven:maven-resolver-provider](https://github.com/apache/maven) from 3.9.7 to 3.9.8.
- [Release notes](https://github.com/apache/maven/releases)
- [Commits](https://github.com/apache/maven/compare/maven-3.9.7...maven-3.9.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-17 20:46:31 -07:00
github-actions[bot] dce96b012a Merge branch '6.3.x' 2024-06-18 03:33:02 +00:00
Josh Cummings 0e7566ede3 Adjust any-request check
Storing the request matcher outside of the for loop means that
if one of the SecurityFilterChain instances is not of type
DefaultSecurityFilterChain, then the error may print out an
earlier request matcher instead of the current one.

Instead, this commit changes to print out the entire filter chain
so that it can be inside of the for loop, regardless of type.

Issue gh-15220
2024-06-17 14:34:03 -06:00
Max Batischev 4c780bf8d4 Add support checking AnyRequestMatcher securityFilterChains
Closes gh-15220
2024-06-17 13:05:36 -06:00
Marcus Hert Da Coregio 470cb46e38 Merge branch '6.3.x' 2024-06-17 16:04:16 -03:00
Marcus Hert Da Coregio 8fd9997a47 Remove outdated note
Closes gh-15263
2024-06-17 08:12:54 -03:00
github-actions[bot] 982ee7dc17 Merge branch '6.3.x' 2024-06-17 04:05:02 +00:00
github-actions[bot] 7173f675c9 Merge branch '6.3.x' 2024-06-17 03:46:38 +00:00
github-actions[bot] dcbc0d6070 Merge branch '6.3.x' 2024-06-17 03:45:44 +00:00
dependabot[bot] 33c0ebdffe Bump org.jfrog.buildinfo:build-info-extractor-gradle
Bumps [org.jfrog.buildinfo:build-info-extractor-gradle](https://github.com/jfrog/build-info) from 4.33.17 to 4.33.19.
- [Release notes](https://github.com/jfrog/build-info/releases)
- [Changelog](https://github.com/jfrog/build-info/blob/master/RELEASE.md)
- [Commits](https://github.com/jfrog/build-info/compare/build-info-gradle-extractor-4.33.17...build-info-gradle-extractor-4.33.19)

---
updated-dependencies:
- dependency-name: org.jfrog.buildinfo:build-info-extractor-gradle
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-16 20:39:32 -07:00
dependabot[bot] 2175f2878d Bump org.springframework.data:spring-data-bom from 2024.0.0 to 2024.0.1
Bumps [org.springframework.data:spring-data-bom](https://github.com/spring-projects/spring-data-bom) from 2024.0.0 to 2024.0.1.
- [Release notes](https://github.com/spring-projects/spring-data-bom/releases)
- [Commits](https://github.com/spring-projects/spring-data-bom/compare/2024.0.0...2024.0.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-16 20:39:26 -07:00
github-actions[bot] 538536aa4d Merge branch '6.3.x' 2024-06-14 04:20:20 +00:00
github-actions[bot] 62d2fd0efd Merge branch '6.3.x' 2024-06-14 03:49:02 +00:00
github-actions[bot] ba2d0cc169 Merge branch '6.3.x' 2024-06-14 03:44:40 +00:00
dependabot[bot] 55a704d2a7 Bump org.springframework:spring-framework-bom from 6.1.8 to 6.1.9
Bumps [org.springframework:spring-framework-bom](https://github.com/spring-projects/spring-framework) from 6.1.8 to 6.1.9.
- [Release notes](https://github.com/spring-projects/spring-framework/releases)
- [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.8...v6.1.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-13 20:30:26 -07:00
github-actions[bot] c02124ffb4 Merge branch '6.3.x' 2024-06-14 03:27:28 +00:00
dependabot[bot] 5a3f1c8b4b Bump org.springframework.ldap:spring-ldap-core from 3.2.3 to 3.2.4
Bumps [org.springframework.ldap:spring-ldap-core](https://github.com/spring-projects/spring-ldap) from 3.2.3 to 3.2.4.
- [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.3...3.2.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-13 20:25:24 -07:00
github-actions[bot] af03f4567d Merge branch '6.3.x' 2024-06-14 03:23:41 +00:00
Steve Riesenberg 7eaab95639 Polish gh-15237 2024-06-13 16:05:15 -05:00
Max Batischev 4e52eda0f5 Add support configuring OAuth2AuthorizationRequestResolver as bean
Closes gh-15236
2024-06-13 16:05:15 -05:00
Steve Riesenberg 60a6b3845d Merge branch '6.3.x'
Closes gh-15246
2024-06-13 16:04:30 -05:00
dependabot[bot] 4cbeea78a8 Bump com.gradle.develocity from 3.17.4 to 3.17.5
Bumps com.gradle.develocity from 3.17.4 to 3.17.5.

---
updated-dependencies:
- dependency-name: com.gradle.develocity
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-12 21:03:41 -07:00
github-actions[bot] b3b53e47ea Merge branch '6.3.x' 2024-06-13 03:54:55 +00:00
github-actions[bot] e4fc79afc5 Merge branch '6.3.x' 2024-06-13 03:46:58 +00:00
github-actions[bot] e977c2a3e9 Merge branch '6.3.x' 2024-06-13 03:24:07 +00:00
Junhyunny 68aef2056e Fix wrong explanation for @PostAuthorize annotation 2024-06-12 11:15:51 -03:00
github-actions[bot] edd6182fb0 Merge branch '6.3.x' 2024-06-12 04:30:59 +00:00
github-actions[bot] 35d738f583 Merge branch '6.3.x' 2024-06-12 04:14:02 +00:00
dependabot[bot] 2c4e0c55ae Bump io.projectreactor:reactor-bom from 2023.0.6 to 2023.0.7
Bumps [io.projectreactor:reactor-bom](https://github.com/reactor/reactor) from 2023.0.6 to 2023.0.7.
- [Release notes](https://github.com/reactor/reactor/releases)
- [Commits](https://github.com/reactor/reactor/compare/2023.0.6...2023.0.7)

---
updated-dependencies:
- dependency-name: io.projectreactor:reactor-bom
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-11 21:12:54 -07:00
dependabot[bot] f0fe62f219 Bump org.hibernate.orm:hibernate-core from 6.4.8.Final to 6.4.9.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.4.8.Final to 6.4.9.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.4.9/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.4.8...6.4.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-11 21:08:51 -07:00
github-actions[bot] a21c5161e8 Merge branch '6.3.x' 2024-06-12 04:01:45 +00:00
github-actions[bot] 9bb9c930d2 Merge branch '6.3.x' 2024-06-12 04:00:59 +00:00
Marcus Hert Da Coregio 0e90f762d3 Add pre-push hook
Issue gh-15028
2024-06-11 13:55:57 -03:00
github-actions[bot] 33a108f974 Merge branch '6.3.x' 2024-06-11 04:12:28 +00:00
github-actions[bot] a65c182798 Merge branch '6.3.x' 2024-06-11 03:43:06 +00:00
dependabot[bot] 07bb9db24e Bump io.micrometer:micrometer-observation from 1.12.6 to 1.12.7
Bumps [io.micrometer:micrometer-observation](https://github.com/micrometer-metrics/micrometer) from 1.12.6 to 1.12.7.
- [Release notes](https://github.com/micrometer-metrics/micrometer/releases)
- [Commits](https://github.com/micrometer-metrics/micrometer/compare/v1.12.6...v1.12.7)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-10 20:41:03 -07:00
Ivanov Maxim Romanovich 725e35977b Removed extra import word 2024-06-10 15:27:19 -06:00
Ivanov Maxim Romanovich 5bffa4e454 Fix import for example 2024-06-10 15:27:19 -06:00
Marcus Hert Da Coregio b4c8fdf91d Add missing @Test annotation 2024-06-10 15:43:52 -03:00
Marcus Hert Da Coregio 7c43fc111f Support RoleHierarchy Bean in authorizeHttpRequests Kotlin DSL
Closes gh-15136
2024-06-10 15:41:28 -03:00
github-actions[bot] ed2b654f71 Merge branch '6.3.x' 2024-06-10 04:19:30 +00:00
github-actions[bot] 2442ba3b47 Merge branch '6.3.x' 2024-06-10 04:17:26 +00:00
github-actions[bot] 1dfbc2d9e4 Merge branch '6.3.x' 2024-06-10 03:51:03 +00:00
dependabot[bot] b4c5a82c2e Bump io-spring-javaformat from 0.0.41 to 0.0.42
Bumps `io-spring-javaformat` from 0.0.41 to 0.0.42.

Updates `io.spring.javaformat:spring-javaformat-checkstyle` from 0.0.41 to 0.0.42
- [Release notes](https://github.com/spring-io/spring-javaformat/releases)
- [Commits](https://github.com/spring-io/spring-javaformat/compare/v0.0.41...v0.0.42)

Updates `io.spring.javaformat:spring-javaformat-gradle-plugin` from 0.0.41 to 0.0.42
- [Release notes](https://github.com/spring-io/spring-javaformat/releases)
- [Commits](https://github.com/spring-io/spring-javaformat/compare/v0.0.41...v0.0.42)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-09 20:41:06 -07:00
dependabot[bot] c5cba930b0 Bump org.jfrog.buildinfo:build-info-extractor-gradle
Bumps [org.jfrog.buildinfo:build-info-extractor-gradle](https://github.com/jfrog/build-info) from 4.33.16 to 4.33.17.
- [Release notes](https://github.com/jfrog/build-info/releases)
- [Changelog](https://github.com/jfrog/build-info/blob/master/RELEASE.md)
- [Commits](https://github.com/jfrog/build-info/compare/build-info-gradle-extractor-4.33.16...build-info-gradle-extractor-4.33.17)

---
updated-dependencies:
- dependency-name: org.jfrog.buildinfo:build-info-extractor-gradle
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-09 20:40:19 -07:00
Josh Cummings 8f167d93a7 Update Wrapper Upgrade Configuration
Issue gh-15155
2024-06-06 20:03:44 -06:00
Josh Cummings aa9e1b5088 Merge branch '6.3.x' 2024-06-06 17:30:15 -06:00
Josh Cummings 4ca0de9c2d Sync XSD with RncToXsd Task 2024-06-06 15:17:56 -06:00
Josh Cummings a7f9ccb6d6 Use GrantedAuthorityDefaults Bean in Kotlin DSL
Closes gh-15171
2024-06-06 15:16:32 -06:00
Josh Cummings 24e3bb11bc Merge branch '6.3.x' 2024-06-06 13:46:08 -06:00
Josh Cummings 87ee464dce Merge branch '6.3.x' 2024-06-06 13:36:39 -06:00
MateuszLenczewski 077439c73e Fix typo in JwtDecoder 2024-06-05 17:38:12 -06:00
Marcus Hert Da Coregio 1b2d2ed611 Merge branch '6.3.x' 2024-06-05 14:16:42 -03:00
Marcus Hert Da Coregio 0cbec13f87 Merge branch '6.3.x'
Closes gh-15206
2024-06-05 13:59:47 -03:00
Max Batischev 44f9396bad Add support remember-me cookie customization
Closes gh-14990
2024-06-05 11:47:20 -03:00
Steve Riesenberg 3acd2c65d9 Add since 6.4
Issue gh-15012
2024-06-04 13:44:33 -05:00
Josh Cummings 81abc453fe Merge branch '6.3.x' 2024-06-03 17:43:12 -06:00
Steve Riesenberg 7c45ebd81c Polish gh-15012 2024-06-03 17:02:38 -05:00
Filip Hrisafov 99aee99b34 Expose user name attribute name in OAuth2UserAuthority 2024-06-03 12:30:34 -05:00
github-actions[bot] b41ec0ae4b Merge branch '6.3.x' 2024-06-03 03:55:54 +00:00
github-actions[bot] 55e82d137f Merge branch '6.3.x' 2024-06-03 03:51:08 +00:00
github-actions[bot] 654b15d8c2 Merge branch '6.3.x' 2024-06-03 03:35:22 +00:00
dependabot[bot] 1665824637 Bump org.hsqldb:hsqldb from 2.7.2 to 2.7.3
Bumps org.hsqldb:hsqldb from 2.7.2 to 2.7.3.

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-02 20:17:59 -07:00
Steve Riesenberg 8ae7cb175b Merge branch '6.3.x'
Closes gh-15187
2024-05-31 19:02:55 -05:00
Josh Cummings 9101bf1f7d Allow logout+jwt JWT type
Closes gh-15003
2024-05-31 14:41:05 -06:00
Josh Cummings b73155df26 Merge branch '6.3.x' 2024-05-31 14:14:02 -06:00
Josh Cummings c4d8693063 Merge branch '6.3.x' 2024-05-31 13:46:50 -06:00
Josh Cummings 6695784a3f Polish Logging
Issue gh-14801
2024-05-31 12:48:07 -06:00
Max Batischev 801e808f67 Add support sign SAML metadata
Closes gh-14801
2024-05-31 12:43:19 -06:00
Josh Cummings f104d1aeea Update Copyright
PR gh-15013
2024-05-31 12:39:17 -06:00
earlgrey02 3b7f714f00 Add SecurityContextRepository to Kotlin Reactive DSL 2024-05-31 12:38:17 -06:00
Juliana Hachmann 9a714424d5 Adds missing translated messages for PT-BR
Partially fix #spring-projectsgh-9315

Adds Brazilian Portuguese translation missing for following messages in messages_pt_BR.properties;
- ExceptionTranslationFilter.insufficientAuthentication 
- LdapAuthenticationProvider.badLdapConnection
- PersistentTokenBasedRememberMeServices.cookieStolen
2024-05-31 12:36:52 -06:00
Josh Cummings aa9bf83c6d Polish Exception Handling
Issue gh-15093
2024-05-31 12:34:33 -06:00
Blagoja Stamatovski 63f48167bd Add Kotlin support to PreFilter and PostFilter annotations
Closes gh-15093
2024-05-31 12:32:28 -06:00
Josh Cummings fbeb82ef62 Merge branch '6.3.x' 2024-05-30 17:52:04 -06:00
Stian Jørgensrud b5e9c4cb9f Fix wrong Kotlin syntax 2024-05-30 17:24:54 -05:00
Stian Jørgensrud 3cd9b77273 Remove nullable from Kotlin example
Use `lateinit` instead of allowing NullPointerExceptions.
2024-05-30 17:24:54 -05:00
Steve Riesenberg 478c3d0313 Merge branch '6.3.x' 2024-05-29 16:24:35 -05:00
Steve Riesenberg ee9f5a2d5e Improve CSRF example for single-page apps
Closes gh-15105
2024-05-29 11:54:08 -05:00
Josh Cummings 17064fc7fb Merge branch '6.3.x' 2024-05-28 12:44:39 -06:00
github-actions[bot] 58747d2621 Merge branch '6.3.x' 2024-05-28 03:56:21 +00:00
dependabot[bot] fdf4689f79 Bump org.jfrog.buildinfo:build-info-extractor-gradle
Bumps [org.jfrog.buildinfo:build-info-extractor-gradle](https://github.com/jfrog/build-info) from 4.33.15 to 4.33.16.
- [Release notes](https://github.com/jfrog/build-info/releases)
- [Changelog](https://github.com/jfrog/build-info/blob/master/RELEASE.md)
- [Commits](https://github.com/jfrog/build-info/compare/build-info-gradle-extractor-4.33.15...build-info-gradle-extractor-4.33.16)

---
updated-dependencies:
- dependency-name: org.jfrog.buildinfo:build-info-extractor-gradle
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-27 20:31:07 -07:00
Marcus Hert Da Coregio c89647a56e Deprecate shouldFilterAllDispatcherTypes from Kotlin DSL
Issue gh-12138
2024-05-27 09:00:54 -03:00
Marcus Hert Da Coregio 9f44f3b79a Deprecate authorizeRequests from Kotlin DSL
Closes gh-15173
2024-05-27 08:51:32 -03:00
github-actions[bot] c534e5ea5b Merge branch '6.3.x' 2024-05-27 04:10:08 +00:00
dependabot[bot] adc9f0f36e Bump org.apache.maven:maven-resolver-provider from 3.9.6 to 3.9.7
Bumps [org.apache.maven:maven-resolver-provider](https://github.com/apache/maven) from 3.9.6 to 3.9.7.
- [Release notes](https://github.com/apache/maven/releases)
- [Commits](https://github.com/apache/maven/compare/maven-3.9.6...maven-3.9.7)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-26 20:57:16 -07:00
github-actions[bot] 7da803f00c Merge branch '6.3.x' 2024-05-27 03:34:27 +00:00
Caio Henrique 67853d585e docs: add correction to java example in multitenanci.adoc 2024-05-24 16:59:13 -06:00
Steve Riesenberg 9b1e9c5db9 Polish and sync java and kotlin configuration docs
Issue gh-15029
2024-05-24 16:30:59 -05:00
Josh Cummings 133c87a643 Merge branch '6.3.x' 2024-05-24 15:06:47 -06:00
Josh Cummings 8b0ad045d3 Merge branch '6.3.x' 2024-05-24 14:27:07 -06:00
Steve Riesenberg 5e2209aeef Fix xref link
Issue gh-15029
2024-05-24 15:11:47 -05:00
Steve Riesenberg 2482e8e446 Polish gh-15029 2024-05-24 14:58:45 -05:00
Abimael Sergio 046a1fc811 Update description of securityMatcher and multiple filter chains
Closes gh-14419
2024-05-24 12:55:41 -05:00
Marcus Hert Da Coregio 3002a82705 Remove unused import
Issue gh-10277
2024-05-24 14:13:34 -03:00
Kyoungwoong 9b2910cd42 Add setter method for userDetailsChecker in CasAuthenticationProvider(#10277)
This commit introduces a setter method for the userDetailsChecker property in the CasAuthenticationProvider class. Previously, the userDetailsChecker was initialized with a default AccountStatusUserDetailsChecker instance, limiting customization options. Now, users can inject their own UserDetailsChecker implementation through the setter method, providing greater flexibility in handling user details validation.
2024-05-24 14:00:56 -03:00
Lukas Dohmen 3af85a562a GH-14688 Use inter-document xref to fix Username and password link
GH-14688 Use inter-document xref to fix username and password link
2024-05-24 13:52:10 -03:00
Marcus Hert Da Coregio 1d3cb3f28e Support RoleHierarchy in AclAuthorizationStrategyImpl
Closes gh-4186
2024-05-24 13:49:48 -03:00
dependabot[bot] 752a56a9d9 Bump @antora/collector-extension in /docs
---
updated-dependencies:
- dependency-name: "@antora/collector-extension"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-24 07:51:13 -07:00
github-actions[bot] cd128fb66d Merge branch '6.3.x' 2024-05-24 14:45:22 +00:00
github-actions[bot] d3e5955642 Merge branch '6.3.x' 2024-05-24 14:44:09 +00:00
Marcus Hert Da Coregio f6ea99d8a3 Prepare for Spring Security 6.4
Closes gh-15155
2024-05-24 11:41:28 -03:00
185 changed files with 8638 additions and 1659 deletions
+51
View File
@@ -54,6 +54,31 @@ updates:
update-types:
- version-update:semver-major
- version-update:semver-minor
- package-ecosystem: gradle
target-branch: 6.3.x
directory: /
schedule:
interval: daily
time: '03:00'
timezone: Etc/UTC
labels:
- 'type: dependency-upgrade'
registries:
- spring-milestones
ignore:
- dependency-name: com.nimbusds:nimbus-jose-jwt
- dependency-name: org.python:jython
- dependency-name: org.apache.directory.server:*
- dependency-name: org.junit:junit-bom
update-types:
- version-update:semver-major
- dependency-name: org.mockito:mockito-bom
update-types:
- version-update:semver-major
- dependency-name: '*'
update-types:
- version-update:semver-major
- version-update:semver-minor
- package-ecosystem: gradle
target-branch: main
directory: /
@@ -83,6 +108,7 @@ updates:
update-types:
- version-update:semver-major
- version-update:semver-minor
- package-ecosystem: github-actions
target-branch: 5.8.x
directory: /
@@ -93,6 +119,26 @@ updates:
- 'in: build'
ignore:
- dependency-name: sjohnr/*
- package-ecosystem: github-actions
target-branch: 6.2.x
directory: /
schedule:
interval: weekly
labels:
- 'type: task'
- 'in: build'
ignore:
- dependency-name: sjohnr/*
- package-ecosystem: github-actions
target-branch: 6.3.x
directory: /
schedule:
interval: weekly
labels:
- 'type: task'
- 'in: build'
ignore:
- dependency-name: sjohnr/*
- package-ecosystem: github-actions
target-branch: main
directory: /
@@ -125,6 +171,11 @@ updates:
directory: /docs
schedule:
interval: weekly
- package-ecosystem: npm
target-branch: 6.3.x
directory: /docs
schedule:
interval: weekly
- package-ecosystem: npm
target-branch: 6.2.x
directory: /docs
@@ -39,7 +39,7 @@ jobs:
toolchain: 17
with:
java-version: ${{ matrix.java-version }}
test-args: --refresh-dependencies -PforceMavenRepositories=snapshot -PisOverrideVersionCatalog -PtestToolchain=${{ matrix.toolchain }} -PspringFrameworkVersion=6.1.+ -PreactorVersion=2023.0.+ -PspringDataVersion=2023.1.+ --stacktrace
test-args: --refresh-dependencies -PforceMavenRepositories=snapshot -PisOverrideVersionCatalog -PtestToolchain=${{ matrix.toolchain }} -PspringFrameworkVersion=6.2.+ -PreactorVersion=2023.0.+ -PspringDataVersion=2024.0.+ --stacktrace
secrets: inherit
check-samples:
name: Check Samples
@@ -63,7 +63,7 @@ jobs:
samples_branch=$(cat gradle.properties | grep "samplesBranch=" | awk -F'=' '{print $2}')
./gradlew publishMavenJavaPublicationToLocalRepository
./gradlew cloneRepository -PrepositoryName="spring-projects/spring-security-samples" -Pref="$samples_branch" -PcloneOutputDirectory="$SAMPLES_DIR"
./gradlew --project-dir "$SAMPLES_DIR" --init-script spring-security-ci.gradle -PlocalRepositoryPath="$LOCAL_REPOSITORY_PATH" -PspringSecurityVersion="$version" :runAllTests
./gradlew --project-dir "$SAMPLES_DIR" --init-script spring-security-ci.gradle -PlocalRepositoryPath="$LOCAL_REPOSITORY_PATH" -PspringSecurityVersion="$version" check
check-tangles:
name: Check for Package Tangles
runs-on: ubuntu-latest
@@ -25,7 +25,7 @@ jobs:
java-version: '17'
distribution: 'temurin'
- name: Set up Gradle
uses: gradle/gradle-build-action@v3
uses: gradle/gradle-build-action@v2
- name: Upgrade Wrappers
run: ./gradlew clean upgradeGradleWrapperAll --continue -Porg.gradle.java.installations.auto-download=false
env:
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
strategy:
matrix:
# List of active maintenance branches.
branch: [ main, 6.2.x, 6.1.x, 5.8.x ]
branch: [ main, 6.3.x, 6.2.x, 5.8.x ]
runs-on: ubuntu-latest
steps:
- name: Checkout
@@ -0,0 +1,35 @@
name: Update Antora UI Spring
on:
schedule:
- cron: '0 10 * * *' # Once per day at 10am UTC
workflow_dispatch:
permissions:
pull-requests: write
issues: write
contents: write
jobs:
update-antora-ui-spring:
runs-on: ubuntu-latest
name: Update on Supported Branches
strategy:
matrix:
branch: [ '5.8.x', '6.2.x', '6.3.x', 'main' ]
steps:
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@852920ba3fb1f28b35a2f13201133bc00ef33677
name: Update
with:
docs-branch: ${{ matrix.branch }}
token: ${{ secrets.GITHUB_TOKEN }}
antora-file-path: 'docs/antora-playbook.yml'
update-antora-ui-spring-docs-build:
runs-on: ubuntu-latest
name: Update on docs-build
steps:
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@852920ba3fb1f28b35a2f13201133bc00ef33677
name: Update
with:
docs-branch: 'docs-build'
token: ${{ secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -18,7 +18,7 @@ Please see our https://github.com/spring-projects/.github/blob/main/CODE_OF_COND
See https://docs.spring.io/spring-security/reference/getting-spring-security.html[Getting Spring Security] for how to obtain Spring Security.
== Documentation
Be sure to read the https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/[Spring Security Reference].
Be sure to read the https://docs.spring.io/spring-security/reference/[Spring Security Reference].
Extensive JavaDoc for the Spring Security code is also available in the https://docs.spring.io/spring-security/site/docs/current/api/[Spring Security API Documentation].
== Quick Start
@@ -17,10 +17,13 @@
package org.springframework.security.acls.domain;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.model.SidRetrievalStrategy;
@@ -59,6 +62,8 @@ public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
private RoleHierarchy roleHierarchy = new NullRoleHierarchy();
/**
* Constructor. The only mandatory parameter relates to the system-wide
* {@link GrantedAuthority} instances that can be held to always permit ACL changes.
@@ -100,7 +105,9 @@ public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
}
// Iterate this principal's authorities to determine right
Set<String> authorities = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
Collection<? extends GrantedAuthority> reachableGrantedAuthorities = this.roleHierarchy
.getReachableGrantedAuthorities(authentication.getAuthorities());
Set<String> authorities = AuthorityUtils.authorityListToSet(reachableGrantedAuthorities);
if (acl.getOwner() instanceof GrantedAuthoritySid
&& authorities.contains(((GrantedAuthoritySid) acl.getOwner()).getGrantedAuthority())) {
return;
@@ -162,4 +169,14 @@ public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
this.securityContextHolderStrategy = securityContextHolderStrategy;
}
/**
* Sets the {@link RoleHierarchy} to use. The default is to use a
* {@link NullRoleHierarchy}
* @since 6.4
*/
public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
Assert.notNull(roleHierarchy, "roleHierarchy cannot be null");
this.roleHierarchy = roleHierarchy;
}
}
@@ -25,6 +25,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
@@ -34,6 +35,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.core.context.SecurityContextImpl;
import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify;
@@ -86,6 +88,15 @@ public class AclAuthorizationStrategyImplTests {
this.strategy.securityCheck(this.acl, AclAuthorizationStrategy.CHANGE_GENERAL);
}
@Test
public void securityCheckWhenRoleReachableByHierarchyThenAuthorized() {
given(this.acl.getOwner()).willReturn(new GrantedAuthoritySid("ROLE_AUTH_B"));
this.strategy = new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_SYSTEM_ADMIN"));
this.strategy.setRoleHierarchy(RoleHierarchyImpl.fromHierarchy("ROLE_AUTH > ROLE_AUTH_B"));
assertThatNoException()
.isThrownBy(() -> this.strategy.securityCheck(this.acl, AclAuthorizationStrategy.CHANGE_GENERAL));
}
@Test
public void securityCheckWhenCustomSecurityContextHolderStrategyThenUses() {
given(this.securityContextHolderStrategy.getContext()).willReturn(this.context);
+1 -1
View File
@@ -126,7 +126,7 @@ wrapperUpgrade {
gradle {
'spring-security' {
repo = 'spring-projects/spring-security'
baseBranch = '6.1.x' // runs only on 6.1.x and the update is merged forward to main
baseBranch = '6.2.x' // runs only on 6.2.x and the update is merged forward to main
}
}
}
@@ -56,6 +56,7 @@ import org.springframework.util.Assert;
*
* @author Ben Alex
* @author Scott Battaglia
* @author Kim Youngwoong
*/
public class CasAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
@@ -63,7 +64,7 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
private AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService;
private final UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
private UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
@@ -187,6 +188,17 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
this.authenticationUserDetailsService = authenticationUserDetailsService;
}
/**
* Sets the UserDetailsChecker to be used for checking the status of retrieved user
* details. This allows customization of the UserDetailsChecker implementation.
* @param userDetailsChecker the UserDetailsChecker to be set
* @since 6.4
*/
public void setUserDetailsChecker(final UserDetailsChecker userDetailsChecker) {
Assert.notNull(userDetailsChecker, "userDetailsChecker cannot be null");
this.userDetailsChecker = userDetailsChecker;
}
public void setServiceProperties(final ServiceProperties serviceProperties) {
this.serviceProperties = serviceProperties;
}
@@ -18,6 +18,7 @@ package org.springframework.security.cas.authentication;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.apereo.cas.client.validation.Assertion;
import org.apereo.cas.client.validation.AssertionImpl;
@@ -31,11 +32,13 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsChecker;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
@@ -55,6 +58,7 @@ import static org.mockito.Mockito.verify;
*
* @author Ben Alex
* @author Scott Battaglia
* @author Kim Youngwoong
*/
@SuppressWarnings("unchecked")
public class CasAuthenticationProviderTests {
@@ -320,6 +324,29 @@ public class CasAuthenticationProviderTests {
assertThat(cap.supports(CasAuthenticationToken.class)).isTrue();
}
@Test
public void testSetUserDetailsChecker() throws AuthenticationException {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
CasServiceTicketAuthenticationToken token = CasServiceTicketAuthenticationToken.stateful("ST-123");
AtomicInteger checkCount = new AtomicInteger(0);
UserDetailsChecker userDetailsChecker = new UserDetailsChecker() {
@Override
public void check(UserDetails user) {
checkCount.incrementAndGet();
}
};
cap.setUserDetailsChecker(userDetailsChecker);
cap.authenticate(token);
assertThat(checkCount.get()).isEqualTo(1);
}
private class MockAuthoritiesPopulator implements AuthenticationUserDetailsService {
@Override
+2 -2
View File
@@ -65,7 +65,7 @@ dependencies {
testImplementation 'jakarta.websocket:jakarta.websocket-api'
testImplementation 'jakarta.websocket:jakarta.websocket-client-api'
testImplementation 'ldapsdk:ldapsdk:4.1'
testImplementation('net.sourceforge.htmlunit:htmlunit') {
testImplementation('org.htmlunit:htmlunit') {
exclude group: 'commons-logging', module: 'commons-logging'
exclude group: 'xml-apis', module: 'xml-apis'
}
@@ -80,7 +80,7 @@ dependencies {
testImplementation "org.hibernate.orm:hibernate-core"
testImplementation 'org.hsqldb:hsqldb'
testImplementation 'org.mockito:mockito-core'
testImplementation('org.seleniumhq.selenium:htmlunit-driver') {
testImplementation('org.seleniumhq.selenium:htmlunit3-driver') {
exclude group: 'commons-logging', module: 'commons-logging'
exclude group: 'xml-apis', module: 'xml-apis'
}
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -96,7 +96,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
pc.getReaderContext()
.fatal("You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or "
+ "spring-security-3.1.xsd schema or spring-security-3.2.xsd schema or spring-security-4.0.xsd schema "
+ "with Spring Security 6.3. Please update your schema declarations to the 6.3 schema.",
+ "with Spring Security 6.4. Please update your schema declarations to the 6.4 schema.",
element);
}
String name = pc.getDelegate().getLocalName(element);
@@ -221,7 +221,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
private boolean matchesVersionInternal(Element element) {
String schemaLocation = element.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
return schemaLocation.matches("(?m).*spring-security-6\\.3.*.xsd.*")
return schemaLocation.matches("(?m).*spring-security-6\\.4.*.xsd.*")
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
|| !schemaLocation.matches("(?m).*spring-security.*");
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -63,6 +63,7 @@ import org.springframework.security.web.firewall.HttpStatusRequestRejectedHandle
import org.springframework.security.web.firewall.ObservationMarkingRequestRejectedHandler;
import org.springframework.security.web.firewall.RequestRejectedHandler;
import org.springframework.security.web.firewall.StrictHttpFirewall;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
import org.springframework.util.Assert;
@@ -296,8 +297,18 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
requestMatcherPrivilegeEvaluatorsEntries
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
}
boolean anyRequestConfigured = false;
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
Assert.isTrue(!anyRequestConfigured,
"A filter chain that matches any request has already been configured, which means that this filter chain ["
+ securityFilterChain
+ "] will never get invoked. Please use `HttpSecurity#securityMatcher` to ensure that there is only one filter chain configured for 'any request' and that the 'any request' filter chain is published last.");
if (securityFilterChain instanceof DefaultSecurityFilterChain defaultSecurityFilterChain) {
if (defaultSecurityFilterChain.getRequestMatcher() instanceof AnyRequestMatcher) {
anyRequestConfigured = true;
}
}
securityFilterChains.add(securityFilterChain);
requestMatcherPrivilegeEvaluatorsEntries
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -285,7 +285,9 @@ public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>>
}
ClientRegistrationRepository clientRegistrationRepository = OAuth2ClientConfigurerUtils
.getClientRegistrationRepository(getBuilder());
return new DefaultOAuth2AuthorizationRequestResolver(clientRegistrationRepository,
ResolvableType resolvableType = ResolvableType.forClass(OAuth2AuthorizationRequestResolver.class);
OAuth2AuthorizationRequestResolver bean = getBeanOrNull(resolvableType);
return (bean != null) ? bean : new DefaultOAuth2AuthorizationRequestResolver(clientRegistrationRepository,
OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI);
}
@@ -31,7 +31,6 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.converter.ClaimTypeConverter;
import org.springframework.security.oauth2.jwt.BadJwtException;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
@@ -81,8 +80,7 @@ final class OidcBackChannelLogoutAuthenticationProvider implements Authenticatio
.jwtProcessorCustomizer((processor) -> processor.setJWSTypeVerifier(typeVerifier))
.build();
decoder.setJwtValidator(jwtValidator.apply(clientRegistration));
decoder.setClaimSetConverter(
new ClaimTypeConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverters()));
decoder.setClaimSetConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverter());
return decoder;
};
}
@@ -16,9 +16,13 @@
package org.springframework.security.config.annotation.web.configurers.saml2;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.security.authentication.AuthenticationManager;
@@ -33,6 +37,7 @@ import org.springframework.security.saml2.provider.service.authentication.Abstra
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations;
import org.springframework.security.saml2.provider.service.web.HttpSessionSaml2AuthenticationRequestRepository;
import org.springframework.security.saml2.provider.service.web.OpenSamlAuthenticationTokenConverter;
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;
@@ -50,6 +55,7 @@ import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.ParameterRequestMatcher;
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatchers;
@@ -111,7 +117,13 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
private String loginPage;
private String authenticationRequestUri = Saml2AuthenticationRequestResolver.DEFAULT_AUTHENTICATION_REQUEST_URI;
private String authenticationRequestUri = "/saml2/authenticate";
private String[] authenticationRequestParams = { "registrationId={registrationId}" };
private RequestMatcher authenticationRequestMatcher = RequestMatchers.anyOf(
new AntPathRequestMatcher(Saml2AuthenticationRequestResolver.DEFAULT_AUTHENTICATION_REQUEST_URI),
new AntPathQueryRequestMatcher(this.authenticationRequestUri, this.authenticationRequestParams));
private Saml2AuthenticationRequestResolver authenticationRequestResolver;
@@ -196,11 +208,31 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
* Request
* @return the {@link Saml2LoginConfigurer} for further configuration
* @since 6.0
* @deprecated Use {@link #authenticationRequestUriQuery} instead
*/
public Saml2LoginConfigurer<B> authenticationRequestUri(String authenticationRequestUri) {
Assert.state(authenticationRequestUri.contains("{registrationId}"),
"authenticationRequestUri must contain {registrationId} path variable");
this.authenticationRequestUri = authenticationRequestUri;
return authenticationRequestUriQuery(authenticationRequestUri);
}
/**
* Customize the URL that the SAML Authentication Request will be sent to. This method
* also supports query parameters like so: <pre>
* authenticationRequestUriQuery("/saml/authenticate?registrationId={registrationId}")
* </pre> {@link RelyingPartyRegistrations}
* @param authenticationRequestUriQuery the URI and query to use for the SAML 2.0
* Authentication Request
* @return the {@link Saml2LoginConfigurer} for further configuration
* @since 6.0
*/
public Saml2LoginConfigurer<B> authenticationRequestUriQuery(String authenticationRequestUriQuery) {
Assert.state(authenticationRequestUriQuery.contains("{registrationId}"),
"authenticationRequestUri must contain {registrationId} path variable or query value");
String[] parts = authenticationRequestUriQuery.split("[?&]");
this.authenticationRequestUri = parts[0];
this.authenticationRequestParams = new String[parts.length - 1];
System.arraycopy(parts, 1, this.authenticationRequestParams, 0, parts.length - 1);
this.authenticationRequestMatcher = new AntPathQueryRequestMatcher(this.authenticationRequestUri,
this.authenticationRequestParams);
return this;
}
@@ -255,7 +287,7 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
}
else {
Map<String, String> providerUrlMap = getIdentityProviderUrlMap(this.authenticationRequestUri,
this.relyingPartyRegistrationRepository);
this.authenticationRequestParams, this.relyingPartyRegistrationRepository);
boolean singleProvider = providerUrlMap.size() == 1;
if (singleProvider) {
// Setup auto-redirect to provider login page
@@ -336,8 +368,7 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
}
OpenSaml4AuthenticationRequestResolver openSaml4AuthenticationRequestResolver = new OpenSaml4AuthenticationRequestResolver(
relyingPartyRegistrationRepository(http));
openSaml4AuthenticationRequestResolver
.setRequestMatcher(new AntPathRequestMatcher(this.authenticationRequestUri));
openSaml4AuthenticationRequestResolver.setRequestMatcher(this.authenticationRequestMatcher);
return openSaml4AuthenticationRequestResolver;
}
@@ -382,20 +413,28 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
return;
}
loginPageGeneratingFilter.setSaml2LoginEnabled(true);
loginPageGeneratingFilter.setSaml2AuthenticationUrlToProviderName(
this.getIdentityProviderUrlMap(this.authenticationRequestUri, this.relyingPartyRegistrationRepository));
loginPageGeneratingFilter
.setSaml2AuthenticationUrlToProviderName(this.getIdentityProviderUrlMap(this.authenticationRequestUri,
this.authenticationRequestParams, this.relyingPartyRegistrationRepository));
loginPageGeneratingFilter.setLoginPageUrl(this.getLoginPage());
loginPageGeneratingFilter.setFailureUrl(this.getFailureUrl());
}
@SuppressWarnings("unchecked")
private Map<String, String> getIdentityProviderUrlMap(String authRequestPrefixUrl,
private Map<String, String> getIdentityProviderUrlMap(String authRequestPrefixUrl, String[] authRequestQueryParams,
RelyingPartyRegistrationRepository idpRepo) {
Map<String, String> idps = new LinkedHashMap<>();
if (idpRepo instanceof Iterable) {
Iterable<RelyingPartyRegistration> repo = (Iterable<RelyingPartyRegistration>) idpRepo;
repo.forEach((p) -> idps.put(authRequestPrefixUrl.replace("{registrationId}", p.getRegistrationId()),
p.getRegistrationId()));
StringBuilder authRequestQuery = new StringBuilder("?");
for (String authRequestQueryParam : authRequestQueryParams) {
authRequestQuery.append(authRequestQueryParam + "&");
}
authRequestQuery.deleteCharAt(authRequestQuery.length() - 1);
String authenticationRequestUriQuery = authRequestPrefixUrl + authRequestQuery;
repo.forEach(
(p) -> idps.put(authenticationRequestUriQuery.replace("{registrationId}", p.getRegistrationId()),
p.getRegistrationId()));
}
return idps;
}
@@ -437,4 +476,35 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
}
}
static class AntPathQueryRequestMatcher implements RequestMatcher {
private final RequestMatcher matcher;
AntPathQueryRequestMatcher(String path, String... params) {
List<RequestMatcher> matchers = new ArrayList<>();
matchers.add(new AntPathRequestMatcher(path));
for (String param : params) {
String[] parts = param.split("=");
if (parts.length == 1) {
matchers.add(new ParameterRequestMatcher(parts[0]));
}
else {
matchers.add(new ParameterRequestMatcher(parts[0], parts[1]));
}
}
this.matcher = new AndRequestMatcher(matchers);
}
@Override
public boolean matches(HttpServletRequest request) {
return matcher(request).isMatch();
}
@Override
public MatchResult matcher(HttpServletRequest request) {
return this.matcher.matcher(request);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,8 +18,6 @@ package org.springframework.security.config.annotation.web.configurers.saml2;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import jakarta.servlet.http.HttpServletRequest;
@@ -60,6 +58,7 @@ import org.springframework.security.web.csrf.CsrfLogoutHandler;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.ParameterRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
/**
@@ -508,23 +507,6 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
}
private static class ParameterRequestMatcher implements RequestMatcher {
Predicate<String> test = Objects::nonNull;
String name;
ParameterRequestMatcher(String name) {
this.name = name;
}
@Override
public boolean matches(HttpServletRequest request) {
return this.test.test(request.getParameter(this.name));
}
}
private static class Saml2RelyingPartyInitiatedLogoutFilter extends LogoutFilter {
Saml2RelyingPartyInitiatedLogoutFilter(LogoutSuccessHandler logoutSuccessHandler, LogoutHandler... handlers) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,8 +18,6 @@ package org.springframework.security.config.http;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import jakarta.servlet.http.HttpServletRequest;
import org.w3c.dom.Element;
@@ -44,6 +42,7 @@ import org.springframework.security.web.authentication.logout.SecurityContextLog
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.ParameterRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
@@ -228,23 +227,6 @@ final class Saml2LogoutBeanDefinitionParser implements BeanDefinitionParser {
return this.logoutFilter;
}
private static class ParameterRequestMatcher implements RequestMatcher {
Predicate<String> test = Objects::nonNull;
String name;
ParameterRequestMatcher(String name) {
this.name = name;
}
@Override
public boolean matches(HttpServletRequest request) {
return this.test.test(request.getParameter(this.name));
}
}
public static class Saml2RequestMatcher implements RequestMatcher {
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
@@ -4532,9 +4532,12 @@ public class ServerHttpSecurity {
}
private OAuth2AuthorizationRequestRedirectWebFilter getRedirectWebFilter() {
OAuth2AuthorizationRequestRedirectWebFilter oauthRedirectFilter;
if (this.authorizationRequestResolver != null) {
return new OAuth2AuthorizationRequestRedirectWebFilter(this.authorizationRequestResolver);
ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver = this.authorizationRequestResolver;
if (authorizationRequestResolver == null) {
authorizationRequestResolver = getBeanOrNull(ServerOAuth2AuthorizationRequestResolver.class);
}
if (authorizationRequestResolver != null) {
return new OAuth2AuthorizationRequestRedirectWebFilter(authorizationRequestResolver);
}
return new OAuth2AuthorizationRequestRedirectWebFilter(getClientRegistrationRepository());
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,17 +16,21 @@
package org.springframework.security.config.annotation.web
import org.springframework.context.ApplicationContext
import org.springframework.http.HttpMethod
import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy
import org.springframework.security.access.hierarchicalroles.RoleHierarchy
import org.springframework.security.authorization.AuthenticatedAuthorizationManager
import org.springframework.security.authorization.AuthorityAuthorizationManager
import org.springframework.security.authorization.AuthorizationDecision
import org.springframework.security.authorization.AuthorizationManager
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer
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.access.IpAddressAuthorizationManager
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher
import org.springframework.security.web.util.matcher.AnyRequestMatcher
import org.springframework.security.web.util.matcher.RequestMatcher
@@ -41,10 +45,29 @@ import java.util.function.Supplier
* @since 5.7
* @property shouldFilterAllDispatcherTypes whether the [AuthorizationFilter] should filter all dispatcher types
*/
class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl() {
class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl {
@Deprecated("""
Add authorization rules to DispatcherType directly.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.dispatcherTypeMatchers(DispatcherType.ERROR).permitAll()
// ...
);
return http.build();
}
}
""")
var shouldFilterAllDispatcherTypes: Boolean? = null
private val authorizationRules = mutableListOf<AuthorizationManagerRule>()
private val rolePrefix: String
private val roleHierarchy: RoleHierarchy
private val HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector"
private val HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector"
@@ -190,7 +213,8 @@ class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl() {
* @return the [AuthorizationManager] with the provided authority
*/
fun hasAuthority(authority: String): AuthorizationManager<RequestAuthorizationContext> {
return AuthorityAuthorizationManager.hasAuthority(authority)
val manager = AuthorityAuthorizationManager.hasAuthority<RequestAuthorizationContext>(authority)
return withRoleHierarchy(manager)
}
/**
@@ -200,7 +224,8 @@ class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl() {
* @return the [AuthorizationManager] with the provided authorities
*/
fun hasAnyAuthority(vararg authorities: String): AuthorizationManager<RequestAuthorizationContext> {
return AuthorityAuthorizationManager.hasAnyAuthority(*authorities)
val manager = AuthorityAuthorizationManager.hasAnyAuthority<RequestAuthorizationContext>(*authorities)
return withRoleHierarchy(manager)
}
/**
@@ -210,7 +235,8 @@ class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl() {
* @return the [AuthorizationManager] with the provided role
*/
fun hasRole(role: String): AuthorizationManager<RequestAuthorizationContext> {
return AuthorityAuthorizationManager.hasRole(role)
val manager = AuthorityAuthorizationManager.hasAnyRole<RequestAuthorizationContext>(this.rolePrefix, arrayOf(role))
return withRoleHierarchy(manager)
}
/**
@@ -220,7 +246,8 @@ class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl() {
* @return the [AuthorizationManager] with the provided roles
*/
fun hasAnyRole(vararg roles: String): AuthorizationManager<RequestAuthorizationContext> {
return AuthorityAuthorizationManager.hasAnyRole(*roles)
val manager = AuthorityAuthorizationManager.hasAnyRole<RequestAuthorizationContext>(this.rolePrefix, arrayOf(*roles))
return withRoleHierarchy(manager)
}
/**
@@ -273,4 +300,37 @@ class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl() {
}
}
}
constructor() {
this.rolePrefix = "ROLE_"
this.roleHierarchy = NullRoleHierarchy()
}
constructor(context: ApplicationContext) {
val rolePrefix = resolveRolePrefix(context)
this.rolePrefix = rolePrefix
val roleHierarchy = resolveRoleHierarchy(context)
this.roleHierarchy = roleHierarchy
}
private fun resolveRolePrefix(context: ApplicationContext): String {
val beanNames = context.getBeanNamesForType(GrantedAuthorityDefaults::class.java)
if (beanNames.isNotEmpty()) {
return context.getBean(GrantedAuthorityDefaults::class.java).rolePrefix
}
return "ROLE_";
}
private fun resolveRoleHierarchy(context: ApplicationContext): RoleHierarchy {
val beanNames = context.getBeanNamesForType(RoleHierarchy::class.java)
if (beanNames.isNotEmpty()) {
return context.getBean(RoleHierarchy::class.java)
}
return NullRoleHierarchy()
}
private fun withRoleHierarchy(manager: AuthorityAuthorizationManager<RequestAuthorizationContext>): AuthorityAuthorizationManager<RequestAuthorizationContext> {
manager.setRoleHierarchy(this.roleHierarchy)
return manager
}
}
@@ -77,6 +77,7 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
private val HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector"
var authenticationManager: AuthenticationManager? = null
val context: ApplicationContext = http.getSharedObject(ApplicationContext::class.java)
/**
* Applies a [SecurityConfigurerAdapter] to this [HttpSecurity]
@@ -263,6 +264,7 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
* access for requests
* @see [AuthorizeRequestsDsl]
*/
@Deprecated(message = "Since 6.4. Use authorizeHttpRequests instead")
fun authorizeRequests(authorizeRequestsConfiguration: AuthorizeRequestsDsl.() -> Unit) {
val authorizeRequestsCustomizer = AuthorizeRequestsDsl().apply(authorizeRequestsConfiguration).get()
this.http.authorizeRequests(authorizeRequestsCustomizer)
@@ -297,7 +299,7 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
* @since 5.7
*/
fun authorizeHttpRequests(authorizeHttpRequestsConfiguration: AuthorizeHttpRequestsDsl.() -> Unit) {
val authorizeHttpRequestsCustomizer = AuthorizeHttpRequestsDsl().apply(authorizeHttpRequestsConfiguration).get()
val authorizeHttpRequestsCustomizer = AuthorizeHttpRequestsDsl(this.context).apply(authorizeHttpRequestsConfiguration).get()
this.http.authorizeHttpRequests(authorizeHttpRequestsCustomizer)
}
@@ -48,6 +48,7 @@ import org.springframework.security.web.authentication.AuthenticationSuccessHand
class Saml2Dsl {
var relyingPartyRegistrationRepository: RelyingPartyRegistrationRepository? = null
var loginPage: String? = null
var authenticationRequestUriQuery: String? = null
var authenticationSuccessHandler: AuthenticationSuccessHandler? = null
var authenticationFailureHandler: AuthenticationFailureHandler? = null
var failureUrl: String? = null
@@ -88,6 +89,9 @@ class Saml2Dsl {
defaultSuccessUrlOption?.also {
saml2Login.defaultSuccessUrl(defaultSuccessUrlOption!!.first, defaultSuccessUrlOption!!.second)
}
authenticationRequestUriQuery?.also {
saml2Login.authenticationRequestUriQuery(authenticationRequestUriQuery)
}
authenticationSuccessHandler?.also { saml2Login.successHandler(authenticationSuccessHandler) }
authenticationFailureHandler?.also { saml2Login.failureHandler(authenticationFailureHandler) }
authenticationManager?.also { saml2Login.authenticationManager(authenticationManager) }
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ package org.springframework.security.config.web.server
import org.springframework.security.authentication.ReactiveAuthenticationManager
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.web.server.ServerWebExchange
import org.springframework.web.server.WebFilter
@@ -65,6 +66,7 @@ operator fun ServerHttpSecurity.invoke(httpConfiguration: ServerHttpSecurityDsl.
class ServerHttpSecurityDsl(private val http: ServerHttpSecurity, private val init: ServerHttpSecurityDsl.() -> Unit) {
var authenticationManager: ReactiveAuthenticationManager? = null
var securityContextRepository: ServerSecurityContextRepository? = null
/**
* Allows configuring the [ServerHttpSecurity] to only be invoked when matching the
@@ -718,6 +720,7 @@ class ServerHttpSecurityDsl(private val http: ServerHttpSecurity, private val in
internal fun build(): SecurityWebFilterChain {
init()
authenticationManager?.also { this.http.authenticationManager(authenticationManager) }
securityContextRepository?.also { this.http.securityContextRepository(securityContextRepository) }
return this.http.build()
}
}
@@ -1,4 +1,21 @@
http\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-6.3.xsd
#
# Copyright 2002-2024 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# 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.
#
http\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-6.4.xsd
http\://www.springframework.org/schema/security/spring-security-6.4.xsd=org/springframework/security/config/spring-security-6.4.xsd
http\://www.springframework.org/schema/security/spring-security-6.3.xsd=org/springframework/security/config/spring-security-6.3.xsd
http\://www.springframework.org/schema/security/spring-security-6.2.xsd=org/springframework/security/config/spring-security-6.2.xsd
http\://www.springframework.org/schema/security/spring-security-6.1.xsd=org/springframework/security/config/spring-security-6.1.xsd
@@ -23,7 +40,8 @@ http\://www.springframework.org/schema/security/spring-security-2.0.xsd=org/spri
http\://www.springframework.org/schema/security/spring-security-2.0.1.xsd=org/springframework/security/config/spring-security-2.0.1.xsd
http\://www.springframework.org/schema/security/spring-security-2.0.2.xsd=org/springframework/security/config/spring-security-2.0.2.xsd
http\://www.springframework.org/schema/security/spring-security-2.0.4.xsd=org/springframework/security/config/spring-security-2.0.4.xsd
https\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-6.3.xsd
https\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-6.4.xsd
https\://www.springframework.org/schema/security/spring-security-6.4.xsd=org/springframework/security/config/spring-security-6.4.xsd
https\://www.springframework.org/schema/security/spring-security-6.3.xsd=org/springframework/security/config/spring-security-6.3.xsd
https\://www.springframework.org/schema/security/spring-security-6.2.xsd=org/springframework/security/config/spring-security-6.2.xsd
https\://www.springframework.org/schema/security/spring-security-6.1.xsd=org/springframework/security/config/spring-security-6.1.xsd
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -318,6 +318,14 @@ public class WebSecurityConfigurationTests {
assertThat(privilegeEvaluator.isAllowed("/ignoring1/child", null)).isTrue();
}
@Test
public void loadConfigWhenTwoSecurityFilterChainsPresentAndSecondWithAnyRequestThenException() {
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> this.spring.register(MultipleAnyRequestSecurityFilterChainConfig.class).autowire())
.havingRootCause()
.isExactlyInstanceOf(IllegalArgumentException.class);
}
private void assertAnotherUserPermission(WebInvocationPrivilegeEvaluator privilegeEvaluator) {
Authentication anotherUser = new TestingAuthenticationToken("anotherUser", "password", "ROLE_ANOTHER");
assertThat(privilegeEvaluator.isAllowed("/user", anotherUser)).isFalse();
@@ -819,4 +827,26 @@ public class WebSecurityConfigurationTests {
}
@Configuration
@EnableWebSecurity
@EnableWebMvc
@Import(AuthenticationTestConfiguration.class)
static class MultipleAnyRequestSecurityFilterChainConfig {
@Bean
@Order(0)
SecurityFilterChain api1(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((auth) -> auth.anyRequest().authenticated());
return http.build();
}
@Bean
@Order(1)
SecurityFilterChain api2(HttpSecurity http) throws Exception {
http.securityMatcher("/app/**").authorizeHttpRequests((auth) -> auth.anyRequest().authenticated());
return http.build();
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
package org.springframework.security.config.annotation.web.configurers;
import jakarta.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -52,6 +53,7 @@ public class NamespaceHttpFirewallTests {
MockMvc mvc;
@Test
@Disabled("MockMvc uses UriComponentsBuilder::fromUriString which was changed in https://github.com/spring-projects/spring-framework/issues/32513")
public void requestWhenPathContainsDoubleDotsThenBehaviorMatchesNamespace() throws Exception {
this.rule.register(HttpFirewallConfig.class).autowire();
this.mvc.perform(get("/public/../private/")).andExpect(status().isBadRequest());
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -285,6 +285,22 @@ public class OAuth2ClientConfigurerTests {
verify(authorizationRedirectStrategy).sendRedirect(any(), any(), anyString());
}
@Test
public void configureWhenCustomAuthorizationRequestResolverBeanPresentThenAuthorizationRequestResolverUsed()
throws Exception {
OAuth2AuthorizationRequestResolver defaultAuthorizationRequestResolver = authorizationRequestResolver;
authorizationRequestResolver = mock(OAuth2AuthorizationRequestResolver.class);
given(authorizationRequestResolver.resolve(any()))
.willAnswer((invocation) -> defaultAuthorizationRequestResolver.resolve(invocation.getArgument(0)));
this.spring.register(OAuth2ClientInLambdaConfig.class, AuthorizationRequestResolverConfig.class).autowire();
// @formatter:off
this.mockMvc.perform(get("/oauth2/authorization/registration-1"))
.andExpect(status().is3xxRedirection())
.andReturn();
// @formatter:on
verify(authorizationRequestResolver).resolve(any());
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
@@ -362,4 +378,14 @@ public class OAuth2ClientConfigurerTests {
}
@Configuration
static class AuthorizationRequestResolverConfig {
@Bean
OAuth2AuthorizationRequestResolver authorizationRequestResolver() {
return authorizationRequestResolver;
}
}
}
@@ -25,7 +25,6 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.gargoylesoftware.htmlunit.util.UrlUtils;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
@@ -41,6 +40,7 @@ import okhttp3.mockwebserver.Dispatcher;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.htmlunit.util.UrlUtils;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -101,6 +101,7 @@ import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.startsWith;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.atLeastOnce;
@@ -113,6 +114,7 @@ import static org.springframework.security.config.annotation.SecurityContextChan
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -343,6 +345,19 @@ public class Saml2LoginConfigurerTests {
any(HttpServletRequest.class), any(HttpServletResponse.class));
}
@Test
public void authenticationRequestWhenCustomAuthenticationRequestPathRepositoryThenUses() throws Exception {
this.spring.register(CustomAuthenticationRequestUriQuery.class).autowire();
MockHttpServletRequestBuilder request = get("/custom/auth/sso");
this.mvc.perform(request)
.andExpect(status().isFound())
.andExpect(redirectedUrl("http://localhost/custom/auth/sso?entityId=registration-id"));
request.queryParam("entityId", registration.getRegistrationId());
MvcResult result = this.mvc.perform(request).andExpect(status().isFound()).andReturn();
String redirectedUrl = result.getResponse().getRedirectedUrl();
assertThat(redirectedUrl).startsWith(registration.getAssertingPartyDetails().getSingleSignOnServiceLocation());
}
@Test
public void saml2LoginWhenLoginProcessingUrlWithoutRegistrationIdAndDefaultAuthenticationConverterThenAutowires()
throws Exception {
@@ -390,7 +405,7 @@ public class Saml2LoginConfigurerTests {
.andExpect(redirectedUrl("http://localhost/login"));
this.mvc.perform(get("/").accept(MediaType.TEXT_HTML))
.andExpect(status().isFound())
.andExpect(redirectedUrl("http://localhost/saml2/authenticate/registration-id"));
.andExpect(header().string("Location", startsWith("http://localhost/saml2/authenticate")));
}
@Test
@@ -669,6 +684,23 @@ public class Saml2LoginConfigurerTests {
}
@Configuration
@EnableWebSecurity
@Import(Saml2LoginConfigBeans.class)
static class CustomAuthenticationRequestUriQuery {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((authz) -> authz.anyRequest().authenticated())
.saml2Login((saml2) -> saml2.authenticationRequestUriQuery("/custom/auth/sso?entityId={registrationId}"));
// @formatter:on
return http.build();
}
}
@Configuration
@EnableWebSecurity
@Import(Saml2LoginConfigBeans.class)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -65,7 +65,7 @@ public class XsdDocumentedTests {
String schema31xDocumentLocation = "org/springframework/security/config/spring-security-3.1.xsd";
String schemaDocumentLocation = "org/springframework/security/config/spring-security-6.3.xsd";
String schemaDocumentLocation = "org/springframework/security/config/spring-security-6.4.xsd";
XmlSupport xml = new XmlSupport();
@@ -151,8 +151,8 @@ public class XsdDocumentedTests {
.list((dir, name) -> name.endsWith(".xsd"));
// @formatter:on
assertThat(schemas.length)
.withFailMessage("the count is equal to 25, if not then schemaDocument needs updating")
.isEqualTo(25);
.withFailMessage("the count is equal to 26, if not then schemaDocument needs updating")
.isEqualTo(26);
}
/**
@@ -18,13 +18,13 @@ package org.springframework.security.config.http;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpStatus;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
@@ -71,7 +71,7 @@ public class AccessDeniedConfigTests {
@WithMockUser
public void configureWhenAccessDeniedHandlerRefThenAutowire() throws Exception {
this.spring.configLocations(this.xml("AccessDeniedHandler")).autowire();
this.mvc.perform(get("/")).andExpect(status().is(HttpStatus.GONE_410));
this.mvc.perform(get("/")).andExpect(status().is(HttpStatus.GONE.value()));
}
@Test
@@ -90,7 +90,7 @@ public class AccessDeniedConfigTests {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) {
response.setStatus(HttpStatus.GONE_410);
response.setStatus(HttpStatus.GONE.value());
}
}
@@ -22,12 +22,12 @@ import java.util.List;
import jakarta.servlet.Filter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpStatus;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.access.AccessDeniedException;
@@ -566,7 +566,7 @@ public class CsrfConfigTests {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) {
response.setStatus(HttpStatus.IM_A_TEAPOT_418);
response.setStatus(HttpStatus.I_AM_A_TEAPOT.value());
}
}
@@ -64,6 +64,7 @@ import org.springframework.security.oauth2.client.registration.InMemoryReactiveC
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
import org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;
import org.springframework.security.oauth2.client.web.server.DefaultServerOAuth2AuthorizationRequestResolver;
import org.springframework.security.oauth2.client.web.server.ServerAuthorizationRequestRepository;
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizationRequestResolver;
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
@@ -457,6 +458,7 @@ public class OAuth2LoginTests {
OidcUser user = TestOidcUsers.create();
ReactiveOAuth2UserService<OidcUserRequest, OidcUser> userService = config.userService;
given(userService.loadUser(any())).willReturn(Mono.just(user));
ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver = config.authorizationRequestResolver;
// @formatter:off
webTestClient.get()
.uri("/login/oauth2/code/google")
@@ -466,6 +468,7 @@ public class OAuth2LoginTests {
verify(config.jwtDecoderFactory).createDecoder(any());
verify(tokenResponseClient).getTokenResponse(any());
verify(securityContextRepository).save(any(), any());
verify(authorizationRequestResolver).resolve(any());
}
// gh-5562
@@ -837,6 +840,10 @@ public class OAuth2LoginTests {
ServerSecurityContextRepository securityContextRepository = mock(ServerSecurityContextRepository.class);
ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver = spy(
new DefaultServerOAuth2AuthorizationRequestResolver(new InMemoryReactiveClientRegistrationRepository(
TestClientRegistrations.clientRegistration().build())));
@Bean
SecurityWebFilterChain springSecurityFilter(ServerHttpSecurity http) {
// @formatter:off
@@ -864,6 +871,11 @@ public class OAuth2LoginTests {
return this.jwtDecoderFactory;
}
@Bean
ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver() {
return this.authorizationRequestResolver;
}
@Bean
ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient() {
return this.tokenResponseClient;
@@ -26,7 +26,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.gargoylesoftware.htmlunit.util.UrlUtils;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
@@ -40,6 +39,7 @@ import okhttp3.mockwebserver.Dispatcher;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.htmlunit.util.UrlUtils;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import reactor.core.publisher.Mono;
@@ -24,10 +24,11 @@ import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import com.gargoylesoftware.htmlunit.FormEncodingType;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebRequest;
import com.gargoylesoftware.htmlunit.util.NameValuePair;
import org.htmlunit.FormEncodingType;
import org.htmlunit.WebClient;
import org.htmlunit.WebRequest;
import org.htmlunit.util.Cookie;
import org.htmlunit.util.NameValuePair;
import reactor.core.publisher.Mono;
import org.springframework.http.HttpMethod;
@@ -117,8 +118,8 @@ final class HtmlUnitWebTestClient {
request.cookie(cookieName, cookieValue);
}
}
Set<com.gargoylesoftware.htmlunit.util.Cookie> managedCookies = this.webClient.getCookies(webRequest.getUrl());
for (com.gargoylesoftware.htmlunit.util.Cookie cookie : managedCookies) {
Set<Cookie> managedCookies = this.webClient.getCookies(webRequest.getUrl());
for (Cookie cookie : managedCookies) {
request.cookie(cookie.getName(), cookie.getValue());
}
}
@@ -20,10 +20,10 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.gargoylesoftware.htmlunit.WebRequest;
import com.gargoylesoftware.htmlunit.WebResponse;
import com.gargoylesoftware.htmlunit.WebResponseData;
import com.gargoylesoftware.htmlunit.util.NameValuePair;
import org.htmlunit.WebRequest;
import org.htmlunit.WebResponse;
import org.htmlunit.WebResponseData;
import org.htmlunit.util.NameValuePair;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
@@ -16,8 +16,8 @@
package org.springframework.security.htmlunit.server;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebConnection;
import org.htmlunit.WebClient;
import org.htmlunit.WebConnection;
import org.openqa.selenium.WebDriver;
import org.springframework.test.web.reactive.server.WebTestClient;
@@ -18,10 +18,10 @@ package org.springframework.security.htmlunit.server;
import java.io.IOException;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebConnection;
import com.gargoylesoftware.htmlunit.WebRequest;
import com.gargoylesoftware.htmlunit.WebResponse;
import org.htmlunit.WebClient;
import org.htmlunit.WebConnection;
import org.htmlunit.WebRequest;
import org.htmlunit.WebResponse;
import org.springframework.lang.Nullable;
import org.springframework.test.web.reactive.server.FluxExchangeResult;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,8 @@
package org.springframework.security.config.annotation.web
import org.assertj.core.api.Assertions.*
import jakarta.servlet.DispatcherType
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.UnsatisfiedDependencyException
@@ -24,10 +25,13 @@ import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpMethod
import org.springframework.security.access.hierarchicalroles.RoleHierarchy
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl
import org.springframework.security.authorization.AuthorizationDecision
import org.springframework.security.authorization.AuthorizationManager
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.core.GrantedAuthorityDefaults
import org.springframework.security.config.test.SpringTestContext
import org.springframework.security.config.test.SpringTestContextExtension
import org.springframework.security.core.Authentication
@@ -55,7 +59,6 @@ import org.springframework.web.servlet.config.annotation.PathMatchConfigurer
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
import org.springframework.web.util.WebUtils
import java.util.function.Supplier
import jakarta.servlet.DispatcherType
/**
* Tests for [AuthorizeHttpRequestsDsl]
@@ -835,7 +838,6 @@ class AuthorizeHttpRequestsDslTests {
@EnableWebSecurity
@EnableWebMvc
open class HasIpAddressConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
@@ -853,4 +855,110 @@ class AuthorizeHttpRequestsDslTests {
}
}
}
@Test
fun `hasRole when prefixed by configured role prefix should fail to configure`() {
assertThatThrownBy { this.spring.register(RoleValidationConfig::class.java).autowire() }
.isInstanceOf(UnsatisfiedDependencyException::class.java)
.hasRootCauseInstanceOf(IllegalArgumentException::class.java)
.hasMessageContaining(
"ROLE_JUNIPER should not start with ROLE_ since ROLE_ is automatically prepended when using hasAnyRole. Consider using hasAnyAuthority instead."
)
assertThatThrownBy { this.spring.register(RoleValidationConfig::class.java, GrantedAuthorityDefaultsConfig::class.java).autowire() }
.isInstanceOf(UnsatisfiedDependencyException::class.java)
.hasRootCauseInstanceOf(IllegalArgumentException::class.java)
.hasMessageContaining(
"CUSTOM_JUNIPER should not start with CUSTOM_ since CUSTOM_ is automatically prepended when using hasAnyRole. Consider using hasAnyAuthority instead."
)
}
@Configuration
@EnableWebSecurity
@EnableWebMvc
open class RoleValidationConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize("/role", hasAnyRole("ROLE_JUNIPER"))
authorize("/custom", hasRole("CUSTOM_JUNIPER"))
}
}
return http.build()
}
}
@Configuration
open class GrantedAuthorityDefaultsConfig {
@Bean
open fun grantedAuthorityDefaults(): GrantedAuthorityDefaults {
return GrantedAuthorityDefaults("CUSTOM_")
}
}
@Test
fun `hasRole when role hierarchy configured then honor hierarchy`() {
this.spring.register(RoleHierarchyConfig::class.java).autowire()
this.mockMvc.get("/protected") {
with(httpBasic("admin", "password"))
}.andExpect {
status {
isOk()
}
}
this.mockMvc.get("/protected") {
with(httpBasic("user", "password"))
}.andExpect {
status {
isOk()
}
}
}
@Configuration
@EnableWebSecurity
@EnableWebMvc
open class RoleHierarchyConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize("/protected", hasRole("USER"))
}
httpBasic { }
}
return http.build()
}
@Bean
open fun roleHierarchy(): RoleHierarchy {
return RoleHierarchyImpl.fromHierarchy("ROLE_ADMIN > ROLE_USER")
}
@Bean
open fun userDetailsService(): UserDetailsService {
val user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build()
val admin = User.withDefaultPasswordEncoder()
.username("admin")
.password("password")
.roles("ADMIN")
.build()
return InMemoryUserDetailsManager(user, admin)
}
@RestController
internal class PathController {
@RequestMapping("/protected")
fun path() {
}
}
}
}
@@ -43,11 +43,13 @@ import org.springframework.security.saml2.provider.service.registration.TestRely
import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter
import org.springframework.security.web.SecurityFilterChain
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.MvcResult
import org.springframework.test.web.servlet.get
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
import java.security.cert.Certificate
import java.security.cert.CertificateFactory
import java.util.Base64
import java.util.*
/**
* Tests for [Saml2Dsl]
@@ -136,6 +138,23 @@ class Saml2DslTests {
verify(exactly = 1) { Saml2LoginCustomAuthenticationManagerConfig.AUTHENTICATION_MANAGER.authenticate(any()) }
}
@Test
@Throws(Exception::class)
fun authenticationRequestWhenCustomAuthenticationRequestPathRepositoryThenUses() {
this.spring.register(CustomAuthenticationRequestUriQuery::class.java).autowire()
val registration = TestRelyingPartyRegistrations.relyingPartyRegistration().build();
val request = MockMvcRequestBuilders.get("/custom/auth/sso")
this.mockMvc.perform(request)
.andExpect(MockMvcResultMatchers.status().isFound())
.andExpect(MockMvcResultMatchers.redirectedUrl("http://localhost/custom/auth/sso?entityId=simplesamlphp"))
request.queryParam("entityId", registration.registrationId)
val result: MvcResult =
this.mockMvc.perform(request).andExpect(MockMvcResultMatchers.status().isFound()).andReturn()
val redirectedUrl = result.response.redirectedUrl
Assertions.assertThat(redirectedUrl)
.startsWith(registration.assertingPartyDetails.singleSignOnServiceLocation)
}
@Configuration
@EnableWebSecurity
open class Saml2LoginCustomAuthenticationManagerConfig {
@@ -162,4 +181,26 @@ class Saml2DslTests {
return repository
}
}
@Configuration
@EnableWebSecurity
open class CustomAuthenticationRequestUriQuery {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize(anyRequest, authenticated)
}
saml2Login {
authenticationRequestUriQuery = "/custom/auth/sso?entityId={registrationId}"
}
}
return http.build()
}
@Bean
open fun relyingPartyRegistrationRepository(): RelyingPartyRegistrationRepository? {
return InMemoryRelyingPartyRegistrationRepository(TestRelyingPartyRegistrations.relyingPartyRegistration().build())
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,7 +35,9 @@ import org.springframework.security.config.test.SpringTestContextExtension
import org.springframework.security.core.Authentication
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter
import org.springframework.security.web.server.SecurityWebFilterChain
import org.springframework.security.web.server.context.NoOpServerSecurityContextRepository
import org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter
import org.springframework.security.web.server.context.ServerSecurityContextRepository
import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter
import org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter
import org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter
@@ -251,4 +253,31 @@ class ServerHttpSecurityDslTests {
return Mono.empty()
}
}
@Test
fun `security context repository when configured in DSL then used`() {
this.spring.register(SecurityContextRepositoryConfig::class.java).autowire()
mockkObject(SecurityContextRepositoryConfig.SECURITY_CONTEXT_REPOSITORY)
every {
SecurityContextRepositoryConfig.SECURITY_CONTEXT_REPOSITORY.load(any())
} returns Mono.empty()
this.client.get().uri("/").exchange()
verify(exactly = 1) { SecurityContextRepositoryConfig.SECURITY_CONTEXT_REPOSITORY.load(any()) }
}
@Configuration
@EnableWebFlux
@EnableWebFluxSecurity
open class SecurityContextRepositoryConfig {
companion object {
val SECURITY_CONTEXT_REPOSITORY: ServerSecurityContextRepository = NoOpServerSecurityContextRepository.getInstance()
}
@Bean
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
securityContextRepository = SECURITY_CONTEXT_REPOSITORY
}
}
}
}
@@ -1,12 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2002-2024 the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<b:beans xmlns="http://www.springframework.org/schema/security"
xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/security org/springframework/security/config/spring-security-6.3.xsd">
http://www.springframework.org/schema/security org/springframework/security/config/spring-security-6.4.xsd">
<tx:annotation-driven />
+14
View File
@@ -1,6 +1,8 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import java.util.concurrent.Callable
apply plugin: 'io.spring.convention.spring-module'
apply plugin: 'kotlin'
dependencies {
management platform(project(":spring-security-dependencies"))
@@ -31,6 +33,9 @@ dependencies {
testImplementation "org.springframework:spring-test"
testImplementation 'org.skyscreamer:jsonassert'
testImplementation 'org.springframework:spring-test'
testImplementation 'org.jetbrains.kotlin:kotlin-reflect'
testImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
testImplementation 'io.mockk:mockk'
testRuntimeOnly 'org.hsqldb:hsqldb'
}
@@ -57,3 +62,12 @@ Callable<String> springVersion() {
return (Callable<String>) { project.configurations.compileClasspath.resolvedConfiguration.resolvedArtifacts
.find { it.name == 'spring-core' }.moduleVersion.id.version }
}
tasks.withType(KotlinCompile).configureEach {
kotlinOptions {
languageVersion = "1.7"
apiVersion = "1.7"
freeCompilerArgs = ["-Xjsr305=strict", "-Xsuppress-version-warnings"]
jvmTarget = "17"
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -52,6 +52,7 @@ import org.springframework.util.Assert;
*
* @author Luke Taylor
* @author Evgeniy Cheban
* @author Blagoja Stamatovski
* @since 3.0
*/
public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpressionHandler<MethodInvocation>
@@ -109,12 +110,13 @@ public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpr
}
/**
* Filters the {@code filterTarget} object (which must be either a collection, array,
* map or stream), by evaluating the supplied expression.
* Filters the {@code filterTarget} object (which must be either a {@link Collection},
* {@code Array}, {@link Map} or {@link Stream}), by evaluating the supplied
* expression.
* <p>
* If a {@code Collection} or {@code Map} is used, the original instance will be
* modified to contain the elements for which the permission expression evaluates to
* {@code true}. For an array, a new array instance will be returned.
* Returns new instances of the same type as the supplied {@code filterTarget} object
* @return The filtered {@link Collection}, {@code Array}, {@link Map} or
* {@link Stream}
*/
@Override
public Object filter(Object filterTarget, Expression filterExpression, EvaluationContext ctx) {
@@ -151,9 +153,17 @@ public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpr
}
}
this.logger.debug(LogMessage.format("Retaining elements: %s", retain));
filterTarget.clear();
filterTarget.addAll(retain);
return filterTarget;
try {
filterTarget.clear();
filterTarget.addAll(retain);
return filterTarget;
}
catch (UnsupportedOperationException readonly) {
this.logger.trace(LogMessage.format(
"Collection threw exception: %s. Will return a new instance instead of mutating its state.",
readonly.getMessage()));
return retain;
}
}
private Object filterArray(Object[] filterTarget, Expression filterExpression, EvaluationContext ctx,
@@ -178,7 +188,7 @@ public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpr
return filtered;
}
private <K, V> Object filterMap(final Map<K, V> filterTarget, Expression filterExpression, EvaluationContext ctx,
private <K, V> Object filterMap(Map<K, V> filterTarget, Expression filterExpression, EvaluationContext ctx,
MethodSecurityExpressionOperations rootObject) {
Map<K, V> retain = new LinkedHashMap<>(filterTarget.size());
this.logger.debug(LogMessage.format("Filtering map with %s elements", filterTarget.size()));
@@ -189,9 +199,17 @@ public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpr
}
}
this.logger.debug(LogMessage.format("Retaining elements: %s", retain));
filterTarget.clear();
filterTarget.putAll(retain);
return filterTarget;
try {
filterTarget.clear();
filterTarget.putAll(retain);
return filterTarget;
}
catch (UnsupportedOperationException readonly) {
this.logger.trace(LogMessage.format(
"Map threw exception: %s. Will return a new instance instead of mutating its state.",
readonly.getMessage()));
return retain;
}
}
private Object filterStream(final Stream<?> filterTarget, Expression filterExpression, EvaluationContext ctx,
@@ -31,11 +31,11 @@ DigestAuthenticationFilter.nonceExpired=Nonce expirou/tempo esgotado
DigestAuthenticationFilter.nonceNotNumeric=Nonce token deveria ter um primero token num\u00E9rico, mas tem {0}
DigestAuthenticationFilter.nonceNotTwoTokens=Nonce token deveria ter dois tokens mas tem {0}
DigestAuthenticationFilter.usernameNotFound=Usu\u00E1rio {0} n\u00E3o encontrado
#ExceptionTranslationFilter.insufficientAuthentication=Full authentication is required to access this resource
ExceptionTranslationFilter.insufficientAuthentication=Autentica\u00e7\u00e3o completa \u00e9 necess\u00e1ria para acessar este recurso
JdbcDaoImpl.noAuthority=Usu\u00E1rio {0} n\u00E3o tem permiss\u00E3o
JdbcDaoImpl.notFound=Usu\u00E1rio {0} n\u00E3o encontrado
LdapAuthenticationProvider.badCredentials=Usu\u00E1rio inexistente ou senha inv\u00E1lida
#LdapAuthenticationProvider.badLdapConnection=Connection to LDAP server failed
LdapAuthenticationProvider.badLdapConnection=Conex\u00E3o com servidor LDAP falhou
LdapAuthenticationProvider.credentialsExpired=Credenciais expiradas
LdapAuthenticationProvider.disabled=Usu\u00E1rio desabilitado
LdapAuthenticationProvider.expired=Conta expirada
@@ -43,7 +43,7 @@ LdapAuthenticationProvider.locked=Conta bloqueada
LdapAuthenticationProvider.emptyUsername=Nome vazio n\u00E3o permitido
LdapAuthenticationProvider.onlySupports=Somente UsernamePasswordAuthenticationToken \u00E9 suportado
PasswordComparisonAuthenticator.badCredentials=Usu\u00E1rio inexistente ou senha inv\u00E1lida
#PersistentTokenBasedRememberMeServices.cookieStolen=Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack.
PersistentTokenBasedRememberMeServices.cookieStolen=Diverg\u00eancia inv\u00e1lida em remember-me token (Series/token). Implica um ataque pr\u00e9vio de roubo de cookies.
ProviderManager.providerNotFound=Nenhum AuthenticationProvider encontrado para {0}
RememberMeAuthenticationProvider.incorrectKey=O RememberMeAuthenticationToken apresentado n\u00E3o cont\u00E9m a chave esperada
RunAsImplAuthenticationProvider.incorrectKey=O RunAsUserToken apresentado n\u00E3o cont\u00E9m a chave esperada
@@ -0,0 +1,236 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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.access.expression.method
import io.mockk.every
import io.mockk.mockk
import org.aopalliance.intercept.MethodInvocation
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.springframework.expression.EvaluationContext
import org.springframework.expression.Expression
import org.springframework.security.core.Authentication
import java.util.stream.Stream
import kotlin.reflect.jvm.internal.impl.load.kotlin.JvmType
import kotlin.reflect.jvm.javaMethod
/**
* @author Blagoja Stamatovski
*/
class DefaultMethodSecurityExpressionHandlerKotlinTests {
private object Foo {
fun bar() {
}
}
private lateinit var authentication: Authentication
private lateinit var methodInvocation: MethodInvocation
private val handler: MethodSecurityExpressionHandler = DefaultMethodSecurityExpressionHandler()
@BeforeEach
fun setUp() {
authentication = mockk()
methodInvocation = mockk()
every { methodInvocation.`this` } returns { Foo }
every { methodInvocation.method } answers { Foo::bar.javaMethod!! }
every { methodInvocation.arguments } answers { arrayOf<JvmType.Object>() }
}
@Test
fun `filters non-empty maps`() {
val expression: Expression = handler.expressionParser.parseExpression("filterObject.key eq 'key2'")
val context: EvaluationContext = handler.createEvaluationContext(
/* authentication = */ authentication,
/* invocation = */ methodInvocation,
)
val nonEmptyMap: Map<String, String> = mapOf(
"key1" to "value1",
"key2" to "value2",
"key3" to "value3",
)
val filtered: Any = handler.filter(
/* filterTarget = */ nonEmptyMap,
/* filterExpression = */ expression,
/* ctx = */ context,
)
assertThat(filtered).isInstanceOf(Map::class.java)
val result = (filtered as Map<String, String>)
assertThat(result).hasSize(1)
assertThat(result).containsKey("key2")
assertThat(result).containsValue("value2")
}
@Test
fun `filters empty maps`() {
val expression: Expression = handler.expressionParser.parseExpression("filterObject.key eq 'key2'")
val context: EvaluationContext = handler.createEvaluationContext(
/* authentication = */ authentication,
/* invocation = */ methodInvocation,
)
val emptyMap: Map<String, String> = emptyMap()
val filtered: Any = handler.filter(
/* filterTarget = */ emptyMap,
/* filterExpression = */ expression,
/* ctx = */ context,
)
assertThat(filtered).isInstanceOf(Map::class.java)
val result = (filtered as Map<String, String>)
assertThat(result).hasSize(0)
}
@Test
fun `filters non-empty collections`() {
val expression: Expression = handler.expressionParser.parseExpression("filterObject eq 'string2'")
val context: EvaluationContext = handler.createEvaluationContext(
/* authentication = */ authentication,
/* invocation = */ methodInvocation,
)
val nonEmptyCollection: Collection<String> = listOf(
"string1",
"string2",
"string1",
)
val filtered: Any = handler.filter(
/* filterTarget = */ nonEmptyCollection,
/* filterExpression = */ expression,
/* ctx = */ context,
)
assertThat(filtered).isInstanceOf(Collection::class.java)
val result = (filtered as Collection<String>)
assertThat(result).hasSize(1)
assertThat(result).contains("string2")
}
@Test
fun `filters empty collections`() {
val expression: Expression = handler.expressionParser.parseExpression("filterObject eq 'string2'")
val context: EvaluationContext = handler.createEvaluationContext(
/* authentication = */ authentication,
/* invocation = */ methodInvocation,
)
val emptyCollection: Collection<String> = emptyList()
val filtered: Any = handler.filter(
/* filterTarget = */ emptyCollection,
/* filterExpression = */ expression,
/* ctx = */ context,
)
assertThat(filtered).isInstanceOf(Collection::class.java)
val result = (filtered as Collection<String>)
assertThat(result).hasSize(0)
}
@Test
fun `filters non-empty arrays`() {
val expression: Expression = handler.expressionParser.parseExpression("filterObject eq 'string2'")
val context: EvaluationContext = handler.createEvaluationContext(
/* authentication = */ authentication,
/* invocation = */ methodInvocation,
)
val nonEmptyArray: Array<String> = arrayOf(
"string1",
"string2",
"string1",
)
val filtered: Any = handler.filter(
/* filterTarget = */ nonEmptyArray,
/* filterExpression = */ expression,
/* ctx = */ context,
)
assertThat(filtered).isInstanceOf(Array<String>::class.java)
val result = (filtered as Array<String>)
assertThat(result).hasSize(1)
assertThat(result).contains("string2")
}
@Test
fun `filters empty arrays`() {
val expression: Expression = handler.expressionParser.parseExpression("filterObject eq 'string2'")
val context: EvaluationContext = handler.createEvaluationContext(
/* authentication = */ authentication,
/* invocation = */ methodInvocation,
)
val emptyArray: Array<String> = emptyArray()
val filtered: Any = handler.filter(
/* filterTarget = */ emptyArray,
/* filterExpression = */ expression,
/* ctx = */ context,
)
assertThat(filtered).isInstanceOf(Array<String>::class.java)
val result = (filtered as Array<String>)
assertThat(result).hasSize(0)
}
@Test
fun `filters non-empty streams`() {
val expression: Expression = handler.expressionParser.parseExpression("filterObject eq 'string2'")
val context: EvaluationContext = handler.createEvaluationContext(
/* authentication = */ authentication,
/* invocation = */ methodInvocation,
)
val nonEmptyStream: Stream<String> = listOf(
"string1",
"string2",
"string1",
).stream()
val filtered: Any = handler.filter(
/* filterTarget = */ nonEmptyStream,
/* filterExpression = */ expression,
/* ctx = */ context,
)
assertThat(filtered).isInstanceOf(Stream::class.java)
val result = (filtered as Stream<String>).toList()
assertThat(result).hasSize(1)
assertThat(result).contains("string2")
}
@Test
fun `filters empty streams`() {
val expression: Expression = handler.expressionParser.parseExpression("filterObject eq 'string2'")
val context: EvaluationContext = handler.createEvaluationContext(
/* authentication = */ authentication,
/* invocation = */ methodInvocation,
)
val emptyStream: Stream<String> = emptyList<String>().stream()
val filtered: Any = handler.filter(
/* filterTarget = */ emptyStream,
/* filterExpression = */ expression,
/* ctx = */ context,
)
assertThat(filtered).isInstanceOf(Stream::class.java)
val result = (filtered as Stream<String>).toList()
assertThat(result).hasSize(0)
}
}
+1
View File
@@ -47,6 +47,7 @@ dependencies {
api libs.jakarta.websocket.jakarta.websocket.client.api
api libs.ldapsdk
api libs.net.sourceforge.htmlunit
api libs.org.htmlunit.htmlunit
api libs.org.apache.directory.server.apacheds.entry
api libs.org.apache.directory.server.apacheds.core
api libs.org.apache.directory.server.apacheds.protocol.ldap
+1
View File
@@ -24,6 +24,7 @@ asciidoc:
extensions:
- '@asciidoctor/tabs'
- '@springio/asciidoctor-extensions'
- '@springio/asciidoctor-extensions/javadoc-extension'
urls:
latest_version_segment_strategy: redirect:to
latest_version_segment: ''
+1 -1
View File
@@ -6,7 +6,7 @@ nav:
ext:
collector:
run:
command: gradlew -q -PbuildSrc.skipTests=true "-Dorg.gradle.jvmargs=-Xmx3g -XX:+HeapDumpOnOutOfMemoryError" :spring-security-docs:generateAntoraYml
command: gradlew -q -PbuildSrc.skipTests=true :spring-security-docs:generateAntoraResources
local: true
scan:
dir: ./build/generated-antora-resources
+1
View File
@@ -8,6 +8,7 @@
* xref:migration/index.adoc[Migrating to 6.2]
** xref:migration/authorization.adoc[Authorization Changes]
* xref:getting-spring-security.adoc[Getting Spring Security]
* xref:attachment$api/java/index.html[Javadoc]
* xref:features/index.adoc[Features]
** xref:features/authentication/index.adoc[Authentication]
*** xref:features/authentication/password-storage.adoc[Password Storage]
@@ -598,7 +598,7 @@ With the above configuration, when a password manager navigates to `/.well-known
There are some scenarios where you need to check whether a password has been compromised, for example, if you are creating an application that deals with sensitive data, it is often needed that you perform some check on user's passwords in order to assert its reliability.
One of these checks can be if the password has been compromised, usually because it has been found in a https://wikipedia.org/wiki/Data_breach[data breach].
To facilitate that, Spring Security provides integration with the https://haveibeenpwned.com/API/v3#PwnedPasswords[Have I Been Pwned API] via the {security-api-url}org/springframework/security/core/password/HaveIBeenPwnedRestApiPasswordChecker.html[`HaveIBeenPwnedRestApiPasswordChecker` implementation] of the {security-api-url}org/springframework/security/core/password/CompromisedPasswordChecker.html[`CompromisedPasswordChecker` interface].
To facilitate that, Spring Security provides integration with the https://haveibeenpwned.com/API/v3#PwnedPasswords[Have I Been Pwned API] via the javadoc:org.springframework.security.web.authentication.password.HaveIBeenPwnedRestApiPasswordChecker[] implementation of the javadoc:org.springframework.security.authentication.password.CompromisedPasswordChecker[] interface.
You can either use the `CompromisedPasswordChecker` API by yourself or, if you are using xref:servlet/authentication/passwords/dao-authentication-provider.adoc[the `DaoAuthenticationProvider]` via xref:servlet/authentication/passwords/index.adoc[Spring Security authentication mechanisms], you can provide a `CompromisedPasswordChecker` bean, and it will be automatically picked up by Spring Security configuration.
@@ -208,11 +208,6 @@ The user receives an email at https://email.example.org that includes a link to
If the user clicks on the link, they would rightfully expect to be authenticated to the social media site.
However, if the `SameSite` attribute is `Strict`, the cookie would not be sent and so the user would not be authenticated.
[NOTE]
====
We could improve the protection and usability of `SameSite` protection against CSRF attacks by implementing https://github.com/spring-projects/spring-security/issues/7537[gh-7537].
====
Another obvious consideration is that, in order for the `SameSite` attribute to protect users, the browser must support the `SameSite` attribute.
Most modern browsers do https://developer.mozilla.org/en-US/docs/Web/HTTP/headers/Set-Cookie#Browser_compatibility[support the SameSite attribute].
However, older browsers that are still in use may not.
@@ -8,9 +8,9 @@ The code is distributed as part of the core module but has no dependencies on an
[[spring-security-crypto-encryption]]
== Encryptors
The {security-api-url}org/springframework/security/crypto/encrypt/Encryptors.html[`Encryptors`] class provides factory methods for constructing symmetric encryptors.
This class lets you create {security-api-url}org/springframework/security/crypto/encrypt/BytesEncryptor.html[`BytesEncryptor`] instances to encrypt data in raw `byte[]` form.
You can also construct {security-api-url}org/springframework/security/crypto/encrypt/TextEncryptor.html[TextEncryptor] instances to encrypt text strings.
The javadoc:org.springframework.security.crypto.encrypt.Encryptors[] class provides factory methods for constructing symmetric encryptors.
This class lets you create javadoc:org.springframework.security.crypto.encrypt.BytesEncryptor[] instances to encrypt data in raw `byte[]` form.
You can also construct javadoc:org.springframework.security.crypto.encrypt.TextEncryptor[] instances to encrypt text strings.
Encryptors are thread-safe.
[NOTE]
@@ -101,9 +101,9 @@ Encrypted results are returned as hex-encoded strings for easy storage on the fi
[[spring-security-crypto-keygenerators]]
== Key Generators
The {security-api-url}org/springframework/security/crypto/keygen/KeyGenerators.html[`KeyGenerators`] class provides a number of convenience factory methods for constructing different types of key generators.
By using this class, you can create a {security-api-url}org/springframework/security/crypto/keygen/BytesKeyGenerator.html[`BytesKeyGenerator`] to generate `byte[]` keys.
You can also construct a {security-api-url}org/springframework/security/crypto/keygen/StringKeyGenerator.html`[StringKeyGenerator]` to generate string keys.
The javadoc:org.springframework.security.crypto.keygen.KeyGenerators[] class provides a number of convenience factory methods for constructing different types of key generators.
By using this class, you can create a javadoc:org.springframework.security.crypto.keygen.BytesKeyGenerator[] to generate `byte[]` keys.
You can also construct a javadoc:org.springframework.security.crypto.keygen.StringKeyGenerator[] to generate string keys.
`KeyGenerators` is a thread-safe class.
=== BytesKeyGenerator
@@ -4,7 +4,7 @@
Similar to xref:servlet/authentication/session-management.adoc#ns-concurrent-sessions[Servlet's Concurrent Sessions Control], Spring Security also provides support to limit the number of concurrent sessions a user can have in a Reactive application.
When you set up Concurrent Sessions Control in Spring Security, it monitors authentications carried out through Form Login, xref:reactive/oauth2/login/index.adoc[OAuth 2.0 Login], and HTTP Basic authentication by hooking into the way those authentication mechanisms handle authentication success.
More specifically, the session management DSL will add the {security-api-url}org/springframework/security/web/server/authentication/ConcurrentSessionControlServerAuthenticationSuccessHandler.html[ConcurrentSessionControlServerAuthenticationSuccessHandler] and the {security-api-url}org/springframework/security/web/server/authentication/RegisterSessionServerAuthenticationSuccessHandler.html[RegisterSessionServerAuthenticationSuccessHandler] to the list of `ServerAuthenticationSuccessHandler` used by the authentication filter.
More specifically, the session management DSL will add the javadoc:org.springframework.security.web.server.authentication.ConcurrentSessionControlServerAuthenticationSuccessHandler[] and the javadoc:org.springframework.security.web.server.authentication.RegisterSessionServerAuthenticationSuccessHandler[] to the list of `ServerAuthenticationSuccessHandler` used by the authentication filter.
The following sections contains examples of how to configure Concurrent Sessions Control.
@@ -197,9 +197,9 @@ If you also need to invalidate the session against the Identity Provider you mus
[[concurrent-sessions-control-custom-strategy]]
== Handling Maximum Number of Sessions Exceeded
By default, when the maximum number of sessions is exceeded, the least recently used session(s) will be expired by using the {security-api-url}org/springframework/security/web/server/authentication/session/InvalidateLeastUsedMaximumSessionsExceededHandler.html[InvalidateLeastUsedMaximumSessionsExceededHandler].
Spring Security also provides another implementation that prevents the user from creating new sessions by using the {security-api-url}org/springframework/security/web/server/authentication/session/PreventLoginMaximumSessionsExceededHandler.html[PreventLoginMaximumSessionsExceededHandler].
If you want to use your own strategy, you can provide a different implementation of {security-api-url}org/springframework/security/web/server/authentication/session/ServerMaximumSessionsExceededHandler.html[ServerMaximumSessionsExceededHandler].
By default, when the maximum number of sessions is exceeded, the least recently used session(s) will be expired by using the javadoc:org.springframework.security.web.server.authentication.InvalidateLeastUsedServerMaximumSessionsExceededHandler[].
Spring Security also provides another implementation that prevents the user from creating new sessions by using the javadoc:org.springframework.security.web.server.authentication.PreventLoginServerMaximumSessionsExceededHandler[].
If you want to use your own strategy, you can provide a different implementation of javadoc:org.springframework.security.web.server.authentication.ServerMaximumSessionsExceededHandler[].
.Configuring maximumSessionsExceededHandler
[tabs]
@@ -254,9 +254,9 @@ open fun reactiveSessionRegistry(): ReactiveSessionRegistry {
[[reactive-concurrent-sessions-control-specify-session-registry]]
== Specifying a `ReactiveSessionRegistry`
In order to keep track of the user's sessions, Spring Security uses a {security-api-url}org/springframework/security/core/session/ReactiveSessionRegistry.html[ReactiveSessionRegistry], and, every time a user logs in, their session information is saved.
In order to keep track of the user's sessions, Spring Security uses a javadoc:org.springframework.security.core.session.ReactiveSessionRegistry[], and, every time a user logs in, their session information is saved.
Spring Security ships with {security-api-url}org/springframework/security/core/session/InMemoryReactiveSessionRegistry.html[InMemoryReactiveSessionRegistry] implementation of `ReactiveSessionRegistry`.
Spring Security ships with javadoc:org.springframework.security.core.session.InMemoryReactiveSessionRegistry[] implementation of `ReactiveSessionRegistry`.
To specify a `ReactiveSessionRegistry` implementation you can either declare it as a bean:
@@ -132,7 +132,7 @@ class HelloWebfluxSecurityConfig {
======
[NOTE]
Make sure to import the `org.springframework.security.config.annotation.web.invoke` function to enable the Kotlin DSL in your class, as the IDE will not always auto-import the method, causing compilation issues.
Make sure to import the `org.springframework.security.config.web.server.invoke` function to enable the Kotlin DSL in your class, as the IDE will not always auto-import the method, causing compilation issues.
This configuration explicitly sets up all the same things as our minimal configuration.
From here, you can more easily make changes to the defaults.
@@ -41,7 +41,7 @@ return http {
== Bearer Token Propagation
Now that you have a bearer token, you can pass that to downstream services.
This is possible with `{security-api-url}org/springframework/security/oauth2/server/resource/web/reactive/function/client/ServerBearerExchangeFilterFunction.html[ServerBearerExchangeFilterFunction]`:
This is possible with javadoc:org.springframework.security.oauth2.server.resource.web.reactive.function.client.ServerBearerExchangeFilterFunction[]:
[tabs]
======
@@ -70,7 +70,7 @@ fun rest(): WebClient {
----
======
When the `WebClient` shown in the preceding example performs requests, Spring Security looks up the current `Authentication` and extract any `{security-api-url}org/springframework/security/oauth2/core/AbstractOAuth2Token.html[AbstractOAuth2Token]` credential.
When the `WebClient` shown in the preceding example performs requests, Spring Security looks up the current `Authentication` and extract any javadoc:org.springframework.security.oauth2.core.AbstractOAuth2Token[] credential.
Then, it propagates that token in the `Authorization` header -- for example:
[tabs]
@@ -234,7 +234,7 @@ fun jwtDecoder(): ReactiveJwtDecoder {
[NOTE]
====
Calling `{security-api-url}org/springframework/security/oauth2/jwt/ReactiveJwtDecoders.html#fromIssuerLocation-java.lang.String-[ReactiveJwtDecoders#fromIssuerLocation]` invokes the Provider Configuration or Authorization Server Metadata endpoint to derive the JWK Set URI.
Calling javadoc:org.springframework.security.oauth2.jwt.ReactiveJwtDecoders#fromIssuerLocation-java.lang.String-[ReactiveJwtDecoders#fromIssuerLocation] invokes the Provider Configuration or Authorization Server Metadata endpoint to derive the JWK Set URI.
If the application does not expose a `ReactiveJwtDecoder` bean, Spring Boot exposes the above default one.
====
@@ -68,7 +68,7 @@ Given an Opaque Token, Resource Server:
. Inspects the response for an `{ 'active' : true }` attribute.
. Maps each scope to an authority with a prefix of `SCOPE_`.
By default, the resulting `Authentication#getPrincipal` is a Spring Security `{security-api-url}org/springframework/security/oauth2/core/OAuth2AuthenticatedPrincipal.html[OAuth2AuthenticatedPrincipal]` object, and `Authentication#getName` maps to the token's `sub` property, if one is present.
By default, the resulting `Authentication#getPrincipal` is a Spring Security javadoc:org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal[] object, and `Authentication#getName` maps to the token's `sub` property, if one is present.
From here, you may want to jump to:
@@ -956,7 +956,7 @@ client
----
======
You can also specify a complete `Jwt`, for which `{security-api-url}org/springframework/security/oauth2/jwt/Jwt.Builder.html[Jwt.Builder]` is quite handy:
You can also specify a complete `Jwt`, for which javadoc:org.springframework.security.oauth2.jwt.Jwt$Builder[] is quite handy:
[tabs]
======
@@ -6,4 +6,4 @@ This appendix provides a reference to the elements available in the security nam
If you haven't used the namespace before, please read the xref:servlet/configuration/xml-namespace.adoc#ns-config[introductory chapter] on namespace configuration, as this is intended as a supplement to the information there.
Using a good quality XML editor while editing a configuration based on the schema is recommended as this will provide contextual information on which elements and attributes are available as well as comments explaining their purpose.
The namespace is written in https://relaxng.org/[RELAX NG] Compact format and later converted into an XSD schema.
If you are familiar with this format, you may wish to examine the https://raw.githubusercontent.com/spring-projects/spring-security/main/config/src/main/resources/org/springframework/security/config/spring-security-6.3.rnc[schema file] directly.
If you are familiar with this format, you may wish to examine the https://raw.githubusercontent.com/spring-projects/spring-security/main/config/src/main/resources/org/springframework/security/config/spring-security-6.4.rnc[schema file] directly.
@@ -118,7 +118,7 @@ image::{figures}/filterchainproxy.png[]
[[servlet-securityfilterchain]]
== SecurityFilterChain
{security-api-url}org/springframework/security/web/SecurityFilterChain.html[`SecurityFilterChain`] is used by <<servlet-filterchainproxy>> to determine which Spring Security `Filter` instances should be invoked for the current request.
javadoc:org.springframework.security.web.SecurityFilterChain[] is used by <<servlet-filterchainproxy>> to determine which Spring Security `Filter` instances should be invoked for the current request.
The following image shows the role of `SecurityFilterChain`.
@@ -250,11 +250,11 @@ If you want to see the list of filters invoked for a particular request, you can
Often times, it is useful to see the list of security ``Filter``s that are invoked for a particular request.
For example, you want to make sure that the <<adding-custom-filter,filter you have added>> is in the list of the security filters.
The list of filters is printed at INFO level on the application startup, so you can see something like the following on the console output for example:
The list of filters is printed at DEBUG level on the application startup, so you can see something like the following on the console output for example:
[source,text,role="terminal"]
----
2023-06-14T08:55:22.321-03:00 INFO 76975 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [
2023-06-14T08:55:22.321-03:00 DEBUG 76975 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [
org.springframework.security.web.session.DisableEncodeUrlFilter@404db674,
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@50f097b5,
org.springframework.security.web.context.SecurityContextHolderFilter@6fc6deb7,
@@ -392,7 +392,7 @@ public FilterRegistrationBean<TenantFilter> tenantFilterRegistration(TenantFilte
== Handling Security Exceptions
The {security-api-url}org/springframework/security/web/access/ExceptionTranslationFilter.html[`ExceptionTranslationFilter`] allows translation of {security-api-url}org/springframework/security/access/AccessDeniedException.html[`AccessDeniedException`] and {security-api-url}/org/springframework/security/core/AuthenticationException.html[`AuthenticationException`] into HTTP responses.
The javadoc:org.springframework.security.web.access.ExceptionTranslationFilter[] allows translation of javadoc:org.springframework.security.access.AccessDeniedException[] and javadoc:org.springframework.security.core.AuthenticationException[] into HTTP responses.
`ExceptionTranslationFilter` is inserted into the <<servlet-filterchainproxy>> as one of the <<servlet-security-filters>>.
@@ -447,7 +447,7 @@ In Spring Security this is done by saving the `HttpServletRequest` using a <<req
[[requestcache]]
=== RequestCache
The `HttpServletRequest` is saved in the {security-api-url}org/springframework/security/web/savedrequest/RequestCache.html[`RequestCache`].
The `HttpServletRequest` is saved in the javadoc:org.springframework.security.web.savedrequest.RequestCache[].
When the user successfully authenticates, the `RequestCache` is used to replay the original request.
The <<requestcacheawarefilter,`RequestCacheAwareFilter`>> uses the `RequestCache` to get the saved `HttpServletRequest` after the user authenticates, while the `ExceptionTranslationFilter` uses the `RequestCache` to save the `HttpServletRequest` after it detects `AuthenticationException`, before redirecting the user to the login endpoint.
@@ -463,7 +463,7 @@ There are a number of reasons you may want to not store the user's unauthenticat
You may want to offload that storage onto the user's browser or store it in a database.
Or you may want to shut off this feature since you always want to redirect the user to the home page instead of the page they tried to visit before login.
To do that, you can use {security-api-url}org/springframework/security/web/savedrequest/NullRequestCache.html[the `NullRequestCache` implementation].
To do that, you can use the javadoc:org.springframework.security.web.savedrequest.NullRequestCache[NullRequestCache] implementation.
.Prevent the Request From Being Saved
[tabs]
@@ -517,7 +517,7 @@ XML::
[[requestcacheawarefilter]]
=== RequestCacheAwareFilter
The {security-api-url}org/springframework/security/web/savedrequest/RequestCacheAwareFilter.html[`RequestCacheAwareFilter`] uses the <<requestcache,`RequestCache`>> to replay the original request.
The javadoc:org.springframework.security.web.savedrequest.RequestCacheAwareFilter[] uses the <<requestcache,`RequestCache`>> to replay the original request.
[[servlet-logging]]
== Logging
@@ -117,13 +117,13 @@ However, if you do, take a look at the JavaDoc for `SecurityContextHolder` to le
[[servlet-authentication-securitycontext]]
== SecurityContext
The {security-api-url}org/springframework/security/core/context/SecurityContext.html[`SecurityContext`] is obtained from the <<servlet-authentication-securitycontextholder>>.
The javadoc:org.springframework.security.core.context.SecurityContext[] is obtained from the <<servlet-authentication-securitycontextholder>>.
The `SecurityContext` contains an <<servlet-authentication-authentication>> object.
[[servlet-authentication-authentication]]
== Authentication
The {security-api-url}org/springframework/security/core/Authentication.html[`Authentication`] interface serves two main purposes within Spring Security:
The javadoc:org.springframework.security.core.Authentication[] interface serves two main purposes within Spring Security:
* An input to <<servlet-authentication-authenticationmanager,`AuthenticationManager`>> to provide the credentials a user has provided to authenticate.
When used in this scenario, `isAuthenticated()` returns `false`.
@@ -141,7 +141,7 @@ Two examples are roles and scopes.
[[servlet-authentication-granted-authority]]
== GrantedAuthority
{security-api-url}org/springframework/security/core/GrantedAuthority.html[`GrantedAuthority`] instances are high-level permissions that the user is granted.
javadoc:org.springframework.security.core.GrantedAuthority[] instances are high-level permissions that the user is granted.
Two examples are roles and scopes.
You can obtain `GrantedAuthority` instances from the <<servlet-authentication-authentication,`Authentication.getAuthorities()`>> method.
@@ -160,7 +160,7 @@ Of course, Spring Security is expressly designed to handle this common requireme
[[servlet-authentication-authenticationmanager]]
== AuthenticationManager
{security-api-url}org/springframework/security/authentication/AuthenticationManager.html[`AuthenticationManager`] is the API that defines how Spring Security's Filters perform xref:features/authentication/index.adoc#authentication[authentication].
javadoc:org.springframework.security.authentication.AuthenticationManager[] is the API that defines how Spring Security's Filters perform xref:features/authentication/index.adoc#authentication[authentication].
The <<servlet-authentication-authentication,`Authentication`>> that is returned is then set on the <<servlet-authentication-securitycontextholder>> by the controller (that is, by xref:servlet/architecture.adoc#servlet-security-filters[Spring Security's `Filters` instances]) that invoked the `AuthenticationManager`.
If you are not integrating with Spring Security's `Filters` instances, you can set the `SecurityContextHolder` directly and are not required to use an `AuthenticationManager`.
@@ -170,7 +170,7 @@ While the implementation of `AuthenticationManager` could be anything, the most
[[servlet-authentication-providermanager]]
== ProviderManager
{security-api-url}org/springframework/security/authentication/ProviderManager.html[`ProviderManager`] is the most commonly used implementation of <<servlet-authentication-authenticationmanager,`AuthenticationManager`>>.
javadoc:org.springframework.security.authentication.ProviderManager[] is the most commonly used implementation of <<servlet-authentication-authenticationmanager,`AuthenticationManager`>>.
`ProviderManager` delegates to a `List` of <<servlet-authentication-authenticationprovider,`AuthenticationProvider`>> instances.
Each `AuthenticationProvider` has an opportunity to indicate that authentication should be successful, fail, or indicate it cannot make a decision and allow a downstream `AuthenticationProvider` to decide.
If none of the configured `AuthenticationProvider` instances can authenticate, authentication fails with a `ProviderNotFoundException`, which is a special `AuthenticationException` that indicates that the `ProviderManager` was not configured to support the type of `Authentication` that was passed into it.
@@ -200,19 +200,19 @@ If the `Authentication` contains a reference to an object in the cache (such as
You need to take this into account if you use a cache.
An obvious solution is to first make a copy of the object, either in the cache implementation or in the `AuthenticationProvider` that creates the returned `Authentication` object.
Alternatively, you can disable the `eraseCredentialsAfterAuthentication` property on `ProviderManager`.
See the Javadoc for the {security-api-url}org/springframework/security/authentication/ProviderManager.html[ProviderManager] class.
See the Javadoc for the javadoc:org.springframework.security.authentication.ProviderManager[] class.
[[servlet-authentication-authenticationprovider]]
== AuthenticationProvider
You can inject multiple {security-api-url}org/springframework/security/authentication/AuthenticationProvider.html[``AuthenticationProvider``s] instances into <<servlet-authentication-providermanager,`ProviderManager`>>.
You can inject multiple javadoc:org.springframework.security.authentication.AuthenticationProvider[] instances into <<servlet-authentication-providermanager,`ProviderManager`>>.
Each `AuthenticationProvider` performs a specific type of authentication.
For example, xref:servlet/authentication/passwords/dao-authentication-provider.adoc#servlet-authentication-daoauthenticationprovider[`DaoAuthenticationProvider`] supports username/password-based authentication, while `JwtAuthenticationProvider` supports authenticating a JWT token.
[[servlet-authentication-authenticationentrypoint]]
== Request Credentials with `AuthenticationEntryPoint`
{security-api-url}org/springframework/security/web/AuthenticationEntryPoint.html[`AuthenticationEntryPoint`] is used to send an HTTP response that requests credentials from a client.
javadoc:org.springframework.security.web.AuthenticationEntryPoint[] is used to send an HTTP response that requests credentials from a client.
Sometimes, a client proactively includes credentials (such as a username and password) to request a resource.
In these cases, Spring Security does not need to provide an HTTP response that requests credentials from the client, since they are already included.
@@ -229,7 +229,7 @@ The `AuthenticationEntryPoint` implementation might perform a xref:servlet/authe
[[servlet-authentication-abstractprocessingfilter]]
== AbstractAuthenticationProcessingFilter
{security-api-url}org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.html[`AbstractAuthenticationProcessingFilter`] is used as a base `Filter` for authenticating a user's credentials.
javadoc:org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter[] is used as a base `Filter` for authenticating a user's credentials.
Before the credentials can be authenticated, Spring Security typically requests the credentials by using <<servlet-authentication-authenticationentrypoint,`AuthenticationEntryPoint`>>.
Next, the `AbstractAuthenticationProcessingFilter` can authenticate any authentication requests that are submitted to it.
@@ -245,26 +245,26 @@ image:{icondir}/number_2.png[] Next, the <<servlet-authentication-authentication
image:{icondir}/number_3.png[] If authentication fails, then __Failure__.
* The <<servlet-authentication-securitycontextholder>> is cleared out.
* `RememberMeServices.loginFail` is invoked.
* `RememberMeServices.loginFail` is invoked.ƒ
If remember me is not configured, this is a no-op.
See the {security-api-url}org/springframework/security/web/authentication/rememberme/package-frame.html[`rememberme`] package.
See the javadoc:org.springframework.security.web.authentication.rememberme.package-summary[rememberme] package.
* `AuthenticationFailureHandler` is invoked.
See the {security-api-url}org/springframework/security/web/authentication/AuthenticationFailureHandler.html[`AuthenticationFailureHandler`] interface.
See the javadoc:org.springframework.security.web.authentication.AuthenticationFailureHandler[] interface.
image:{icondir}/number_4.png[] If authentication is successful, then __Success__.
* `SessionAuthenticationStrategy` is notified of a new login.
See the {security-api-url}org/springframework/security/web/authentication/session/SessionAuthenticationStrategy.html[`SessionAuthenticationStrategy`] interface.
See the javadoc:org.springframework.security.web.authentication.session.SessionAuthenticationStrategy[] interface.
* The <<servlet-authentication-authentication>> is set on the <<servlet-authentication-securitycontextholder>>.
Later, if you need to save the `SecurityContext` so that it can be automatically set on future requests, `SecurityContextRepository#saveContext` must be explicitly invoked.
See the {security-api-url}org/springframework/security/web/context/SecurityContextHolderFilter.html[`SecurityContextHolderFilter`] class.
See the javadoc:org.springframework.security.web.context.SecurityContextHolderFilter[] class.
* `RememberMeServices.loginSuccess` is invoked.
If remember me is not configured, this is a no-op.
See the {security-api-url}org/springframework/security/web/authentication/rememberme/package-frame.html[`rememberme`] package.
See the javadoc:org.springframework.security.web.authentication.rememberme.package-summary[rememberme] package.
* `ApplicationEventPublisher` publishes an `InteractiveAuthenticationSuccessEvent`.
* `AuthenticationSuccessHandler` is invoked.
See the {security-api-url}org/springframework/security/web/authentication/AuthenticationSuccessHandler.html[`AuthenticationSuccessHandler`] interface.
See the javadoc:org.springframework.security.web.authentication.AuthenticationSuccessHandler[] interface.
// daoauthenticationprovider (goes in username/password)
@@ -143,10 +143,10 @@ Java::
@Bean
public AuthenticationEventPublisher authenticationEventPublisher
(ApplicationEventPublisher applicationEventPublisher) {
AuthenticationEventPublisher authenticationEventPublisher =
DefaultAuthenticationEventPublisher authenticationEventPublisher =
new DefaultAuthenticationEventPublisher(applicationEventPublisher);
authenticationEventPublisher.setDefaultAuthenticationFailureEvent
(GenericAuthenticationFailureEvent.class);
(AbstractAuthenticationFailureEvent.class);
return authenticationEventPublisher;
}
----
@@ -159,7 +159,7 @@ Kotlin::
fun authenticationEventPublisher
(applicationEventPublisher: ApplicationEventPublisher?): AuthenticationEventPublisher {
val authenticationEventPublisher = DefaultAuthenticationEventPublisher(applicationEventPublisher)
authenticationEventPublisher.setDefaultAuthenticationFailureEvent(GenericAuthenticationFailureEvent::class.java)
authenticationEventPublisher.setDefaultAuthenticationFailureEvent(AbstractAuthenticationFailureEvent::class.java)
return authenticationEventPublisher
}
----
@@ -58,7 +58,7 @@ This means that `DefaultJaasAuthenticationProvider` is not bound to any particul
To make it easy to inject a `Configuration` into `DefaultJaasAuthenticationProvider`, a default in-memory implementation named `InMemoryConfiguration` is provided.
The implementation constructor accepts a `Map` where each key represents a login configuration name, and the value represents an `Array` of `AppConfigurationEntry` instances.
`InMemoryConfiguration` also supports a default `Array` of `AppConfigurationEntry` objects that is used if no mapping is found within the provided `Map`.
For details, see the {security-api-url}org/springframework/security/authentication/jaas/memory/InMemoryConfiguration.html[Javadoc of `InMemoryConfiguration`].
For details, see the Javadoc of javadoc:org.springframework.security.authentication.jaas.memory.InMemoryConfiguration[].
[[jaas-djap-config]]
@@ -30,16 +30,16 @@ Please note that if xref:servlet/exploits/csrf.adoc[CSRF protection] is disabled
In your application it is not necessary to use `GET /logout` to perform a logout.
So long as xref:servlet/exploits/csrf.adoc[the needed CSRF token] is present in the request, your application can simply `POST /logout` to induce a logout.
If you request `POST /logout`, then it will perform the following default operations using a series of {security-api-url}org/springframework/security/web/authentication/logout/LogoutHandler.html[``LogoutHandler``]s:
If you request `POST /logout`, then it will perform the following default operations using a series of javadoc:org.springframework.security.web.authentication.logout.LogoutHandler[] instances:
- Invalidate the HTTP session ({security-api-url}org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html[`SecurityContextLogoutHandler`])
- Clear the xref:servlet/authentication/session-management.adoc#use-securitycontextholderstrategy[`SecurityContextHolderStrategy`] ({security-api-url}org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html[`SecurityContextLogoutHandler`])
- Clear the xref:servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`] ({security-api-url}org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html[`SecurityContextLogoutHandler`])
- Invalidate the HTTP session (javadoc:org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler[])
- Clear the xref:servlet/authentication/session-management.adoc#use-securitycontextholderstrategy[`SecurityContextHolderStrategy`] (javadoc:org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler[])
- Clear the xref:servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`] (javadoc:org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler[])
- Clean up any xref:servlet/authentication/rememberme.adoc[RememberMe authentication] (`TokenRememberMeServices` / `PersistentTokenRememberMeServices`)
- Clear out any saved xref:servlet/exploits/csrf.adoc[CSRF token] ({security-api-url}org/springframework/security/web/csrf/CsrfLogoutHandler.html[`CsrfLogoutHandler`])
- xref:servlet/authentication/events.adoc[Fire] a `LogoutSuccessEvent` ({security-api-url}org/springframework/security/web/authentication/logout/LogoutSuccessEventPublishingLogoutHandler.html[`LogoutSuccessEventPublishingLogoutHandler`])
- Clear out any saved xref:servlet/exploits/csrf.adoc[CSRF token] (javadoc:org.springframework.security.web.csrf.CsrfLogoutHandler[])
- xref:servlet/authentication/events.adoc[Fire] a `LogoutSuccessEvent` (javadoc:org.springframework.security.web.authentication.logout.LogoutSuccessEventPublishingLogoutHandler[])
Once completed, then it will exercise its default {security-api-url}org/springframework/security/web/authentication/logout/LogoutSuccessHandler.html[`LogoutSuccessHandler`] which redirects to `/login?logout`.
Once completed, then it will exercise its default javadoc:org.springframework.security.web.authentication.logout.LogoutSuccessHandler[] which redirects to `/login?logout`.
[[customizing-logout-uris]]
== Customizing Logout URIs
@@ -197,15 +197,15 @@ http {
======
[NOTE]
Because {security-api-url}org/springframework/security/web/authentication/logout/LogoutHandler.html[``LogoutHandler``]s are for the purposes of cleanup, they should not throw exceptions.
Because javadoc:org.springframework.security.web.authentication.logout.LogoutHandler[] instances are for the purposes of cleanup, they should not throw exceptions.
[TIP]
Since {security-api-url}org/springframework/security/web/authentication/logout/LogoutHandler.html[`LogoutHandler`] is a functional interface, you can provide a custom one as a lambda.
Since javadoc:org.springframework.security.web.authentication.logout.LogoutHandler[] is a functional interface, you can provide a custom one as a lambda.
Some logout handler configurations are common enough that they are exposed directly in the `logout` DSL and `<logout>` element.
One example is configuring session invalidation and another is which additional cookies should be deleted.
For example, you can configure the {security-api-url}org/springframework/security/web/authentication/logout/CookieClearingLogoutHandler.html[`CookieClearingLogoutHandler`] as seen above.
For example, you can configure the javadoc:org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler[] as seen above.
[[delete-cookies]]
Or you can instead set the appropriate configuration value like so:
@@ -242,7 +242,7 @@ Xml::
======
[NOTE]
Specifying that the `JSESSIONID` cookie is not necessary since {security-api-url}/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html[`SecurityContextLogoutHandler`] removes it by virtue of invalidating the session.
Specifying that the `JSESSIONID` cookie is not necessary since javadoc:org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler[] removes it by virtue of invalidating the session.
[[clear-all-site-data]]
=== Using Clear-Site-Data to Log Out the User
@@ -310,7 +310,7 @@ http {
== Customizing Logout Success
While using `logoutSuccessUrl` will suffice for most cases, you may need to do something different from redirecting to a URL once logout is complete.
{security-api-url}org/springframework/security/web/authentication/logout/LogoutSuccessHandler.html[`LogoutSuccessHandler`] is the Spring Security component for customizing logout success actions.
javadoc:org.springframework.security.web.authentication.logout.LogoutSuccessHandler[] is the Spring Security component for customizing logout success actions.
For example, instead of redirecting, you may want to only return a status code.
In this case, you can provide a success handler instance, like so:
@@ -349,7 +349,7 @@ Xml::
======
[TIP]
Since {security-api-url}org/springframework/security/web/authentication/logout/LogoutSuccessHandler.html[`LogoutSuccessHandler`] is a functional interface, you can provide a custom one as a lambda.
Since javadoc:org.springframework.security.web.authentication.logout.LogoutSuccessHandler[] is a functional interface, you can provide a custom one as a lambda.
[[creating-custom-logout-endpoint]]
== Creating a Custom Logout Endpoint
@@ -387,7 +387,7 @@ fun performLogout(): String {
----
======
then you will need to have that endpoint invoke Spring Security's {security-api-url}/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html[`SecurityContextLogoutHandler`] to ensure a secure and complete logout.
then you will need to have that endpoint invoke Spring Security's javadoc:org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler[] to ensure a secure and complete logout.
Something like the following is needed at a minimum:
.Custom Logout Endpoint
@@ -422,12 +422,12 @@ fun performLogout(val authentication: Authentication, val request: HttpServletRe
----
======
Such will clear out the {security-api-url}/org/springframework/security/core/context/SecurityContextHolderStrategy.html[`SecurityContextHolderStrategy`] and {security-api-url}/org/springframework/security/web/context/SecurityContextRepository.html[`SecurityContextRepository`] as needed.
Such will clear out the javadoc:org.springframework.security.core.context.SecurityContextHolderStrategy[] and javadoc:org.springframework.security.web.context.SecurityContextRepository[] as needed.
Also, you'll need to <<permit-logout-endpoints, explicitly permit the endpoint>>.
[WARNING]
Failing to call {security-api-url}/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html[`SecurityContextLogoutHandler`] means that xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontext[the `SecurityContext`] could still be available on subsequent requests, meaning that the user is not actually logged out.
Failing to call javadoc:org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler[] means that xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontext[the `SecurityContext`] could still be available on subsequent requests, meaning that the user is not actually logged out.
[[testing-logout]]
== Testing Logout
@@ -18,7 +18,7 @@ image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to
image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-http-requests.adoc[`AuthorizationFilter`] indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`.
image:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__.
The configured xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of {security-api-url}org/springframework/security/web/authentication/www/BasicAuthenticationEntryPoint.html[`BasicAuthenticationEntryPoint`], which sends a WWW-Authenticate header.
The configured xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of javadoc:org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint[], which sends a WWW-Authenticate header.
The `RequestCache` is typically a `NullRequestCache` that does not save the request since the client is capable of replaying the requests it originally requested.
When a client receives the `WWW-Authenticate` header, it knows it should retry with a username and password.
@@ -41,18 +41,18 @@ image:{icondir}/number_3.png[] If authentication fails, then __Failure__.
. The xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out.
. `RememberMeServices.loginFail` is invoked.
If remember me is not configured, this is a no-op.
See the {security-api-url}org/springframework/security/web/authentication/RememberMeServices.html[`RememberMeServices`] interface in the Javadoc.
See the javadoc:org.springframework.security.web.authentication.RememberMeServices[] interface in the Javadoc.
. `AuthenticationEntryPoint` is invoked to trigger the WWW-Authenticate to be sent again.
See the {security-api-url}org/springframework/security/web/AuthenticationEntryPoint.html[`AuthenticationEntryPoint`] interface in the Javadoc.
See the javadoc:org.springframework.security.web.AuthenticationEntryPoint[] interface in the Javadoc.
image:{icondir}/number_4.png[] If authentication is successful, then __Success__.
. The xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[Authentication] is set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder].
. `RememberMeServices.loginSuccess` is invoked.
If remember me is not configured, this is a no-op.
See the {security-api-url}org/springframework/security/web/authentication/RememberMeServices.html[`RememberMeServices`] interface in the Javadoc.
See the javadoc:org.springframework.security.web.authentication.RememberMeServices[] interface in the Javadoc.
. The `BasicAuthenticationFilter` invokes `FilterChain.doFilter(request,response)` to continue with the rest of the application logic.
See the {security-api-url}org/springframework/security/web/authentication/www/BasicAuthenticationFilter.html[`BasicAuthenticationFilter`] Class in the Javadoc
See the javadoc:org.springframework.security.web.authentication.www.BasicAuthenticationFilter[] Class in the Javadoc
By default, Spring Security's HTTP Basic Authentication support is enabled.
However, as soon as any servlet based configuration is provided, HTTP Basic must be explicitly provided.
@@ -1,8 +1,8 @@
[[servlet-authentication-credentialscontainer]]
= CredentialsContainer
{security-api-url}org/springframework/security/core/CredentialsContainer.html[The `CredentialsContainer`] interface indicates that the implementing object contains sensitive data, and is used internally by Spring Security to erase the authentication credentials after a successful authentication.
This interface is implemented by most of Spring Security internal domain classes, like {security-api-url}org/springframework/security/core/userdetails/User.html[User] and {security-api-url}org/springframework/security/authentication/UsernamePasswordAuthenticationToken.html[UsernamePasswordAuthenticationToken].
The javadoc:org.springframework.security.core.CredentialsContainer[] interface indicates that the implementing object contains sensitive data, and is used internally by Spring Security to erase the authentication credentials after a successful authentication.
This interface is implemented by most of Spring Security internal domain classes, like javadoc:org.springframework.security.core.userdetails.User[] and javadoc:org.springframework.security.authentication.UsernamePasswordAuthenticationToken[].
The `ProviderManager` manager checks whether the returned `Authentication` implements this interface.
If so, xref:servlet/authentication/architecture.adoc#servlet-authentication-providermanager-erasing-credentials[it calls the `eraseCredentials` method] to remove the credentials from the object.
@@ -2,7 +2,7 @@
= DaoAuthenticationProvider
:figures: servlet/authentication/unpwd
{security-api-url}org/springframework/security/authentication/dao/DaoAuthenticationProvider.html[`DaoAuthenticationProvider`] is an xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationprovider[`AuthenticationProvider`] implementation that uses a xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`] and xref:servlet/authentication/passwords/password-encoder.adoc#servlet-authentication-password-storage[`PasswordEncoder`] to authenticate a username and password.
javadoc:org.springframework.security.authentication.dao.DaoAuthenticationProvider[] is an xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationprovider[`AuthenticationProvider`] implementation that uses a xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`] and xref:servlet/authentication/passwords/password-encoder.adoc#servlet-authentication-password-storage[`PasswordEncoder`] to authenticate a username and password.
This section examines how `DaoAuthenticationProvider` works within Spring Security.
The following figure explains the workings of the xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationmanager[`AuthenticationManager`] in figures from the xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd-input[Reading the Username & Password] section.
@@ -33,7 +33,7 @@ key: A private key to prevent modification of the nonce token
----
You need to ensure that you xref:features/authentication/password-storage.adoc#authentication-password-storage-configuration[configure] insecure plain text xref:features/authentication/password-storage.adoc#authentication-password-storage[Password Storage] using `NoOpPasswordEncoder`.
(See the {security-api-url}org/springframework/security/crypto/password/NoOpPasswordEncoder.html[`NoOpPasswordEncoder`] class in the Javadoc.)
(See the javadoc:org.springframework.security.crypto.password.NoOpPasswordEncoder[] class in the Javadoc.)
The following provides an example of configuring Digest Authentication with Java Configuration:
.Digest Authentication
@@ -19,7 +19,7 @@ image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to
image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-http-requests.adoc[`AuthorizationFilter`] indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`.
image:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__ and sends a redirect to the login page with the configured xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`].
In most cases, the `AuthenticationEntryPoint` is an instance of {security-api-url}org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.html[`LoginUrlAuthenticationEntryPoint`].
In most cases, the `AuthenticationEntryPoint` is an instance of javadoc:org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint[].
image:{icondir}/number_4.png[] The browser requests the login page to which it was redirected.
@@ -45,19 +45,19 @@ image:{icondir}/number_3.png[] If authentication fails, then __Failure__.
. The xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out.
. `RememberMeServices.loginFail` is invoked.
If remember me is not configured, this is a no-op.
See the {security-api-url}org/springframework/security/web/authentication/RememberMeServices.html[`RememberMeServices`] interface in the Javadoc.
See the javadoc:org.springframework.security.web.authentication.RememberMeServices[] interface in the Javadoc.
. `AuthenticationFailureHandler` is invoked.
See the {security-api-url}org/springframework/security/web/authentication/AuthenticationFailureHandler.html[`AuthenticationFailureHandler`] class in the Javadoc
See the javadoc:org.springframework.security.web.authentication.AuthenticationFailureHandler[] class in the Javadoc
image:{icondir}/number_4.png[] If authentication is successful, then __Success__.
. `SessionAuthenticationStrategy` is notified of a new login.
See the {security-api-url}org/springframework/security/web/authentication/session/SessionAuthenticationStrategy.html[`SessionAuthenticationStrategy`] interface in the Javadoc.
See the javadoc:org.springframework.security.web.authentication.session.SessionAuthenticationStrategy[] interface in the Javadoc.
. The xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[Authentication] is set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder].
See the {security-api-url}org/springframework/security/web/context/SecurityContextPersistenceFilter.html[`SecurityContextPersistenceFilter`] class in the Javadoc.
See the javadoc:org.springframework.security.web.context.SecurityContextPersistenceFilter[] class in the Javadoc.
. `RememberMeServices.loginSuccess` is invoked.
If remember me is not configured, this is a no-op.
See the {security-api-url}org/springframework/security/web/authentication/RememberMeServices.html[`RememberMeServices`] interface in the Javadoc.
See the javadoc:org.springframework.security.web.authentication.RememberMeServices[] interface in the Javadoc.
. `ApplicationEventPublisher` publishes an `InteractiveAuthenticationSuccessEvent`.
. The `AuthenticationSuccessHandler` is invoked. Typically, this is a `SimpleUrlAuthenticationSuccessHandler`, which redirects to a request saved by xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] when we redirect to the login page.
@@ -3,7 +3,7 @@
Spring Security's `InMemoryUserDetailsManager` implements xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[UserDetailsService] to provide support for username/password based authentication that is stored in memory.
`InMemoryUserDetailsManager` provides management of `UserDetails` by implementing the `UserDetailsManager` interface.
`UserDetails`-based authentication is used by Spring Security when it is configured to <<servlet-authentication-unpwd-input,accept a username and password>> for authentication.
`UserDetails`-based authentication is used by Spring Security when it is configured to xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd-input[accept a username and password] for authentication.
In the following sample, we use xref:features/authentication/password-storage.adoc#authentication-password-storage-boot-cli[Spring Boot CLI] to encode a password value of `password` and get the encoded password of `+{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW+`:
@@ -1,7 +1,7 @@
[[servlet-authentication-userdetailsservice]]
= UserDetailsService
{security-api-url}org/springframework/security/core/userdetails/UserDetailsService.html[`UserDetailsService`] is used by xref:servlet/authentication/passwords/dao-authentication-provider.adoc#servlet-authentication-daoauthenticationprovider[`DaoAuthenticationProvider`] for retrieving a username, a password, and other attributes for authenticating with a username and password.
javadoc:org.springframework.security.core.userdetails.UserDetailsService[] is used by xref:servlet/authentication/passwords/dao-authentication-provider.adoc#servlet-authentication-daoauthenticationprovider[`DaoAuthenticationProvider`] for retrieving a username, a password, and other attributes for authenticating with a username and password.
Spring Security provides xref:servlet/authentication/passwords/in-memory.adoc#servlet-authentication-inmemory[in-memory], xref:servlet/authentication/passwords/jdbc.adoc#servlet-authentication-jdbc[JDBC], and xref:servlet/authentication/passwords/caching.adoc#servlet-authentication-caching-user-details[caching] implementations of `UserDetailsService`.
You can define custom authentication by exposing a custom `UserDetailsService` as a bean.
@@ -1,5 +1,5 @@
[[servlet-authentication-userdetails]]
= UserDetails
{security-api-url}org/springframework/security/core/userdetails/UserDetails.html[`UserDetails`] is returned by the xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`].
javadoc:org.springframework.security.core.userdetails.UserDetails[] is returned by the xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`].
The xref:servlet/authentication/passwords/dao-authentication-provider.adoc#servlet-authentication-daoauthenticationprovider[`DaoAuthenticationProvider`] validates the `UserDetails` and then returns an xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`] that has a principal that is the `UserDetails` returned by the configured `UserDetailsService`.
@@ -59,8 +59,8 @@ Cookie: SESSION=4c66e474-3f5a-43ed-8e48-cc1d8cb1d1c8
== SecurityContextRepository
// FIXME: api documentation
In Spring Security the association of the user to future requests is made using {security-api-url}org/springframework/security/web/context/SecurityContextRepository.html[`SecurityContextRepository`].
The default implementation of `SecurityContextRepository` is {security-api-url}org/springframework/security/web/context/DelegatingSecurityContextRepository.html[`DelegatingSecurityContextRepository`] which delegates to the following:
In Spring Security the association of the user to future requests is made using javadoc:org.springframework.security.web.context.SecurityContextRepository[].
The default implementation of `SecurityContextRepository` is javadoc:org.springframework.security.web.context.DelegatingSecurityContextRepository[] which delegates to the following:
* <<httpsecuritycontextrepository,`HttpSessionSecurityContextRepository`>>
* <<requestattributesecuritycontextrepository,`RequestAttributeSecurityContextRepository`>>
@@ -68,18 +68,18 @@ The default implementation of `SecurityContextRepository` is {security-api-url}o
[[httpsecuritycontextrepository]]
=== HttpSessionSecurityContextRepository
The {security-api-url}org/springframework/security/web/context/HttpSessionSecurityContextRepository.html[`HttpSessionSecurityContextRepository`] associates the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontext[`SecurityContext`] to the `HttpSession`.
The javadoc:org.springframework.security.web.context.HttpSessionSecurityContextRepository[] associates the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontext[`SecurityContext`] to the `HttpSession`.
Users can replace `HttpSessionSecurityContextRepository` with another implementation of `SecurityContextRepository` if they wish to associate the user with subsequent requests in another way or not at all.
[[nullsecuritycontextrepository]]
=== NullSecurityContextRepository
If it is not desirable to associate the `SecurityContext` to an `HttpSession` (i.e. when authenticating with OAuth) the {security-api-url}org/springframework/security/web/context/NullSecurityContextRepository.html[`NullSecurityContextRepository`] is an implementation of `SecurityContextRepository` that does nothing.
If it is not desirable to associate the `SecurityContext` to an `HttpSession` (i.e. when authenticating with OAuth) the javadoc:org.springframework.security.web.context.NullSecurityContextRepository[] is an implementation of `SecurityContextRepository` that does nothing.
[[requestattributesecuritycontextrepository]]
=== RequestAttributeSecurityContextRepository
The {security-api-url}org/springframework/security/web/context/RequestAttributeSecurityContextRepository.html[`RequestAttributeSecurityContextRepository`] saves the `SecurityContext` as a request attribute to make sure the `SecurityContext` is available for a single request that occurs across dispatch types that may clear out the `SecurityContext`.
The javadoc:org.springframework.security.web.context.RequestAttributeSecurityContextRepository[] saves the `SecurityContext` as a request attribute to make sure the `SecurityContext` is available for a single request that occurs across dispatch types that may clear out the `SecurityContext`.
For example, assume that a client makes a request, is authenticated, and then an error occurs.
Depending on the servlet container implementation, the error means that any `SecurityContext` that was established is cleared out and then the error dispatch is made.
@@ -118,7 +118,7 @@ XML::
[[delegatingsecuritycontextrepository]]
=== DelegatingSecurityContextRepository
The {security-api-url}org/springframework/security/web/context/DelegatingSecurityContextRepository.html[`DelegatingSecurityContextRepository`] saves the `SecurityContext` to multiple `SecurityContextRepository` delegates and allows retrieval from any of the delegates in a specified order.
The javadoc:org.springframework.security.web.context.DelegatingSecurityContextRepository[] saves the `SecurityContext` to multiple `SecurityContextRepository` delegates and allows retrieval from any of the delegates in a specified order.
The most useful arrangement for this is configured with the following example, which allows the use of both xref:requestattributesecuritycontextrepository[`RequestAttributeSecurityContextRepository`] and xref:httpsecuritycontextrepository[`HttpSessionSecurityContextRepository`] simultaneously.
@@ -189,7 +189,7 @@ In Spring Security 6, the example shown above is the default configuration.
[[securitycontextpersistencefilter]]
== SecurityContextPersistenceFilter
The {security-api-url}org/springframework/security/web/context/SecurityContextPersistenceFilter.html[`SecurityContextPersistenceFilter`] is responsible for persisting the `SecurityContext` between requests using the xref::servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`].
The javadoc:org.springframework.security.web.context.SecurityContextPersistenceFilter[] is responsible for persisting the `SecurityContext` between requests using the xref::servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`].
image::{figures}/securitycontextpersistencefilter.png[]
@@ -210,7 +210,7 @@ To avoid these problems, the `SecurityContextPersistenceFilter` wraps both the `
[[securitycontextholderfilter]]
== SecurityContextHolderFilter
The {security-api-url}org/springframework/security/web/context/SecurityContextHolderFilter.html[`SecurityContextHolderFilter`] is responsible for loading the `SecurityContext` between requests using the xref::servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`].
The javadoc:org.springframework.security.web.context.SecurityContextHolderFilter[] is responsible for loading the `SecurityContext` between requests using the xref::servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`].
image::{figures}/securitycontextholderfilter.png[]
@@ -89,7 +89,7 @@ void loginSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication successfulAuthentication);
----
See the Javadoc for {security-api-url}org/springframework/security/web/authentication/RememberMeServices.html[`RememberMeServices`] for a fuller discussion on what the methods do, although note that, at this stage, `AbstractAuthenticationProcessingFilter` calls only the `loginFail()` and `loginSuccess()` methods.
See the Javadoc for javadoc:org.springframework.security.web.authentication.RememberMeServices[] for a fuller discussion on what the methods do, although note that, at this stage, `AbstractAuthenticationProcessingFilter` calls only the `loginFail()` and `loginSuccess()` methods.
The `autoLogin()` method is called by `RememberMeAuthenticationFilter` whenever the `SecurityContextHolder` does not contain an `Authentication`.
This interface, therefore, provides the underlying remember-me implementation with sufficient notification of authentication-related events and delegates to the implementation whenever a candidate web request might contain a cookie and wish to be remembered.
This design allows any number of remember-me implementation strategies.
@@ -49,7 +49,7 @@ The latter is also used when configuring an invalid session URL through the name
[[moving-away-from-sessionmanagementfilter]]
==== Moving Away From `SessionManagementFilter`
In Spring Security 5, the default configuration relies on `SessionManagementFilter` to detect if a user just authenticated and invoke {security-api-url}org/springframework/security/web/authentication/session/SessionAuthenticationStrategy.html[the `SessionAuthenticationStrategy`].
In Spring Security 5, the default configuration relies on `SessionManagementFilter` to detect if a user just authenticated and invoke the javadoc:org.springframework.security.web.authentication.session.SessionAuthenticationStrategy[SessionAuthenticationStrategy].
The problem with this is that it means that in a typical setup, the `HttpSession` must be read for every request.
In Spring Security 6, the default is that authentication mechanisms themselves must invoke the `SessionAuthenticationStrategy`.
@@ -63,10 +63,10 @@ In Spring Security 6, the `SessionManagementFilter` is not used by default, ther
|Method |Replacement
|`sessionAuthenticationErrorUrl`
|Configure an {security-api-url}/org/springframework/security/web/authentication/AuthenticationFailureHandler.html[`AuthenticationFailureHandler`] in your authentication mechanism
|Configure an javadoc:org.springframework.security.web.authentication.AuthenticationFailureHandler[] in your authentication mechanism
|`sessionAuthenticationFailureHandler`
|Configure an {security-api-url}/org/springframework/security/web/authentication/AuthenticationFailureHandler.html[`AuthenticationFailureHandler`] in your authentication mechanism
|Configure an javadoc:org.springframework.security.web.authentication.AuthenticationFailureHandler[] in your authentication mechanism
|`sessionAuthenticationStrategy`
|Configure an `SessionAuthenticationStrategy` in your authentication mechanism as <<moving-away-from-sessionmanagementfilter,discussed above>>
@@ -589,8 +589,8 @@ If that is your case, you might want to <<clearing-session-cookie-on-logout,conf
=== Customizing the Invalid Session Strategy
The `invalidSessionUrl` is a convenience method for setting the `InvalidSessionStrategy` using the {security-api-url}/org/springframework/security/web/session/SimpleRedirectInvalidSessionStrategy.html[`SimpleRedirectInvalidSessionStrategy` implementation].
If you want to customize the behavior, you can implement the {security-api-url}/org/springframework/security/web/session/InvalidSessionStrategy.html[`InvalidSessionStrategy`] interface and configure it using the `invalidSessionStrategy` method:
The `invalidSessionUrl` is a convenience method for setting the `InvalidSessionStrategy` using the javadoc:org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy[`SimpleRedirectInvalidSessionStrategy` implementation].
If you want to customize the behavior, you can implement the javadoc:org.springframework.security.web.session.InvalidSessionStrategy[] interface and configure it using the `invalidSessionStrategy` method:
[tabs]
======
@@ -898,7 +898,7 @@ public class SomeClass {
== Forcing Eager Session Creation
At times, it can be valuable to eagerly create sessions.
This can be done by using the {security-api-url}org/springframework/security/web/session/ForceEagerSessionCreationFilter.html[`ForceEagerSessionCreationFilter`] which can be configured using:
This can be done by using the javadoc:org.springframework.security.web.session.ForceEagerSessionCreationFilter[] which can be configured using:
[tabs]
======
@@ -145,7 +145,7 @@ Many sites allow certain limited access under remember-me authentication, but re
[[authz-authorization-managers]]
==== AuthorizationManagers
There are also helpful static factories in {security-api-url}org/springframework/security/authorization/AuthorizationManagers.html[`AuthorizationManagers`] for composing individual ``AuthorizationManager``s into more sophisticated expressions.
There are also helpful static factories in javadoc:org.springframework.security.authorization.AuthorizationManagers[] for composing individual ``AuthorizationManager``s into more sophisticated expressions.
[[authz-custom-authorization-manager]]
==== Custom Authorization Managers
@@ -391,7 +391,7 @@ Many sites allow certain limited access under remember-me authentication but req
When we have used the `IS_AUTHENTICATED_ANONYMOUSLY` attribute to grant anonymous access, this attribute was being processed by the `AuthenticatedVoter`.
For more information, see
{security-api-url}org/springframework/security/access/vote/AuthenticatedVoter.html[`AuthenticatedVoter`].
javadoc:org.springframework.security.access.vote.AuthenticatedVoter[].
[[authz-custom-voter]]
@@ -192,7 +192,7 @@ public SecurityFilterChain web(HttpSecurity http) throws Exception {
.anyRequest().authenticated()
)
// ...
return http.build();
}
----
@@ -209,7 +209,7 @@ fun web(http: HttpSecurity): SecurityFilterChain {
authorize(anyRequest, authenticated)
}
}
return http.build()
}
----
@@ -403,7 +403,7 @@ Spring Security supports matching requests against a regular expression.
This can come in handy if you want to apply more strict matching criteria than `**` on a subdirectory.
For example, consider a path that contains the username and the rule that all usernames must be alphanumeric.
You can use {security-api-url}org/springframework/security/web/util/matcher/RegexRequestMatcher.html[`RegexRequestMatcher`] to respect this rule, like so:
You can use javadoc:org.springframework.security.web.util.matcher.RegexRequestMatcher[] to respect this rule, like so:
.Match with Regex
[tabs]
@@ -643,7 +643,7 @@ This need can arise in at least two different ways:
[NOTE]
This feature is not currently supported in XML
In Java configuration, you can create your own {security-api-url}org/springframework/security/web/util/matcher/RequestMatcher.html[`RequestMatcher`] and supply it to the DSL like so:
In Java configuration, you can create your own javadoc:org.springframework.security.web.util.matcher.RequestMatcher[] and supply it to the DSL like so:
.Authorize by Dispatcher Type
====
@@ -672,7 +672,7 @@ http {
====
[TIP]
Because {security-api-url}org/springframework/security/web/util/matcher/RequestMatcher.html[`RequestMatcher`] is a functional interface, you can supply it as a lambda in the DSL.
Because javadoc:org.springframework.security.web.util.matcher.RequestMatcher[] is a functional interface, you can supply it as a lambda in the DSL.
However, if you want to extract values from the request, you will need to have a concrete class since that requires overriding a `default` method.
Once authorized, you can test it using xref:servlet/test/method.adoc#test-method-withmockuser[Security's test support] in the following way:
@@ -922,15 +922,15 @@ Because the performance impact is now addressed, Spring Security recommends usin
== Migrating from `authorizeRequests`
[NOTE]
`AuthorizationFilter` supersedes {security-api-url}org/springframework/security/web/access/intercept/FilterSecurityInterceptor.html[`FilterSecurityInterceptor`].
`AuthorizationFilter` supersedes javadoc:org.springframework.security.web.access.intercept.FilterSecurityInterceptor[].
To remain backward compatible, `FilterSecurityInterceptor` remains the default.
This section discusses how `AuthorizationFilter` works and how to override the default configuration.
The {security-api-url}org/springframework/security/web/access/intercept/AuthorizationFilter.html[`AuthorizationFilter`] provides xref:servlet/authorization/index.adoc#servlet-authorization[authorization] for ``HttpServletRequest``s.
The javadoc:org.springframework.security.web.access.intercept.AuthorizationFilter[] provides xref:servlet/authorization/index.adoc#servlet-authorization[authorization] for ``HttpServletRequest``s.
It is inserted into the xref:servlet/architecture.adoc#servlet-filterchainproxy[FilterChainProxy] as one of the xref:servlet/architecture.adoc#servlet-security-filters[Security Filters].
You can override the default when you declare a `SecurityFilterChain`.
Instead of using {security-api-url}org/springframework/security/config/annotation/web/builders/HttpSecurity.html#authorizeRequests()[`authorizeRequests`], use `authorizeHttpRequests`, like so:
Instead of using javadoc:org.springframework.security.config.annotation.web.builders.HttpSecurity#authorizeRequests()[authorizeRequests], use `authorizeHttpRequests`, like so:
.Use authorizeHttpRequests
[tabs]
@@ -960,12 +960,12 @@ This simplifies reuse and customization.
Instead of the authentication needing to be looked up for every request, it will only look it up in requests where an authorization decision requires authentication.
3. Bean-based configuration support.
When `authorizeHttpRequests` is used instead of `authorizeRequests`, then {security-api-url}org/springframework/security/web/access/intercept/AuthorizationFilter.html[`AuthorizationFilter`] is used instead of {security-api-url}org/springframework/security/web/access/intercept/FilterSecurityInterceptor.html[`FilterSecurityInterceptor`].
When `authorizeHttpRequests` is used instead of `authorizeRequests`, then javadoc:org.springframework.security.web.access.intercept.AuthorizationFilter[] is used instead of javadoc:org.springframework.security.web.access.intercept.FilterSecurityInterceptor[].
=== Migrating Expressions
Where possible, it is recommended that you use type-safe authorization managers instead of SpEL.
For Java configuration, {security-api-url}org/springframework/security/web/access/expression/WebExpressionAuthorizationManager.html[`WebExpressionAuthorizationManager`] is available to help migrate legacy SpEL.
For Java configuration, javadoc:org.springframework.security.web.access.expression.WebExpressionAuthorizationManager[] is available to help migrate legacy SpEL.
To use `WebExpressionAuthorizationManager`, you can construct one with the expression you are trying to migrate, like so:
@@ -1009,12 +1009,12 @@ Kotlin::
For complex instructions that include bean references as well as other expressions, it is recommended that you change those to implement `AuthorizationManager` and refer to them by calling `.access(AuthorizationManager)`.
If you are not able to do that, you can configure a {security-api-url}org/springframework/security/web/access/expression/DefaultHttpSecurityExpressionHandler.html[`DefaultHttpSecurityExpressionHandler`] with a bean resolver and supply that to `WebExpressionAuthorizationManager#setExpressionhandler`.
If you are not able to do that, you can configure a javadoc:org.springframework.security.web.access.expression.DefaultHttpSecurityExpressionHandler[] with a bean resolver and supply that to `WebExpressionAuthorizationManager#setExpressionhandler`.
[[security-matchers]]
== Security Matchers
The {security-api-url}org/springframework/security/web/util/matcher/RequestMatcher.html[`RequestMatcher`] interface is used to determine if a request matches a given rule.
The javadoc:org.springframework.security.web.util.matcher.RequestMatcher[] interface is used to determine if a request matches a given rule.
We use `securityMatchers` to determine if xref:servlet/configuration/java.adoc#jc-httpsecurity[a given `HttpSecurity`] should be applied to a given request.
The same way, we can use `requestMatchers` to determine the authorization rules that we should apply to a given request.
Look at the following example:
@@ -1074,7 +1074,7 @@ open class SecurityConfig {
<3> Allow access to URLs that start with `/admin/` to users with the `ADMIN` role
<4> Any other request that doesn't match the rules above, will require authentication
The `securityMatcher(s)` and `requestMatcher(s)` methods will decide which `RequestMatcher` implementation fits best for your application: If {spring-framework-reference-url}web.html#spring-web[Spring MVC] is in the classpath, then {security-api-url}org/springframework/security/web/servlet/util/matcher/MvcRequestMatcher.html[`MvcRequestMatcher`] will be used, otherwise, {security-api-url}org/springframework/security/web/servlet/util/matcher/AntPathRequestMatcher.html[`AntPathRequestMatcher`] will be used.
The `securityMatcher(s)` and `requestMatcher(s)` methods will decide which `RequestMatcher` implementation fits best for your application: If {spring-framework-reference-url}web.html#spring-web[Spring MVC] is in the classpath, then javadoc:org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher[] will be used, otherwise, javadoc:org.springframework.security.web.util.matcher.AntPathRequestMatcher[] will be used.
You can read more about the Spring MVC integration xref:servlet/integrations/mvc.adoc[here].
If you want to use a specific `RequestMatcher`, just pass an implementation to the `securityMatcher` and/or `requestMatcher` methods:
@@ -117,15 +117,15 @@ A given invocation to `MyCustomerService#readCustomer` may look something like t
image::{figures}/methodsecurity.png[]
1. Spring AOP invokes its proxy method for `readCustomer`. Among the proxy's other advisors, it invokes an {security-api-url}org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptor.html[`AuthorizationManagerBeforeMethodInterceptor`] that matches <<annotation-method-pointcuts,the `@PreAuthorize` pointcut>>
2. The interceptor invokes {security-api-url}org/springframework/security/authorization/method/PreAuthorizeAuthorizationManager.html[`PreAuthorizeAuthorizationManager#check`]
1. Spring AOP invokes its proxy method for `readCustomer`. Among the proxy's other advisors, it invokes an javadoc:org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor[] that matches <<annotation-method-pointcuts,the `@PreAuthorize` pointcut>>
2. The interceptor invokes javadoc:org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager[`PreAuthorizeAuthorizationManager#check`]
3. The authorization manager uses a `MethodSecurityExpressionHandler` to parse the annotation's <<authorization-expressions,SpEL expression>> and constructs a corresponding `EvaluationContext` from a `MethodSecurityExpressionRoot` containing xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[a `Supplier<Authentication>`] and `MethodInvocation`.
4. The interceptor uses this context to evaluate the expression; specifically, it reads xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[the `Authentication`] from the `Supplier` and checks whether it has `permission:read` in its collection of xref:servlet/authorization/architecture.adoc#authz-authorities[authorities]
5. If the evaluation passes, then Spring AOP proceeds to invoke the method.
6. If not, the interceptor publishes an `AuthorizationDeniedEvent` and throws an {security-api-url}org/springframework/security/access/AccessDeniedException.html[`AccessDeniedException`] which xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[the `ExceptionTranslationFilter`] catches and returns a 403 status code to the response
7. After the method returns, Spring AOP invokes an {security-api-url}org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptor.html[`AuthorizationManagerAfterMethodInterceptor`] that matches <<annotation-method-pointcuts,the `@PostAuthorize` pointcut>>, operating the same as above, but with {security-api-url}org/springframework/security/authorization/method/PostAuthorizeAuthorizationManager.html[`PostAuthorizeAuthorizationManager`]
6. If not, the interceptor publishes an `AuthorizationDeniedEvent` and throws an javadoc:org.springframework.security.access.AccessDeniedException[] which xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[the `ExceptionTranslationFilter`] catches and returns a 403 status code to the response
7. After the method returns, Spring AOP invokes an javadoc:org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor[] that matches <<annotation-method-pointcuts,the `@PostAuthorize` pointcut>>, operating the same as above, but with javadoc:org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager[]
8. If the evaluation passes (in this case, the return value belongs to the logged-in user), processing continues normally
9. If not, the interceptor publishes an `AuthorizationDeniedEvent` and throws an {security-api-url}org/springframework/security/access/AccessDeniedException.html[`AccessDeniedException`], which xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[the `ExceptionTranslationFilter`] catches and returns a 403 status code to the response
9. If not, the interceptor publishes an `AuthorizationDeniedEvent` and throws an javadoc:org.springframework.security.access.AccessDeniedException[], which xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[the `ExceptionTranslationFilter`] catches and returns a 403 status code to the response
[NOTE]
If the method is not being called in the context of an HTTP request, you will likely need to handle the `AccessDeniedException` yourself
@@ -150,7 +150,7 @@ Instead, use SpEL's boolean support or its support for delegating to a separate
Each annotation has its own pointcut instance that looks for that annotation or its <<meta-annotations,meta-annotation>> counterparts across the entire object hierarchy, starting at <<class-or-interface-annotations,the method and its enclosing class>>.
You can see the specifics of this in {security-api-url}org/springframework/security/authorization/method/AuthorizationMethodPointcuts.html[`AuthorizationMethodPointcuts`].
// FIXME: AuthorizationMethodPointcuts is package private and Javadoc is not published You can see the specifics of this in javadoc:org.springframework.security.authorization.method.AuthorizationMethodPointcuts[].
[[annotation-method-interceptors]]
=== Each Annotation Has Its Own Method Interceptor
@@ -161,12 +161,12 @@ For example, if needed, you can disable the Spring Security defaults and <<_enab
The method interceptors are as follows:
* For <<use-preauthorize,`@PreAuthorize`>>, Spring Security uses {security-api-url}org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptor.html[`AuthorizationManagerBeforeMethodInterceptor#preAuthorize`], which in turn uses {security-api-url}org/springframework/security/authorization/method/PreAuthorizeAuthorizationManager.html[`PreAuthorizeAuthorizationManager`]
* For <<use-postauthorize,`@PostAuthorize`>>, Spring Security uses {security-api-url}org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptor.html[`AuthorizationManagerBeforeMethodInterceptor#postAuthorize`], which in turn uses {security-api-url}org/springframework/security/authorization/method/PostAuthorizeAuthorizationManager.html[`PostAuthorizeAuthorizationManager`]
* For <<use-prefilter,`@PreFilter`>>, Spring Security uses {security-api-url}org/springframework/security/authorization/method/PreFilterAuthorizationMethodInterceptor.html[`PreFilterAuthorizationMethodInterceptor`]
* For <<use-postfilter,`@PostFilter`>>, Spring Security uses {security-api-url}org/springframework/security/authorization/method/PostFilterAuthorizationMethodInterceptor.html[`PostFilterAuthorizationMethodInterceptor`]
* For <<use-secured,`@Secured`>>, Spring Security uses {security-api-url}org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptor.html[`AuthorizationManagerBeforeMethodInterceptor#secured`], which in turn uses {security-api-url}org/springframework/security/authorization/method/SecuredAuthorizationManager.html[`SecuredAuthorizationManager`]
* For JSR-250 annotations, Spring Security uses {security-api-url}org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptor.html[`AuthorizationManagerBeforeMethodInterceptor#jsr250`], which in turn uses {security-api-url}org/springframework/security/authorization/method/Jsr250AuthorizationManager.html[`Jsr250AuthorizationManager`]
* For <<use-preauthorize,`@PreAuthorize`>>, Spring Security uses javadoc:org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor[`AuthorizationManagerBeforeMethodInterceptor#preAuthorize`], which in turn uses javadoc:org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager[]
* For <<use-postauthorize,`@PostAuthorize`>>, Spring Security uses javadoc:org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor[`AuthorizationManagerAfterMethodInterceptor#postAuthorize`], which in turn uses javadoc:org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager[]
* For <<use-prefilter,`@PreFilter`>>, Spring Security uses javadoc:org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor[]
* For <<use-postfilter,`@PostFilter`>>, Spring Security uses javadoc:org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor[]
* For <<use-secured,`@Secured`>>, Spring Security uses javadoc:org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor[`AuthorizationManagerBeforeMethodInterceptor#secured`], which in turn uses javadoc:org.springframework.security.authorization.method.SecuredAuthorizationManager[]
* For JSR-250 annotations, Spring Security uses javadoc:org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor[`AuthorizationManagerBeforeMethodInterceptor#jsr250`], which in turn uses javadoc:org.springframework.security.authorization.method.Jsr250AuthorizationManager[]
Generally speaking, you can consider the following listing as representative of what interceptors Spring Security publishes when you add `@EnableMethodSecurity`:
@@ -311,7 +311,7 @@ The primary way Spring Security enables method-level authorization support is th
[[use-preauthorize]]
=== Authorizing Method Invocation with `@PreAuthorize`
When <<activate-method-security,Method Security is active>>, you can annotate a method with the {security-api-url}org/springframework/security/access/prepost/PreAuthorize.html[`@PreAuthorize`] annotation like so:
When <<activate-method-security,Method Security is active>>, you can annotate a method with the javadoc:org.springframework.security.access.prepost.PreAuthorize[format=annotation] annotation like so:
[tabs]
======
@@ -399,7 +399,7 @@ While `@PreAuthorize` is quite helpful for declaring needed authorities, it can
[[use-postauthorize]]
=== Authorization Method Results with `@PostAuthorize`
When Method Security is active, you can annotate a method with the {security-api-url}org/springframework/security/access/prepost/PostAuthorize.html[`@PostAuthorize`] annotation like so:
When Method Security is active, you can annotate a method with the javadoc:org.springframework.security.access.prepost.PostAuthorize[format=annotation] annotation like so:
[tabs]
======
@@ -546,10 +546,7 @@ If not, Spring Security will throw an `AccessDeniedException` and return a 403 s
[[use-prefilter]]
=== Filtering Method Parameters with `@PreFilter`
[NOTE]
`@PreFilter` is not yet supported for Kotlin-specific data types; for that reason, only Java snippets are shown
When Method Security is active, you can annotate a method with the {security-api-url}org/springframework/security/access/prepost/PreFilter.html[`@PreFilter`] annotation like so:
When Method Security is active, you can annotate a method with the javadoc:org.springframework.security.access.prepost.PreFilter[format=annotation] annotation like so:
[tabs]
======
@@ -566,6 +563,20 @@ public class BankService {
}
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Component
open class BankService {
@PreFilter("filterObject.owner == authentication.name")
fun updateAccounts(vararg accounts: Account): Collection<Account> {
// ... `accounts` will only contain the accounts owned by the logged-in user
return updated
}
}
----
======
This is meant to filter out any values from `accounts` where the expression `filterObject.owner == authentication.name` fails.
@@ -591,6 +602,23 @@ void updateAccountsWhenOwnedThenReturns() {
assertThat(updated).containsOnly(ownedBy);
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Autowired
lateinit var bankService: BankService
@WithMockUser(username="owner")
@Test
fun updateAccountsWhenOwnedThenReturns() {
val ownedBy: Account = ...
val notOwnedBy: Account = ...
val updated: Collection<Account> = bankService.updateAccounts(ownedBy, notOwnedBy)
assertThat(updated).containsOnly(ownedBy)
}
----
======
[TIP]
@@ -618,6 +646,23 @@ public Collection<Account> updateAccounts(Map<String, Account> accounts)
@PreFilter("filterObject.owner == authentication.name")
public Collection<Account> updateAccounts(Stream<Account> accounts)
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@PreFilter("filterObject.owner == authentication.name")
fun updateAccounts(accounts: Array<Account>): Collection<Account>
@PreFilter("filterObject.owner == authentication.name")
fun updateAccounts(accounts: Collection<Account>): Collection<Account>
@PreFilter("filterObject.value.owner == authentication.name")
fun updateAccounts(accounts: Map<String, Account>): Collection<Account>
@PreFilter("filterObject.owner == authentication.name")
fun updateAccounts(accounts: Stream<Account>): Collection<Account>
----
======
The result is that the above method will only have the `Account` instances where their `owner` attribute matches the logged-in user's `name`.
@@ -625,10 +670,7 @@ The result is that the above method will only have the `Account` instances where
[[use-postfilter]]
=== Filtering Method Results with `@PostFilter`
[NOTE]
`@PostFilter` is not yet supported for Kotlin-specific data types; for that reason, only Java snippets are shown
When Method Security is active, you can annotate a method with the {security-api-url}org/springframework/security/access/prepost/PostFilter.html[`@PostFilter`] annotation like so:
When Method Security is active, you can annotate a method with the javadoc:org.springframework.security.access.prepost.PostFilter[format=annotation] annotation like so:
[tabs]
======
@@ -645,6 +687,20 @@ public class BankService {
}
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Component
open class BankService {
@PreFilter("filterObject.owner == authentication.name")
fun readAccounts(vararg ids: String): Collection<Account> {
// ... the return value will be filtered to only contain the accounts owned by the logged-in user
return accounts
}
}
----
======
This is meant to filter out any values from the return value where the expression `filterObject.owner == authentication.name` fails.
@@ -669,6 +725,22 @@ void readAccountsWhenOwnedThenReturns() {
assertThat(accounts.get(0).getOwner()).isEqualTo("owner");
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Autowired
lateinit var bankService: BankService
@WithMockUser(username="owner")
@Test
fun readAccountsWhenOwnedThenReturns() {
val accounts: Collection<Account> = bankService.updateAccounts("owner", "not-owner")
assertThat(accounts).hasSize(1)
assertThat(accounts[0].owner).isEqualTo("owner")
}
----
======
[TIP]
@@ -678,7 +750,15 @@ void readAccountsWhenOwnedThenReturns() {
For example, the above `readAccounts` declaration will function the same way as the following other three:
```java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@PostFilter("filterObject.owner == authentication.name")
public Collection<Account> readAccounts(String... ids)
@PostFilter("filterObject.owner == authentication.name")
public Account[] readAccounts(String... ids)
@@ -687,7 +767,25 @@ public Map<String, Account> readAccounts(String... ids)
@PostFilter("filterObject.owner == authentication.name")
public Stream<Account> readAccounts(String... ids)
```
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@PostFilter("filterObject.owner == authentication.name")
fun readAccounts(vararg ids: String): Collection<Account>
@PostFilter("filterObject.owner == authentication.name")
fun readAccounts(vararg ids: String): Array<Account>
@PostFilter("filterObject.owner == authentication.name")
fun readAccounts(vararg ids: String): Map<String, Account>
@PostFilter("filterObject.owner == authentication.name")
fun readAccounts(vararg ids: String): Stream<Account>
----
======
The result is that the above method will return the `Account` instances where their `owner` attribute matches the logged-in user's `name`.
@@ -697,7 +795,7 @@ In-memory filtering can obviously be expensive, and so be considerate of whether
[[use-secured]]
=== Authorizing Method Invocation with `@Secured`
{security-api-url}org/springframework/security/access/annotation/Secured.html[`@Secured`] is a legacy option for authorizing invocations.
javadoc:org.springframework.security.access.annotation.Secured[format=annotation] is a legacy option for authorizing invocations.
<<use-preauthorize,`@PreAuthorize`>> supercedes it and is recommended instead.
To use the `@Secured` annotation, you should first change your Method Security declaration to enable it like so:
@@ -1377,7 +1475,7 @@ You can place your interceptor in between Spring Security method interceptors us
=== Customizing Expression Handling
Or, third, you can customize how each SpEL expression is handled.
To do that, you can expose a custom {security-api-url}org.springframework.security.access.expression.method.MethodSecurityExpressionHandler.html[`MethodSecurityExpressionHandler`], like so:
To do that, you can expose a custom javadoc:org.springframework.security.access.expression.method.MethodSecurityExpressionHandler[], like so:
.Custom MethodSecurityExpressionHandler
[tabs]
@@ -1706,7 +1804,7 @@ The intention of this expression is to require that the current `Authentication`
+
Behind the scenes, this is implemented by using `AnnotationParameterNameDiscoverer`, which you can customize to support the value attribute of any specified annotation.
* If xref:servlet/integrations/data.adoc[Spring Data's] `@Param` annotation is present on at least one parameter for the method, the value is used.
2. If xref:servlet/integrations/data.adoc[Spring Data's] `@Param` annotation is present on at least one parameter for the method, the value is used.
The following example uses the `@Param` annotation:
+
[tabs]
@@ -1740,10 +1838,10 @@ The intention of this expression is to require that `name` be equal to `Authenti
+
Behind the scenes, this is implemented by using `AnnotationParameterNameDiscoverer`, which you can customize to support the value attribute of any specified annotation.
* If you compile your code with the `-parameters` argument, the standard JDK reflection API is used to discover the parameter names.
3. If you compile your code with the `-parameters` argument, the standard JDK reflection API is used to discover the parameter names.
This works on both classes and interfaces.
* Finally, if you compile your code with debug symbols, the parameter names are discovered by using the debug symbols.
4. Finally, if you compile your code with debug symbols, the parameter names are discovered by using the debug symbols.
This does not work for interfaces, since they do not have debug information about the parameter names.
For interfaces, either annotations or the `-parameters` approach must be used.
@@ -2259,8 +2357,8 @@ You can also add the Spring Boot property `spring.jackson.default-property-inclu
There are some scenarios where you may not wish to throw an `AuthorizationDeniedException` when a method is invoked without the required permissions.
Instead, you might wish to return a post-processed result, like a masked result, or a default value in cases where authorization denied happened before invoking the method.
Spring Security provides support for handling authorization denied on method invocation by using the {security-api-url}org/springframework/security/authorization/method/HandleAuthorizationDenied.html[`@HandleAuthorizationDenied`].
The handler works for denied authorizations that happened in the <<authorizing-with-annotations,`@PreAuthorize` and `@PostAuthorize` annotations>> as well as {security-api-url}org/springframework/security/authorization/AuthorizationDeniedException.html[`AuthorizationDeniedException`] thrown from the method invocation itself.
Spring Security provides support for handling authorization denied on method invocation by using the javadoc:org.springframework.security.authorization.method.HandleAuthorizationDenied[format=annotation].
The handler works for denied authorizations that happened in the <<authorizing-with-annotations,`@PreAuthorize` and `@PostAuthorize` annotations>> as well as javadoc:org.springframework.security.authorization.AuthorizationDeniedException[] thrown from the method invocation itself.
Let's consider the example from the <<authorize-object,previous section>>, but instead of creating the `AccessDeniedExceptionInterceptor` to transform an `AccessDeniedException` to a `null` return value, we will use the `handlerClass` attribute from `@HandleAuthorizationDenied`:
@@ -2375,7 +2473,7 @@ fun getEmailWhenProxiedThenNullEmail() {
There are some scenarios where you might want to return a secure result derived from the denied result.
For example, if a user is not authorized to see email addresses, you might want to apply some masking on the original email address, i.e. _useremail@example.com_ would become _use\\******@example.com_.
For those scenarios, you can override the `handleDeniedInvocationResult` from the `MethodAuthorizationDeniedHandler`, which has the {security-api-url}org/springframework/security/authorization/method/MethodInvocationResult.html[`MethodInvocationResult`] as an argument.
For those scenarios, you can override the `handleDeniedInvocationResult` from the `MethodAuthorizationDeniedHandler`, which has the javadoc:org.springframework.security.authorization.method.MethodInvocationResult[] as an argument.
Let's continue with the previous example, but instead of returning `null`, we will return a masked value of the email:
[tabs]
@@ -2720,7 +2818,7 @@ If you are using `@EnableGlobalMethodSecurity`, you should migrate to `@EnableMe
[[servlet-replace-globalmethodsecurity-with-methodsecurity]]
=== Replace xref:servlet/authorization/method-security.adoc#jc-enable-global-method-security[global method security] with xref:servlet/authorization/method-security.adoc#jc-enable-method-security[method security]
{security-api-url}org/springframework/security/config/annotation/method/configuration/EnableGlobalMethodSecurity.html[`@EnableGlobalMethodSecurity`] and xref:servlet/appendix/namespace/method-security.adoc#nsa-global-method-security[`<global-method-security>`] are deprecated in favor of {security-api-url}org/springframework/security/config/annotation/method/configuration/EnableMethodSecurity.html[`@EnableMethodSecurity`] and xref:servlet/appendix/namespace/method-security.adoc#nsa-method-security[`<method-security>`], respectively.
javadoc:org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity[format=annotation] and xref:servlet/appendix/namespace/method-security.adoc#nsa-global-method-security[`<global-method-security>`] are deprecated in favor of javadoc:org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity[`@EnableMethodSecurity`] and xref:servlet/appendix/namespace/method-security.adoc#nsa-method-security[`<method-security>`], respectively.
The new annotation and XML element activate Spring's xref:servlet/authorization/method-security.adoc#jc-enable-method-security[pre-post annotations] by default and use `AuthorizationManager` internally.
This means that the following two listings are functionally equivalent:
@@ -176,8 +176,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.formLogin(withDefaults())
.httpBasic(withDefaults());
.formLogin(Customizer.withDefaults())
.httpBasic(Customizer.withDefaults());
return http.build();
}
----
@@ -185,10 +185,10 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
The default configuration (shown in the preceding example):
* Ensures that any request to our application requires the user to be authenticated
* Lets users authenticate with form based login
* Lets users authenticate with form-based login
* Lets users authenticate with HTTP Basic authentication
Note that this configuration is parallels the XML Namespace configuration:
Note that this configuration parallels the XML namespace configuration:
[source,xml]
----
@@ -199,12 +199,16 @@ Note that this configuration is parallels the XML Namespace configuration:
</http>
----
== Multiple HttpSecurity Instances
=== Multiple HttpSecurity Instances
To effectively manage security in an application where certain areas need different protection, we can employ multiple filter chains alongside the `securityMatcher` DSL method.
This approach allows us to define distinct security configurations tailored to specific parts of the application, enhancing overall application security and control.
We can configure multiple `HttpSecurity` instances just as we can have multiple `<http>` blocks in XML.
The key is to register multiple `SecurityFilterChain` ``@Bean``s.
The following example has a different configuration for URLs that start with `/api/`.
The following example has a different configuration for URLs that begin with `/api/`:
[[multiple-httpsecurity-instances-java]]
[source,java]
----
@Configuration
@@ -212,7 +216,6 @@ The following example has a different configuration for URLs that start with `/a
public class MultiHttpSecurityConfig {
@Bean <1>
public UserDetailsService userDetailsService() throws Exception {
// ensure the passwords are encoded properly
UserBuilder users = User.withDefaultPasswordEncoder();
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(users.username("user").password("password").roles("USER").build());
@@ -228,7 +231,7 @@ public class MultiHttpSecurityConfig {
.authorizeHttpRequests(authorize -> authorize
.anyRequest().hasRole("ADMIN")
)
.httpBasic(withDefaults());
.httpBasic(Customizer.withDefaults());
return http.build();
}
@@ -238,18 +241,244 @@ public class MultiHttpSecurityConfig {
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.formLogin(withDefaults());
.formLogin(Customizer.withDefaults());
return http.build();
}
}
----
<1> Configure Authentication as usual.
<2> Create an instance of `SecurityFilterChain` that contains `@Order` to specify which `SecurityFilterChain` should be considered first.
<3> The `http.securityMatcher` states that this `HttpSecurity` is applicable only to URLs that start with `/api/`.
<3> The `http.securityMatcher()` states that this `HttpSecurity` is applicable only to URLs that begin with `/api/`.
<4> Create another instance of `SecurityFilterChain`.
If the URL does not start with `/api/`, this configuration is used.
If the URL does not begin with `/api/`, this configuration is used.
This configuration is considered after `apiFilterChain`, since it has an `@Order` value after `1` (no `@Order` defaults to last).
=== Choosing `securityMatcher` or `requestMatchers`
A common question is:
> What is the difference between the `http.securityMatcher()` method and `requestMatchers()` used for request authorization (i.e. inside of `http.authorizeHttpRequests()`)?
To answer this question, it helps to understand that each `HttpSecurity` instance used to build a `SecurityFilterChain` contains a `RequestMatcher` to match incoming requests.
If a request does not match a `SecurityFilterChain` with higher priority (e.g. `@Order(1)`), the request can be tried against a filter chain with lower priority (e.g. no `@Order`).
[NOTE]
====
The matching logic for multiple filter chains is performed by the xref:servlet/architecture.adoc#servlet-filterchainproxy[`FilterChainProxy`].
====
The default `RequestMatcher` matches *any request* to ensure Spring Security protects *all requests by default*.
[NOTE]
====
Specifying a `securityMatcher` overrides this default.
====
[WARNING]
====
If no filter chain matches a particular request, the request is *not protected* by Spring Security.
====
The following example demonstrates a single filter chain that only protects requests that begin with `/secured/`:
[[choosing-security-matcher-request-matchers-java]]
[source,java]
----
@Configuration
@EnableWebSecurity
public class PartialSecurityConfig {
@Bean
public UserDetailsService userDetailsService() throws Exception {
// ...
}
@Bean
public SecurityFilterChain securedFilterChain(HttpSecurity http) throws Exception {
http
.securityMatcher("/secured/**") <1>
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/secured/user").hasRole("USER") <2>
.requestMatchers("/secured/admin").hasRole("ADMIN") <3>
.anyRequest().authenticated() <4>
)
.httpBasic(Customizer.withDefaults())
.formLogin(Customizer.withDefaults());
return http.build();
}
}
----
<1> Requests that begin with `/secured/` will be protected but any other requests are not protected.
<2> Requests to `/secured/user` require the `ROLE_USER` authority.
<3> Requests to `/secured/admin` require the `ROLE_ADMIN` authority.
<4> Any other requests (such as `/secured/other`) simply require an authenticated user.
[TIP]
====
It is _recommended_ to provide a `SecurityFilterChain` that does not specify any `securityMatcher` to ensure the entire application is protected, as demonstrated in the <<multiple-httpsecurity-instances-java,earlier example>>.
====
Notice that the `requestMatchers` method only applies to individual authorization rules.
Each request listed there must also match the overall `securityMatcher` for this particular `HttpSecurity` instance used to create the `SecurityFilterChain`.
Using `anyRequest()` in this example matches all other requests within this particular `SecurityFilterChain` (which must begin with `/secured/`).
[NOTE]
====
See xref:servlet/authorization/authorize-http-requests.adoc[Authorize HttpServletRequests] for more information on `requestMatchers`.
====
=== `SecurityFilterChain` Endpoints
Several filters in the `SecurityFilterChain` directly provide endpoints, such as the `UsernamePasswordAuthenticationFilter` which is set up by `http.formLogin()` and provides the `POST /login` endpoint.
In the <<choosing-security-matcher-request-matchers-java,above example>>, the `/login` endpoint is not matched by `http.securityMatcher("/secured/**")` and therefore that application would not have any `GET /login` or `POST /login` endpoint.
Such requests would return `404 Not Found`.
This is often surprising to users.
Specifying `http.securityMatcher()` affects what requests are matched by that `SecurityFilterChain`.
However, it does not automatically affect endpoints provided by the filter chain.
In such cases, you may need to customize the URL of any endpoints you would like the filter chain to provide.
The following example demonstrates a configuration that secures requests that begin with `/secured/` and denies all other requests, while also customizing endpoints provided by the `SecurityFilterChain`:
[[security-filter-chain-endpoints-java]]
[source,java]
----
@Configuration
@EnableWebSecurity
public class SecuredSecurityConfig {
@Bean
public UserDetailsService userDetailsService() throws Exception {
// ...
}
@Bean
@Order(1)
public SecurityFilterChain securedFilterChain(HttpSecurity http) throws Exception {
http
.securityMatcher("/secured/**") <1>
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated() <2>
)
.formLogin(formLogin -> formLogin <3>
.loginPage("/secured/login")
.loginProcessingUrl("/secured/login")
.permitAll()
)
.logout(logout -> logout <4>
.logoutUrl("/secured/logout")
.logoutSuccessUrl("/secured/login?logout")
.permitAll()
)
.formLogin(Customizer.withDefaults());
return http.build();
}
@Bean
public SecurityFilterChain defaultFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.anyRequest().denyAll() <5>
);
return http.build();
}
}
----
<1> Requests that begin with `/secured/` will be protected by this filter chain.
<2> Requests that begin with `/secured/` require an authenticated user.
<3> Customize form login to prefix URLs with `/secured/`.
<4> Customize logout to prefix URLs with `/secured/`.
<5> All other requests will be denied.
[NOTE]
====
This example customizes the login and logout pages, which disables Spring Security's generated pages.
You must xref:servlet/authentication/passwords/form.adoc#servlet-authentication-form-custom[provide your own] custom endpoints for `GET /secured/login` and `GET /secured/logout`.
Note that Spring Security still provides `POST /secured/login` and `POST /secured/logout` endpoints for you.
====
=== Real World Example
The following example demonstrates a slightly more real-world configuration putting all of these elements together:
[[real-world-example-java]]
[source,java]
----
@Configuration
@EnableWebSecurity
public class BankingSecurityConfig {
@Bean <1>
public UserDetailsService userDetailsService() {
UserBuilder users = User.withDefaultPasswordEncoder();
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(users.username("user1").password("password").roles("USER", "VIEW_BALANCE").build());
manager.createUser(users.username("user2").password("password").roles("USER").build());
manager.createUser(users.username("admin").password("password").roles("ADMIN").build());
return manager;
}
@Bean
@Order(1) <2>
public SecurityFilterChain approvalsSecurityFilterChain(HttpSecurity http) throws Exception {
String[] approvalsPaths = { "/accounts/approvals/**", "/loans/approvals/**", "/credit-cards/approvals/**" };
http
.securityMatcher(approvalsPaths)
.authorizeHttpRequests(authorize -> authorize
.anyRequest().hasRole("ADMIN")
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
@Bean
@Order(2) <3>
public SecurityFilterChain bankingSecurityFilterChain(HttpSecurity http) throws Exception {
String[] bankingPaths = { "/accounts/**", "/loans/**", "/credit-cards/**", "/balances/**" };
String[] viewBalancePaths = { "/balances/**" };
http
.securityMatcher(bankingPaths)
.authorizeHttpRequests(authorize -> authorize
.requestMatchers(viewBalancePaths).hasRole("VIEW_BALANCE")
.anyRequest().hasRole("USER")
);
return http.build();
}
@Bean <4>
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
String[] allowedPaths = { "/", "/user-login", "/user-logout", "/notices", "/contact", "/register" };
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers(allowedPaths).permitAll()
.anyRequest().authenticated()
)
.formLogin(formLogin -> formLogin
.loginPage("/user-login")
.loginProcessingUrl("/user-login")
)
.logout(logout -> logout
.logoutUrl("/user-logout")
.logoutSuccessUrl("/?logout")
);
return http.build();
}
}
----
<1> Begin by configuring authentication settings.
<2> Define a `SecurityFilterChain` instance with `@Order(1)`, which means that this filter chain will have the highest priority.
This filter chain applies only to requests that begin with `/accounts/approvals/`, `/loans/approvals/` or `/credit-cards/approvals/`.
Requests to this filter chain require the `ROLE_ADMIN` authority and allow HTTP Basic Authentication.
<3> Next, create another `SecurityFilterChain` instance with `@Order(2)` which will be considered second.
This filter chain applies only to requests that begin with `/accounts/`, `/loans/`, `/credit-cards/`, or `/balances/`.
Notice that because this filter chain is second, any requests that include `/approvals/` will match the previous filter chain and will *not* be matched by this filter chain.
Requests to this filter chain require the `ROLE_USER` authority.
This filter chain does not define any authentication because the next (default) filter chain contains that configuration.
<4> Lastly, create an additional `SecurityFilterChain` instance without an `@Order` annotation.
This configuration will handle requests not covered by the other filter chains and will be processed last (no `@Order` defaults to last).
Requests that match `/`, `/user-login`, `/user-logout`, `/notices`, `/contact` and `/register` allow access without authentication.
Any other requests require the user to be authenticated to access any URL not explicitly allowed or protected by other filter chains.
[[jc-custom-dsls]]
== Custom DSLs
@@ -38,7 +38,7 @@ open fun filterChain(http: HttpSecurity): SecurityFilterChain {
[NOTE]
Make sure to import the `org.springframework.security.config.annotation.web.invoke` function to enable the Kotlin DSL in your class, as the IDE will not always auto-import the method, causing compilation issues.
The default configuration (shown in the preceding listing):
The default configuration (shown in the preceding example):
* Ensures that any request to our application requires the user to be authenticated
* Lets users authenticate with form-based login
@@ -55,12 +55,16 @@ Note that this configuration parallels the XML namespace configuration:
</http>
----
== Multiple HttpSecurity Instances
=== Multiple HttpSecurity Instances
We can configure multiple `HttpSecurity` instances, just as we can have multiple `<http>` blocks.
To effectively manage security in an application where certain areas need different protection, we can employ multiple filter chains alongside the `securityMatcher` DSL method.
This approach allows us to define distinct security configurations tailored to specific parts of the application, enhancing overall application security and control.
We can configure multiple `HttpSecurity` instances just as we can have multiple `<http>` blocks in XML.
The key is to register multiple `SecurityFilterChain` ``@Bean``s.
The following example has a different configuration for URLs that start with `/api/`:
The following example has a different configuration for URLs that begin with `/api/`:
[[multiple-httpsecurity-instances-kotlin]]
[source,kotlin]
----
import org.springframework.security.config.annotation.web.invoke
@@ -69,16 +73,16 @@ import org.springframework.security.config.annotation.web.invoke
@EnableWebSecurity
class MultiHttpSecurityConfig {
@Bean <1>
public fun userDetailsService(): UserDetailsService {
val users: User.UserBuilder = User.withDefaultPasswordEncoder()
open fun userDetailsService(): UserDetailsService {
val users = User.withDefaultPasswordEncoder()
val manager = InMemoryUserDetailsManager()
manager.createUser(users.username("user").password("password").roles("USER").build())
manager.createUser(users.username("admin").password("password").roles("USER","ADMIN").build())
return manager
}
@Order(1) <2>
@Bean
@Order(1) <2>
open fun apiFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
securityMatcher("/api/**") <3>
@@ -102,10 +106,243 @@ class MultiHttpSecurityConfig {
}
}
----
<1> Configure Authentication as usual.
<2> Create an instance of `SecurityFilterChain` that contains `@Order` to specify which `SecurityFilterChain` should be considered first.
<3> The `http.securityMatcher` states that this `HttpSecurity` is applicable only to URLs that start with `/api/`
<3> The `http.securityMatcher()` states that this `HttpSecurity` is applicable only to URLs that begin with `/api/`.
<4> Create another instance of `SecurityFilterChain`.
If the URL does not start with `/api/`, this configuration is used.
If the URL does not begin with `/api/`, this configuration is used.
This configuration is considered after `apiFilterChain`, since it has an `@Order` value after `1` (no `@Order` defaults to last).
=== Choosing `securityMatcher` or `requestMatchers`
A common question is:
> What is the difference between the `http.securityMatcher()` method and `requestMatchers()` used for request authorization (i.e. inside of `http.authorizeHttpRequests()`)?
To answer this question, it helps to understand that each `HttpSecurity` instance used to build a `SecurityFilterChain` contains a `RequestMatcher` to match incoming requests.
If a request does not match a `SecurityFilterChain` with higher priority (e.g. `@Order(1)`), the request can be tried against a filter chain with lower priority (e.g. no `@Order`).
[NOTE]
====
The matching logic for multiple filter chains is performed by the xref:servlet/architecture.adoc#servlet-filterchainproxy[`FilterChainProxy`].
====
The default `RequestMatcher` matches *any request* to ensure Spring Security protects *all requests by default*.
[NOTE]
====
Specifying a `securityMatcher` overrides this default.
====
[WARNING]
====
If no filter chain matches a particular request, the request is *not protected* by Spring Security.
====
The following example demonstrates a single filter chain that only protects requests that begin with `/secured/`:
[[choosing-security-matcher-request-matchers-kotlin]]
[source,kotlin]
----
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class PartialSecurityConfig {
@Bean
open fun userDetailsService(): UserDetailsService {
// ...
}
@Bean
open fun securedFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
securityMatcher("/secured/**") <1>
authorizeHttpRequests {
authorize("/secured/user", hasRole("USER")) <2>
authorize("/secured/admin", hasRole("ADMIN")) <3>
authorize(anyRequest, authenticated) <4>
}
httpBasic { }
formLogin { }
}
return http.build()
}
}
----
<1> Requests that begin with `/secured/` will be protected but any other requests are not protected.
<2> Requests to `/secured/user` require the `ROLE_USER` authority.
<3> Requests to `/secured/admin` require the `ROLE_ADMIN` authority.
<4> Any other requests (such as `/secured/other`) simply require an authenticated user.
[TIP]
====
It is _recommended_ to provide a `SecurityFilterChain` that does not specify any `securityMatcher` to ensure the entire application is protected, as demonstrated in the <<multiple-httpsecurity-instances-kotlin,earlier example>>.
====
Notice that the `requestMatchers` method only applies to individual authorization rules.
Each request listed there must also match the overall `securityMatcher` for this particular `HttpSecurity` instance used to create the `SecurityFilterChain`.
Using `anyRequest()` in this example matches all other requests within this particular `SecurityFilterChain` (which must begin with `/secured/`).
[NOTE]
====
See xref:servlet/authorization/authorize-http-requests.adoc[Authorize HttpServletRequests] for more information on `requestMatchers`.
====
=== `SecurityFilterChain` Endpoints
Several filters in the `SecurityFilterChain` directly provide endpoints, such as the `UsernamePasswordAuthenticationFilter` which is set up by `http.formLogin()` and provides the `POST /login` endpoint.
In the <<choosing-security-matcher-request-matchers-kotlin,above example>>, the `/login` endpoint is not matched by `http.securityMatcher("/secured/**")` and therefore that application would not have any `GET /login` or `POST /login` endpoint.
Such requests would return `404 Not Found`.
This is often surprising to users.
Specifying `http.securityMatcher()` affects what requests are matched by that `SecurityFilterChain`.
However, it does not automatically affect endpoints provided by the filter chain.
In such cases, you may need to customize the URL of any endpoints you would like the filter chain to provide.
The following example demonstrates a configuration that secures requests that begin with `/secured/` and denies all other requests, while also customizing endpoints provided by the `SecurityFilterChain`:
[[security-filter-chain-endpoints-kotlin]]
[source,kotlin]
----
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecuredSecurityConfig {
@Bean
open fun userDetailsService(): UserDetailsService {
// ...
}
@Bean
@Order(1)
open fun securedFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
securityMatcher("/secured/**") <1>
authorizeHttpRequests {
authorize(anyRequest, authenticated) <2>
}
formLogin { <3>
loginPage = "/secured/login"
loginProcessingUrl = "/secured/login"
permitAll = true
}
logout { <4>
logoutUrl = "/secured/logout"
logoutSuccessUrl = "/secured/login?logout"
permitAll = true
}
}
return http.build()
}
@Bean
open fun defaultFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize(anyRequest, denyAll) <5>
}
}
return http.build()
}
}
----
<1> Requests that begin with `/secured/` will be protected by this filter chain.
<2> Requests that begin with `/secured/` require an authenticated user.
<3> Customize form login to prefix URLs with `/secured/`.
<4> Customize logout to prefix URLs with `/secured/`.
<5> All other requests will be denied.
[NOTE]
====
This example customizes the login and logout pages, which disables Spring Security's generated pages.
You must xref:servlet/authentication/passwords/form.adoc#servlet-authentication-form-custom[provide your own] custom endpoints for `GET /secured/login` and `GET /secured/logout`.
Note that Spring Security still provides `POST /secured/login` and `POST /secured/logout` endpoints for you.
====
=== Real World Example
The following example demonstrates a slightly more real-world configuration putting all of these elements together:
[[real-world-example-kotlin]]
[source,kotlin]
----
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class BankingSecurityConfig {
@Bean <1>
open fun userDetailsService(): UserDetailsService {
val users = User.withDefaultPasswordEncoder()
val manager = InMemoryUserDetailsManager()
manager.createUser(users.username("user1").password("password").roles("USER", "VIEW_BALANCE").build())
manager.createUser(users.username("user2").password("password").roles("USER").build())
manager.createUser(users.username("admin").password("password").roles("ADMIN").build())
return manager
}
@Bean
@Order(1) <2>
open fun approvalsSecurityFilterChain(http: HttpSecurity): SecurityFilterChain {
val approvalsPaths = arrayOf("/accounts/approvals/**", "/loans/approvals/**", "/credit-cards/approvals/**")
http {
securityMatcher(approvalsPaths)
authorizeHttpRequests {
authorize(anyRequest, hasRole("ADMIN"))
}
httpBasic { }
}
return http.build()
}
@Bean
@Order(2) <3>
open fun bankingSecurityFilterChain(http: HttpSecurity): SecurityFilterChain {
val bankingPaths = arrayOf("/accounts/**", "/loans/**", "/credit-cards/**", "/balances/**")
val viewBalancePaths = arrayOf("/balances/**")
http {
securityMatcher(bankingPaths)
authorizeHttpRequests {
authorize(viewBalancePaths, hasRole("VIEW_BALANCE"))
authorize(anyRequest, hasRole("USER"))
}
}
return http.build()
}
@Bean <4>
open fun defaultSecurityFilterChain(http: HttpSecurity): SecurityFilterChain {
val allowedPaths = arrayOf("/", "/user-login", "/user-logout", "/notices", "/contact", "/register")
http {
authorizeHttpRequests {
authorize(allowedPaths, permitAll)
authorize(anyRequest, authenticated)
}
formLogin {
loginPage = "/user-login"
loginProcessingUrl = "/user-login"
}
logout {
logoutUrl = "/user-logout"
logoutSuccessUrl = "/?logout"
}
}
return http.build()
}
}
----
<1> Begin by configuring authentication settings.
<2> Define a `SecurityFilterChain` instance with `@Order(1)`, which means that this filter chain will have the highest priority.
This filter chain applies only to requests that begin with `/accounts/approvals/`, `/loans/approvals/` or `/credit-cards/approvals/`.
Requests to this filter chain require the `ROLE_ADMIN` authority and allow HTTP Basic Authentication.
<3> Next, create another `SecurityFilterChain` instance with `@Order(2)` which will be considered second.
This filter chain applies only to requests that begin with `/accounts/`, `/loans/`, `/credit-cards/`, or `/balances/`.
Notice that because this filter chain is second, any requests that include `/approvals/` will match the previous filter chain and will *not* be matched by this filter chain.
Requests to this filter chain require the `ROLE_USER` authority.
This filter chain does not define any authentication because the next (default) filter chain contains that configuration.
<4> Lastly, create an additional `SecurityFilterChain` instance without an `@Order` annotation.
This configuration will handle requests not covered by the other filter chains and will be processed last (no `@Order` defaults to last).
Requests that match `/`, `/user-login`, `/user-logout`, `/notices`, `/contact` and `/register` allow access without authentication.
Any other requests require the user to be authenticated to access any URL not explicitly allowed or protected by other filter chains.
@@ -79,20 +79,20 @@ To learn more about CSRF protection for your application, consider the following
[[csrf-components]]
== Understanding CSRF Protection's Components
CSRF protection is provided by several components that are composed within the {security-api-url}org/springframework/security/web/csrf/CsrfFilter.html[`CsrfFilter`]:
CSRF protection is provided by several components that are composed within the javadoc:org.springframework.security.web.csrf.CsrfFilter[]:
.`CsrfFilter` Components
image::{figures}/csrf.png[]
CSRF protection is divided into two parts:
1. Make the {security-api-url}org/springframework/security/web/csrf/CsrfToken.html[`CsrfToken`] available to the application by delegating to the <<csrf-token-request-handler,`CsrfTokenRequestHandler`>>.
1. Make the javadoc:org.springframework.security.web.csrf.CsrfToken[] available to the application by delegating to the <<csrf-token-request-handler,`CsrfTokenRequestHandler`>>.
2. Determine if the request requires CSRF protection, load and validate the token, and <<csrf-access-denied-handler,handle `AccessDeniedException`>>.
.`CsrfFilter` Processing
image::{figures}/csrf-processing.png[]
* image:{icondir}/number_1.png[] First, the {security-api-url}org/springframework/security/web/csrf/DeferredCsrfToken.html[`DeferredCsrfToken`] is loaded, which holds a reference to the <<csrf-token-repository,`CsrfTokenRepository`>> so that the persisted `CsrfToken` can be loaded later (in image:{icondir}/number_4.png[]).
* image:{icondir}/number_1.png[] First, the javadoc:org.springframework.security.web.csrf.DeferredCsrfToken[] is loaded, which holds a reference to the <<csrf-token-repository,`CsrfTokenRepository`>> so that the persisted `CsrfToken` can be loaded later (in image:{icondir}/number_4.png[]).
* image:{icondir}/number_2.png[] Second, a `Supplier<CsrfToken>` (created from `DeferredCsrfToken`) is given to the <<csrf-token-request-handler,`CsrfTokenRequestHandler`>>, which is responsible for populating a request attribute to make the `CsrfToken` available to the rest of the application.
* image:{icondir}/number_3.png[] Next, the main CSRF protection processing begins and checks if the current request requires CSRF protection. If not required, the filter chain is continued and processing ends.
* image:{icondir}/number_4.png[] If CSRF protection is required, the persisted `CsrfToken` is finally loaded from the `DeferredCsrfToken`.
@@ -128,7 +128,7 @@ You can also specify <<csrf-token-repository-custom,your own implementation>> to
[[csrf-token-repository-httpsession]]
=== Using the `HttpSessionCsrfTokenRepository`
By default, Spring Security stores the expected CSRF token in the `HttpSession` by using {security-api-url}org/springframework/security/web/csrf/HttpSessionCsrfTokenRepository.html[`HttpSessionCsrfTokenRepository`], so no additional code is necessary.
By default, Spring Security stores the expected CSRF token in the `HttpSession` by using javadoc:org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository[], so no additional code is necessary.
The `HttpSessionCsrfTokenRepository` reads the token from an HTTP request header named `X-CSRF-TOKEN` or the request parameter `_csrf` by default.
@@ -197,7 +197,7 @@ XML::
[[csrf-token-repository-cookie]]
=== Using the `CookieCsrfTokenRepository`
You can persist the `CsrfToken` in a cookie to <<csrf-integration-javascript,support a JavaScript-based application>> using the {security-api-url}org/springframework/security/web/csrf/CookieCsrfTokenRepository.html[`CookieCsrfTokenRepository`].
You can persist the `CsrfToken` in a cookie to <<csrf-integration-javascript,support a JavaScript-based application>> using the javadoc:org.springframework.security.web.csrf.CookieCsrfTokenRepository[].
The `CookieCsrfTokenRepository` writes to a cookie named `XSRF-TOKEN` and reads it from an HTTP request header named `X-XSRF-TOKEN` or the request parameter `_csrf` by default.
These defaults come from Angular and its predecessor https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection[AngularJS].
@@ -280,7 +280,7 @@ If you do not need the ability to read the cookie with JavaScript directly, we _
[[csrf-token-repository-custom]]
=== Customizing the `CsrfTokenRepository`
There can be cases where you want to implement a custom {security-api-url}org/springframework/security/web/csrf/CsrfTokenRepository.html[`CsrfTokenRepository`].
There can be cases where you want to implement a custom javadoc:org.springframework.security.web.csrf.CsrfTokenRepository[].
Once you've implemented the `CsrfTokenRepository` interface, you can configure Spring Security to use it with the following configuration:
@@ -708,7 +708,7 @@ The following view technologies automatically include the actual CSRF token in a
* https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-view-jsp-formtaglib[Springs form tag library]
* https://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html#integration-with-requestdatavalueprocessor[Thymeleaf]
* Any other view technology that integrates with {spring-framework-api-url}org/springframework/web/servlet/support/RequestDataValueProcessor.html[`RequestDataValueProcessor`] (via {security-api-url}org/springframework/security/web/servlet/support/csrf/CsrfRequestDataValueProcessor.html[`CsrfRequestDataValueProcessor`])
* Any other view technology that integrates with {spring-framework-api-url}org/springframework/web/servlet/support/RequestDataValueProcessor.html[`RequestDataValueProcessor`] (via javadoc:org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor[])
* You can also include the token yourself via the xref:servlet/integrations/jsp-taglibs.adoc#taglibs-csrfinput[csrfInput] tag
If these options are not available, you can take advantage of the fact that the `CsrfToken` is exposed as an <<csrf-token-request-handler,`HttpServletRequest` attribute named `_csrf`>>.
@@ -763,7 +763,7 @@ Spring Security defers loading a new CSRF token by default, and additional work
[NOTE]
====
Refreshing the token after authentication success and logout success is required because the {security-api-url}org/springframework/security/web/csrf/CsrfAuthenticationStrategy.html[`CsrfAuthenticationStrategy`] and {security-api-url}org/springframework/security/web/csrf/CsrfLogoutHandler.html[`CsrfLogoutHandler`] will clear the previous token.
Refreshing the token after authentication success and logout success is required because the javadoc:org.springframework.security.web.csrf.CsrfAuthenticationStrategy[] and javadoc:org.springframework.security.web.csrf.CsrfLogoutHandler[] will clear the previous token.
The client application will not be able to perform an unsafe HTTP request, such as a POST, without obtaining a fresh token.
====
@@ -788,14 +788,14 @@ public class SecurityConfig {
.csrf((csrf) -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) // <1>
.csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler()) // <2>
)
.addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class); // <3>
);
return http.build();
}
}
final class SpaCsrfTokenRequestHandler extends CsrfTokenRequestAttributeHandler {
private final CsrfTokenRequestHandler delegate = new XorCsrfTokenRequestAttributeHandler();
final class SpaCsrfTokenRequestHandler implements CsrfTokenRequestHandler {
private final CsrfTokenRequestHandler plain = new CsrfTokenRequestAttributeHandler();
private final CsrfTokenRequestHandler xor = new XorCsrfTokenRequestAttributeHandler();
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, Supplier<CsrfToken> csrfToken) {
@@ -803,40 +803,28 @@ final class SpaCsrfTokenRequestHandler extends CsrfTokenRequestAttributeHandler
* Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of
* the CsrfToken when it is rendered in the response body.
*/
this.delegate.handle(request, response, csrfToken);
this.xor.handle(request, response, csrfToken);
/*
* Render the token value to a cookie by causing the deferred token to be loaded.
*/
csrfToken.get();
}
@Override
public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
String headerValue = request.getHeader(csrfToken.getHeaderName());
/*
* If the request contains a request header, use CsrfTokenRequestAttributeHandler
* to resolve the CsrfToken. This applies when a single-page application includes
* the header value automatically, which was obtained via a cookie containing the
* raw CsrfToken.
*/
if (StringUtils.hasText(request.getHeader(csrfToken.getHeaderName()))) {
return super.resolveCsrfTokenValue(request, csrfToken);
}
/*
*
* In all other cases (e.g. if the request contains a request parameter), use
* XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies
* when a server-side rendered form includes the _csrf request parameter as a
* hidden input.
*/
return this.delegate.resolveCsrfTokenValue(request, csrfToken);
}
}
final class CsrfCookieFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrfToken = (CsrfToken) request.getAttribute("_csrf");
// Render the token value to a cookie by causing the deferred token to be loaded
csrfToken.getToken();
filterChain.doFilter(request, response);
return (StringUtils.hasText(headerValue) ? this.plain : this.xor).resolveCsrfTokenValue(request, csrfToken);
}
}
----
@@ -856,35 +844,40 @@ class SecurityConfig {
http {
// ...
csrf {
csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse() // <1>
csrfTokenRequestHandler = SpaCsrfTokenRequestHandler() // <2>
csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse() // <1>
csrfTokenRequestHandler = SpaCsrfTokenRequestHandler() // <2>
}
}
http.addFilterAfter(CsrfCookieFilter(), BasicAuthenticationFilter::class.java) // <3>
return http.build()
}
}
class SpaCsrfTokenRequestHandler : CsrfTokenRequestAttributeHandler() {
private val delegate: CsrfTokenRequestHandler = XorCsrfTokenRequestAttributeHandler()
class SpaCsrfTokenRequestHandler : CsrfTokenRequestHandler {
private val plain: CsrfTokenRequestHandler = CsrfTokenRequestAttributeHandler()
private val xor: CsrfTokenRequestHandler = XorCsrfTokenRequestAttributeHandler()
override fun handle(request: HttpServletRequest, response: HttpServletResponse, csrfToken: Supplier<CsrfToken>) {
/*
* Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of
* the CsrfToken when it is rendered in the response body.
*/
delegate.handle(request, response, csrfToken)
xor.handle(request, response, csrfToken)
/*
* Render the token value to a cookie by causing the deferred token to be loaded.
*/
csrfToken.get()
}
override fun resolveCsrfTokenValue(request: HttpServletRequest, csrfToken: CsrfToken): String? {
val headerValue = request.getHeader(csrfToken.headerName)
/*
* If the request contains a request header, use CsrfTokenRequestAttributeHandler
* to resolve the CsrfToken. This applies when a single-page application includes
* the header value automatically, which was obtained via a cookie containing the
* raw CsrfToken.
*/
return if (StringUtils.hasText(request.getHeader(csrfToken.headerName))) {
super.resolveCsrfTokenValue(request, csrfToken)
return if (StringUtils.hasText(headerValue)) {
plain
} else {
/*
* In all other cases (e.g. if the request contains a request parameter), use
@@ -892,19 +885,8 @@ class SpaCsrfTokenRequestHandler : CsrfTokenRequestAttributeHandler() {
* when a server-side rendered form includes the _csrf request parameter as a
* hidden input.
*/
delegate.resolveCsrfTokenValue(request, csrfToken)
}
}
}
class CsrfCookieFilter : OncePerRequestFilter() {
@Throws(ServletException::class, IOException::class)
override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain) {
val csrfToken = request.getAttribute("_csrf") as CsrfToken
// Render the token value to a cookie by causing the deferred token to be loaded
csrfToken.token
filterChain.doFilter(request, response)
xor
}.resolveCsrfTokenValue(request, csrfToken)
}
}
----
@@ -916,23 +898,20 @@ XML::
<http>
<!-- ... -->
<csrf
token-repository-ref="tokenRepository" <1>
request-handler-ref="requestHandler"/> <2>
<custom-filter ref="csrfCookieFilter" after="BASIC_AUTH_FILTER"/> <3>
token-repository-ref="tokenRepository" <1>
request-handler-ref="requestHandler"/> <2>
</http>
<b:bean id="tokenRepository"
class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
p:cookieHttpOnly="false"/>
<b:bean id="requestHandler"
class="example.SpaCsrfTokenRequestHandler"/>
<b:bean id="csrfCookieFilter"
class="example.CsrfCookieFilter"/>
----
======
<1> Configure `CookieCsrfTokenRepository` with `HttpOnly` set to `false` so the cookie can be read by the JavaScript application.
<2> Configure a custom `CsrfTokenRequestHandler` that resolves the CSRF token based on whether it is an HTTP request header (`X-XSRF-TOKEN`) or request parameter (`_csrf`).
<3> Configure a custom `Filter` to load the `CsrfToken` on every request, which will return a new cookie if needed.
This implementation also causes the deferred `CsrfToken` to be loaded on every request, which will return a new cookie if needed.
[[csrf-integration-javascript-mpa]]
==== Multi-Page Applications
@@ -1109,7 +1088,7 @@ This endpoint should be called to obtain a CSRF token when the application is la
[NOTE]
====
Refreshing the token after authentication success and logout success is required because the {security-api-url}org/springframework/security/web/csrf/CsrfAuthenticationStrategy.html[`CsrfAuthenticationStrategy`] and {security-api-url}org/springframework/security/web/csrf/CsrfLogoutHandler.html[`CsrfLogoutHandler`] will clear the previous token.
Refreshing the token after authentication success and logout success is required because the javadoc:org.springframework.security.web.csrf.CsrfAuthenticationStrategy[] and javadoc:org.springframework.security.web.csrf.CsrfLogoutHandler[] will clear the previous token.
The client application will not be able to perform an unsafe HTTP request, such as a POST, without obtaining a fresh token.
====
@@ -28,7 +28,7 @@ The strategy is implemented in the class `AntPathRequestMatcher`, which uses Spr
If you need a more powerful matching strategy, you can use regular expressions.
The strategy implementation is then `RegexRequestMatcher`.
See the {security-api-url}/org/springframework/security/web/util/matcher/RegexRequestMatcher.html[Javadoc for this class] for more information.
See the javadoc:org.springframework.security.web.util.matcher.RegexRequestMatcher[] Javadoc for more information.
In practice, we recommend that you use method security at your service layer, to control access to your application, rather than rely entirely on the use of security constraints defined at the web-application level.
URLs change, and it is difficult to take into account all the possible URLs that an application might support and how requests might be manipulated.
@@ -153,12 +153,12 @@ This means that we are running our `Runnable` with the same user that was used t
See the {security-api-url}index.html[Javadoc] for additional integrations with both the Java concurrent APIs and the Spring Task abstractions.
They are self-explanatory once you understand the previous code.
* {security-api-url}org/springframework/security/concurrent/DelegatingSecurityContextCallable.html[`DelegatingSecurityContextCallable`]
* {security-api-url}org/springframework/security/concurrent/DelegatingSecurityContextExecutor.html[`DelegatingSecurityContextExecutor`]
* {security-api-url}org/springframework/security/concurrent/DelegatingSecurityContextExecutorService.html[`DelegatingSecurityContextExecutorService`]
* {security-api-url}org/springframework/security/concurrent/DelegatingSecurityContextRunnable.html[`DelegatingSecurityContextRunnable`]
* {security-api-url}org/springframework/security/concurrent/DelegatingSecurityContextScheduledExecutorService.html[`DelegatingSecurityContextScheduledExecutorService`]
* {security-api-url}org/springframework/security/scheduling/DelegatingSecurityContextSchedulingTaskExecutor.html[`DelegatingSecurityContextSchedulingTaskExecutor`]
* {security-api-url}org/springframework/security/task/DelegatingSecurityContextAsyncTaskExecutor.html[`DelegatingSecurityContextAsyncTaskExecutor`]
* {security-api-url}org/springframework/security/task/DelegatingSecurityContextTaskExecutor.html[`DelegatingSecurityContextTaskExecutor`]
* {security-api-url}org/springframework/security/scheduling/DelegatingSecurityContextTaskScheduler.html[`DelegatingSecurityContextTaskScheduler`]
* javadoc:org.springframework.security.concurrent.DelegatingSecurityContextCallable[]
* javadoc:org.springframework.security.concurrent.DelegatingSecurityContextExecutor[]
* javadoc:org.springframework.security.concurrent.DelegatingSecurityContextExecutorService[]
* javadoc:org.springframework.security.concurrent.DelegatingSecurityContextRunnable[]
* javadoc:org.springframework.security.concurrent.DelegatingSecurityContextScheduledExecutorService[]
* javadoc:org.springframework.security.scheduling.DelegatingSecurityContextSchedulingTaskExecutor[]
* javadoc:org.springframework.security.task.DelegatingSecurityContextAsyncTaskExecutor[]
* javadoc:org.springframework.security.task.DelegatingSecurityContextTaskExecutor[]
* javadoc:org.springframework.security.scheduling.DelegatingSecurityContextTaskScheduler[]
@@ -23,8 +23,8 @@ String json = mapper.writeValueAsString(context);
====
The following Spring Security modules provide Jackson support:
- spring-security-core ({security-api-url}org/springframework/security/jackson2/CoreJackson2Module.html[`CoreJackson2Module`])
- spring-security-web ({security-api-url}org/springframework/security/web/jackson2/WebJackson2Module.html[`WebJackson2Module`], {security-api-url}org/springframework/security/web/jackson2/WebServletJackson2Module.html[`WebServletJackson2Module`], {security-api-url}org/springframework/security/web/server/jackson2/WebServerJackson2Module.html[`WebServerJackson2Module`])
- <<oauth2client, spring-security-oauth2-client>> ({security-api-url}org/springframework/security/oauth2/client/jackson2/OAuth2ClientJackson2Module.html[`OAuth2ClientJackson2Module`])
- spring-security-cas ({security-api-url}org/springframework/security/cas/jackson2/CasJackson2Module.html[`CasJackson2Module`])
- spring-security-core (javadoc:org.springframework.security.jackson2.CoreJackson2Module[])
- spring-security-web (javadoc:org.springframework.security.web.jackson2.WebJackson2Module[], javadoc:org.springframework.security.web.jackson2.WebServletJackson2Module[], javadoc:org.springframework.security.web.server.jackson2.WebServerJackson2Module[])
- <<oauth2client, spring-security-oauth2-client>> (javadoc:org.springframework.security.oauth2.client.jackson2.OAuth2ClientJackson2Module[])
- spring-security-cas (javadoc:org.springframework.security.cas.jackson2.CasJackson2Module[])
====
@@ -105,7 +105,7 @@ Xml::
== Bearer Token Propagation
Now that your resource server has validated the token, it might be handy to pass it to downstream services.
This is quite simple with `{security-api-url}org/springframework/security/oauth2/server/resource/web/reactive/function/client/ServletBearerExchangeFilterFunction.html[ServletBearerExchangeFilterFunction]`, which you can see in the following example:
This is quite simple with javadoc:org.springframework.security.oauth2.server.resource.web.reactive.function.client.ServletBearerExchangeFilterFunction[], which you can see in the following example:
[tabs]
======
@@ -134,7 +134,7 @@ fun rest(): WebClient {
----
======
When the above `WebClient` is used to perform requests, Spring Security will look up the current `Authentication` and extract any `{security-api-url}org/springframework/security/oauth2/core/AbstractOAuth2Token.html[AbstractOAuth2Token]` credential.
When the above `WebClient` is used to perform requests, Spring Security will look up the current `Authentication` and extract any javadoc:org.springframework.security.oauth2.core.AbstractOAuth2Token[] credential.
Then, it will propagate that token in the `Authorization` header.
For example:
@@ -198,7 +198,7 @@ this.rest.get()
In this case, the filter will fall back and simply forward the request onto the rest of the web filter chain.
[NOTE]
Unlike the {security-api-url}org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.html[OAuth 2.0 Client filter function], this filter function makes no attempt to renew the token, should it be expired.
Unlike the javadoc:org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction[OAuth 2.0 Client filter function], this filter function makes no attempt to renew the token, should it be expired.
To obtain this level of support, please use the OAuth 2.0 Client filter.
=== `RestTemplate` support
@@ -259,7 +259,7 @@ fun rest(): RestTemplate {
[NOTE]
Unlike the {security-api-url}org/springframework/security/oauth2/client/OAuth2AuthorizedClientManager.html[OAuth 2.0 Authorized Client Manager], this filter interceptor makes no attempt to renew the token, should it be expired.
Unlike the javadoc:org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager[OAuth 2.0 Authorized Client Manager], this filter interceptor makes no attempt to renew the token, should it be expired.
To obtain this level of support, please create an interceptor using the xref:servlet/oauth2/client/index.adoc#oauth2client[OAuth 2.0 Authorized Client Manager].
[[oauth2resourceserver-bearertoken-failure]]
@@ -30,7 +30,7 @@ image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to
image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-http-requests.adoc[`AuthorizationFilter`] indicates that the unauthenticated request is _Denied_ by throwing an `AccessDeniedException`.
image:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates _Start Authentication_.
The configured xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of {security-api-url}org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthenticationEntryPoint.html[`BearerTokenAuthenticationEntryPoint`], which sends a `WWW-Authenticate` header.
The configured xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of javadoc:org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint[], which sends a `WWW-Authenticate` header.
The `RequestCache` is typically a `NullRequestCache` that does not save the request, since the client is capable of replaying the requests it originally requested.
When a client receives the `WWW-Authenticate: Bearer` header, it knows it should retry with a bearer token.
@@ -86,7 +86,7 @@ From here, consider jumping to:
Next, let's see the architectural components that Spring Security uses to support https://tools.ietf.org/html/rfc7519[JWT] Authentication in servlet-based applications, like the one we just saw.
{security-api-url}org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationProvider.html[`JwtAuthenticationProvider`] is an xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationprovider[`AuthenticationProvider`] implementation that leverages a <<oauth2resourceserver-jwt-decoder,`JwtDecoder`>> and <<oauth2resourceserver-jwt-authorization-extraction,`JwtAuthenticationConverter`>> to authenticate a JWT.
javadoc:org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider[] is an xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationprovider[`AuthenticationProvider`] implementation that leverages a <<oauth2resourceserver-jwt-decoder,`JwtDecoder`>> and <<oauth2resourceserver-jwt-authorization-extraction,`JwtAuthenticationConverter`>> to authenticate a JWT.
Let's take a look at how `JwtAuthenticationProvider` works within Spring Security.
The figure explains details of how the xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationmanager[`AuthenticationManager`] in figures from <<oauth2resourceserver-authentication-bearertokenauthenticationfilter,Reading the Bearer Token>> works.
@@ -293,7 +293,7 @@ fun jwtDecoder(): JwtDecoder {
======
[NOTE]
Calling `{security-api-url}org/springframework/security/oauth2/jwt/JwtDecoders.html#fromIssuerLocation-java.lang.String-[JwtDecoders#fromIssuerLocation]` is what invokes the Provider Configuration or Authorization Server Metadata endpoint in order to derive the JWK Set Uri.
Calling javadoc:org.springframework.security.oauth2.jwt.JwtDecoders#fromIssuerLocation-java.lang.String-[JwtDecoders#fromIssuerLocation] is what invokes the Provider Configuration or Authorization Server Metadata endpoint in order to derive the JWK Set Uri.
If the application doesn't expose a `JwtDecoder` bean, then Spring Boot will expose the above default one.
@@ -879,7 +879,9 @@ public class DirectlyConfiguredJwkSetUri {
.requestMatchers("/messages/**").access(hasScope("messages"))
.anyRequest().authenticated()
)
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(Customizer.withDefaults())
);
return http.build();
}
}
@@ -419,7 +419,7 @@ Java::
----
@Bean
JwtDecoder jwtDecoder(JWTProcessor jwtProcessor, OAuth2TokenValidator<Jwt> jwtValidator) {
NimbusJwtDecoder decoder = new NimbusJwtDecoder(processor);
NimbusJwtDecoder decoder = new NimbusJwtDecoder(jwtProcessor);
OAuth2TokenValidator<Jwt> validator = new DelegatingOAuth2TokenValidator<>
(JwtValidators.createDefault(), jwtValidator);
decoder.setJwtValidator(validator);
@@ -68,7 +68,7 @@ Given an Opaque Token, Resource Server will
2. Inspect the response for an `{ 'active' : true }` attribute
3. Map each scope to an authority with the prefix `SCOPE_`
The resulting `Authentication#getPrincipal`, by default, is a Spring Security `{security-api-url}org/springframework/security/oauth2/core/OAuth2AuthenticatedPrincipal.html[OAuth2AuthenticatedPrincipal]` object, and `Authentication#getName` maps to the token's `sub` property, if one is present.
The resulting `Authentication#getPrincipal`, by default, is a Spring Security javadoc:org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal[] object, and `Authentication#getName` maps to the token's `sub` property, if one is present.
From here, you may want to jump to:
@@ -82,7 +82,7 @@ From here, you may want to jump to:
Next, let's see the architectural components that Spring Security uses to support https://tools.ietf.org/html/rfc7662[opaque token] Authentication in servlet-based applications, like the one we just saw.
{security-api-url}org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProvider.html[`OpaqueTokenAuthenticationProvider`] is an xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationprovider[`AuthenticationProvider`] implementation that leverages a <<oauth2resourceserver-opaque-introspector,`OpaqueTokenIntrospector`>> to authenticate an opaque token.
javadoc:org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenAuthenticationProvider[] is an xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationprovider[`AuthenticationProvider`] implementation that leverages a <<oauth2resourceserver-opaque-introspector,`OpaqueTokenIntrospector`>> to authenticate an opaque token.
Let's take a look at how `OpaqueTokenAuthenticationProvider` works within Spring Security.
The figure explains details of how the xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationmanager[`AuthenticationManager`] in figures from <<oauth2resourceserver-authentication-bearertokenauthenticationfilter,Reading the Bearer Token>> works.
@@ -204,7 +204,9 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::opaqueToken);
.oauth2ResourceServer(oauth2 -> oauth2
.opaqueToken(Customizer.withDefaults())
);
return http.build();
}
----
@@ -564,7 +566,9 @@ public class MappedAuthorities {
.requestMatchers("/messages/**").access(hasScope("messages"))
.anyRequest().authenticated()
)
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::opaqueToken);
.oauth2ResourceServer(oauth2 -> oauth2
.opaqueToken(Customizer.withDefaults())
);
return http.build();
}
}
@@ -4,7 +4,7 @@
As stated earlier, Spring Security's SAML 2.0 support produces a `<saml2:AuthnRequest>` to commence authentication with the asserting party.
Spring Security achieves this in part by registering the `Saml2WebSsoAuthenticationRequestFilter` in the filter chain.
This filter by default responds to endpoint `+/saml2/authenticate/{registrationId}+`.
This filter by default responds to the endpoints `+/saml2/authenticate/{registrationId}+` and `+/saml2/authenticate?registrationId={registrationId}+`.
For example, if you were deployed to `https://rp.example.com` and you gave your registration an ID of `okta`, you could navigate to:
@@ -12,6 +12,42 @@ For example, if you were deployed to `https://rp.example.com` and you gave your
and the result would be a redirect that included a `SAMLRequest` parameter containing the signed, deflated, and encoded `<saml2:AuthnRequest>`.
== Configuring the `<saml2:AuthnRequest>` Endpoint
To configure the endpoint differently from the default, you can set the value in `saml2Login`:
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
SecurityFilterChain filterChain(HttpSecurity http) {
http
.saml2Login((saml2) -> saml2
.authenticationRequestUriQuery("/custom/auth/sso?peerEntityID={registrationId}")
);
return new CustomSaml2AuthenticationRequestRepository();
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
saml2Login {
authenticationRequestUriQuery = "/custom/auth/sso?peerEntityID={registrationId}"
}
}
return CustomSaml2AuthenticationRequestRepository()
}
----
======
[[servlet-saml2login-store-authn-request]]
== Changing How the `<saml2:AuthnRequest>` Gets Stored
@@ -19,7 +19,7 @@ image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to
image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-http-requests.adoc[`AuthorizationFilter`] indicates that the unauthenticated request is _Denied_ by throwing an `AccessDeniedException`.
image:{icondir}/number_3.png[] Since the user lacks authorization, the xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates _Start Authentication_.
The configured xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of {security-api-url}org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.html[`LoginUrlAuthenticationEntryPoint`], which redirects to <<servlet-saml2login-sp-initiated-factory,the `<saml2:AuthnRequest>` generating endpoint>>, `Saml2WebSsoAuthenticationRequestFilter`.
The configured xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of javadoc:org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint[], which redirects to <<servlet-saml2login-sp-initiated-factory,the `<saml2:AuthnRequest>` generating endpoint>>, `Saml2WebSsoAuthenticationRequestFilter`.
Alternatively, if you have <<servlet-saml2login-relyingpartyregistrationrepository,configured more than one asserting party>>, it first redirects to a picker page.
image:{icondir}/number_4.png[] Next, the `Saml2WebSsoAuthenticationRequestFilter` creates, signs, serializes, and encodes a `<saml2:AuthnRequest>` using its configured <<servlet-saml2login-sp-initiated-factory,`Saml2AuthenticationRequestFactory`>>.
@@ -418,7 +418,7 @@ class MyCustomSecurityConfiguration {
The preceding example requires the role of `USER` for any URL that starts with `/messages/`.
[[servlet-saml2login-relyingpartyregistrationrepository]]
The second `@Bean` Spring Boot creates is a {security-api-url}org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationRepository.html[`RelyingPartyRegistrationRepository`], which represents the asserting party and relying party metadata.
The second `@Bean` Spring Boot creates is a javadoc:org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository[], which represents the asserting party and relying party metadata.
This includes such things as the location of the SSO endpoint the relying party should use when requesting authentication from the asserting party.
You can override the default by publishing your own `RelyingPartyRegistrationRepository` bean.
@@ -588,9 +588,60 @@ class MyCustomSecurityConfiguration {
A relying party can be multi-tenant by registering more than one relying party in the `RelyingPartyRegistrationRepository`.
====
[[servlet-saml2login-relyingpartyregistrationrepository-caching]]
If you want your metadata to be refreshable on a periodic basis, you can wrap your repository in `CachingRelyingPartyRegistrationRepository` like so:
.Caching Relying Party Registration Repository
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@EnableWebSecurity
public class MyCustomSecurityConfiguration {
@Bean
public RelyingPartyRegistrationRepository registrations(CacheManager cacheManager) {
Supplier<IterableRelyingPartyRegistrationRepository> delegate = () ->
new InMemoryRelyingPartyRegistrationRepository(RelyingPartyRegistrations
.fromMetadataLocation("https://idp.example.org/ap/metadata")
.registrationId("ap").build());
CachingRelyingPartyRegistrationRepository registrations =
new CachingRelyingPartyRegistrationRepository(delegate);
registrations.setCache(cacheManager.getCache("my-cache-name"));
return registrations;
}
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebSecurity
class MyCustomSecurityConfiguration {
@Bean
fun registrations(cacheManager: CacheManager): RelyingPartyRegistrationRepository {
val delegate = Supplier<IterableRelyingPartyRegistrationRepository> {
InMemoryRelyingPartyRegistrationRepository(RelyingPartyRegistrations
.fromMetadataLocation("https://idp.example.org/ap/metadata")
.registrationId("ap").build())
}
val registrations = CachingRelyingPartyRegistrationRepository(delegate)
registrations.setCache(cacheManager.getCache("my-cache-name"))
return registrations
}
}
----
======
In this way, the set of `RelyingPartyRegistration`s will refresh based on {spring-framework-reference-url}integration/cache/store-configuration.html[the cache's eviction schedule].
[[servlet-saml2login-relyingpartyregistration]]
== RelyingPartyRegistration
A {security-api-url}org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistration.html[`RelyingPartyRegistration`]
A javadoc:org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration[]
instance represents a link between an relying party and an asserting party's metadata.
In a `RelyingPartyRegistration`, you can provide relying party metadata like its `Issuer` value, where it expects SAML Responses to be sent to, and any credentials that it owns for the purposes of signing or decrypting payloads.
@@ -189,28 +189,28 @@ Next, let's see the architectural components that Spring Security uses to suppor
For RP-initiated logout:
image:{icondir}/number_1.png[] Spring Security executes its xref:servlet/authentication/logout.adoc#logout-architecture[logout flow], calling its ``LogoutHandler``s to invalidate the session and perform other cleanup.
It then invokes the {security-api-url}org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2RelyingPartyInitiatedLogoutSuccessHandler.html[`Saml2RelyingPartyInitiatedLogoutSuccessHandler`].
It then invokes the javadoc:org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2RelyingPartyInitiatedLogoutSuccessHandler[].
image:{icondir}/number_2.png[] The logout success handler uses an instance of
{security-api-url}org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestResolver.html[`Saml2LogoutRequestResolver`] to create, sign, and serialize a `<saml2:LogoutRequest>`.
javadoc:org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestResolver[] to create, sign, and serialize a `<saml2:LogoutRequest>`.
It uses the keys and configuration from the xref:servlet/saml2/login/overview.adoc#servlet-saml2login-relyingpartyregistration[`RelyingPartyRegistration`] that is associated with the current `Saml2AuthenticatedPrincipal`.
Then, it redirect-POSTs the `<saml2:LogoutRequest>` to the asserting party SLO endpoint
The browser hands control over to the asserting party.
If the asserting party redirects back (which it may not), then the application proceeds to step image:{icondir}/number_3.png[].
image:{icondir}/number_3.png[] The {security-api-url}org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutResponseFilter.html[`Saml2LogoutResponseFilter`] deserializes, verifies, and processes the `<saml2:LogoutResponse>` with its {security-api-url}org/springframework/security/saml2/provider/service/authentication/logout/Saml2LogoutResponseValidator.html[`Saml2LogoutResponseValidator`].
image:{icondir}/number_3.png[] The javadoc:org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseFilter[] deserializes, verifies, and processes the `<saml2:LogoutResponse>` with its javadoc:org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponseValidator[].
image:{icondir}/number_4.png[] If valid, then it completes the local logout flow by redirecting to `/login?logout`, or whatever has been configured.
If invalid, then it responds with a 400.
For AP-initiated logout:
image:{icondir}/number_1.png[] The {security-api-url}org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestFilter.html[`Saml2LogoutRequestFilter`] deserializes, verifies, and processes the `<saml2:LogoutRequest>` with its {security-api-url}org/springframework/security/saml2/provider/service/authentication/logout/Saml2LogoutRequestValidator.html[`Saml2LogoutRequestValidator`].
image:{icondir}/number_1.png[] The javadoc:org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter[] deserializes, verifies, and processes the `<saml2:LogoutRequest>` with its javadoc:org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidator[].
image:{icondir}/number_2.png[] If valid, then the filter calls the configured ``LogoutHandler``s, invalidating the session and performing other cleanup.
image:{icondir}/number_3.png[] It uses a {security-api-url}org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutResponseResolver.html[`Saml2LogoutResponseResolver`] to create, sign and serialize a `<saml2:LogoutResponse>`.
image:{icondir}/number_3.png[] It uses a javadoc:org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseResolver[] to create, sign and serialize a `<saml2:LogoutResponse>`.
It uses the keys and configuration from the xref:servlet/saml2/login/overview.adoc#servlet-saml2login-relyingpartyregistration[`RelyingPartyRegistration`] derived from the endpoint or from the contents of the `<saml2:LogoutRequest>`.
Then, it redirect-POSTs the `<saml2:LogoutResponse>` to the asserting party SLO endpoint.
@@ -964,7 +964,7 @@ mvc.get("/endpoint") {
----
======
You can also specify a complete `Jwt`, for which `{security-api-url}org/springframework/security/oauth2/jwt/Jwt.Builder.html[Jwt.Builder]` comes quite handy:
You can also specify a complete `Jwt`, for which javadoc:org.springframework.security.oauth2.jwt.Jwt$Builder[] comes quite handy:
[tabs]
======
@@ -44,7 +44,7 @@ Kotlin::
+
[source,kotlin,role="secondary"]
----
@ExtendWith(SpringExtension.class)
@ExtendWith(SpringExtension::class)
@ContextConfiguration(classes = [SecurityConfig::class])
@WebAppConfiguration
class CsrfShowcaseTests {
@@ -52,7 +52,7 @@ class CsrfShowcaseTests {
@Autowired
private lateinit var context: WebApplicationContext
private var mvc: MockMvc? = null
private lateinit var mvc: MockMvc
@BeforeEach
fun setup() {
+4 -302
View File
@@ -1,307 +1,9 @@
[[new]]
= What's New in Spring Security 6.3
= What's New in Spring Security 6.4
Spring Security 6.3 provides a number of new features.
Spring Security 6.4 provides a number of new features.
Below are the highlights of the release, or you can view https://github.com/spring-projects/spring-security/releases[the release notes] for a detailed listing of each feature and bug fix.
== Passive JDK Serialization Support
- https://github.com/spring-projects/spring-security/issues/4186[gh-4186] - Support `RoleHierarchy` in `AclAuthorizationStrategyImpl`
- https://github.com/spring-projects/spring-security/issues/15136[gh-15136] - Support `RoleHierarchy` Bean in `authorizeHttpRequests` Kotlin DSL
When it comes to its support for JDK-serialized security components, Spring Security has historically been quite aggressive, supporting each serialization version for only one Spring Security minor version.
This meant that if you had JDK-serialized security components, then they would need to be evacuated before upgrading to the next Spring Security version since they would no longer be deserializable.
Now that Spring Security performs a minor release every six months, this became a much larger pain point.
To address that, Spring Security now will https://spring.io/blog/2024/01/19/spring-security-6-3-adds-passive-jdk-serialization-deserialization-for[maintain passivity with JDK serialization], like it does with JSON serialization, making for more seamless upgrades.
== Authorization
An ongoing theme for the last several releases has been to refactor and improve Spring Security's authorization subsystem.
Starting with replacing the `AccessDecisionManager` API with `AuthorizationManager` it's now come to the point where we are able to add several exciting new features.
=== Annotation Parameters - https://github.com/spring-projects/spring-security/issues/14480[#14480]
The first 6.3 feature is https://github.com/spring-projects/spring-security/issues/14480[support for annotation parameters].
Consider Spring Security's support for xref:servlet/authorization/method-security.adoc#meta-annotations[meta-annotations] like this one:
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@PreAuthorize("hasAuthority('SCOPE_message:read')")
public @interface HasMessageRead {}
----
Kotlin::
+
.Kotlin
[source,kotlin,role="secondary"]
----
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@PreAuthorize("hasAuthority('SCOPE_message:read')")
annotation class HasMessageRead
----
======
Before this release, something like this is only helpful when it is used widely across the codebase.
But now, xref:servlet/authorization/method-security.adoc#_templating_meta_annotation_expressions[you can add parameters] like so:
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@PreAuthorize("hasAuthority('SCOPE_{scope}')")
public @interface HasScope {
String scope();
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@PreAuthorize("hasAuthority('SCOPE_{scope}')")
annotation class HasScope (val scope:String)
----
======
making it possible to do things like this:
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@HasScope("message:read")
public String method() { ... }
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@HasScope("message:read")
fun method(): String { ... }
----
======
and apply your SpEL expression in several more places.
=== Secure Return Values - https://github.com/spring-projects/spring-security/issues/14596[#14596], https://github.com/spring-projects/spring-security/issues/14597[#14597]
Since the early days of Spring Security, you've been able to xref:servlet/authorization/method-security.adoc#use-preauthorize[annotate Spring beans with `@PreAuthorize` and `@PostAuthorize`].
But controllers, services, and repositories are not the only things you care to secure.
For example, what about a domain object `Order` where only admins should be able to call the `Order#getPayment` method?
Now in 6.3, https://github.com/spring-projects/spring-security/issues/14597[you can annotate those methods], too.
First, annotate the `getPayment` method like you would a Spring bean:
[tabs]
======
Java::
+
[source,java,role="primary"]
----
public class Order {
@HasScope("payment:read")
Payment getPayment() { ... }
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
class Order {
@HasScope("payment:read")
fun getPayment(): Payment { ... }
}
----
======
And then xref:servlet/authorization/method-security.adoc#authorize-object[annotate your Spring Data repository with `@AuthorizeReturnObject`] like so:
[tabs]
======
Java::
+
[source,java,role="primary"]
----
public interface OrderRepository implements CrudRepository<Order, String> {
@AuthorizeReturnObject
Optional<Order> findOrderById(String id);
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
interface OrderRepository : CrudRepository<Order, String> {
@AuthorizeReturnObject
fun findOrderById(id: String?): Optional<Order?>?
}
----
======
At that point, Spring Security will protect any `Order` returned from `findOrderById` by way of https://github.com/spring-projects/spring-security/issues/14596[proxying the `Order` instance].
=== Error Handling - https://github.com/spring-projects/spring-security/issues/14598[#14598], https://github.com/spring-projects/spring-security/issues/14600[#14600], https://github.com/spring-projects/spring-security/issues/14601[#14601]
In this release, you can also https://github.com/spring-projects/spring-security/issues/14601[intercept and handle failure at the method level] with its last new method security annotation.
When you xref:servlet/authorization/method-security.adoc#fallback-values-authorization-denied[annotate a method with `@HandleAuthorizationDenied`] like so:
[tabs]
======
Java::
+
[source,java,role="primary"]
----
public class Payment {
@HandleAuthorizationDenied(handlerClass=Mask.class)
@PreAuthorize("hasAuthority('card:read')")
public String getCreditCardNumber() { ... }
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
class Payment {
@HandleAuthorizationDenied(handlerClass=Mask.class)
@PreAuthorize("hasAuthority('card:read')")
fun getCreditCardNumber(): String { ... }
}
----
======
and publish a `Mask` bean:
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Component
public class Mask implements MethodAuthorizationDeniedHandler {
@Override
public Object handleDeniedInvocation(MethodInvocation invocation, AuthorizationResult result) {
return "***";
}
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Component
class Mask : MethodAuthorizationDeniedHandler {
fun handleDeniedInvocation(invocation: MethodInvocation?, result: AuthorizationResult?): Any = "***"
}
----
======
then any unauthorized call to `Payment#getCreditCardNumber` will return `\***` instead of the number.
You can see all these features at work together in https://github.com/spring-projects/spring-security-samples/tree/main/servlet/spring-boot/java/data[the latest Spring Security Data sample].
== Compromised Password Checking - https://github.com/spring-projects/spring-security/issues/7395[#7395]
If you are going to let users pick passwords, it's critical to ensure that such a password isn't already compromised.
Spring Security 6.3 makes this as simple as xref:features/authentication/password-storage.adoc#authentication-compromised-password-check[publishing a `CompromisedPasswordChecker` bean]:
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
public CompromisedPasswordChecker compromisedPasswordChecker() {
return new HaveIBeenPwnedRestApiPasswordChecker();
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
fun compromisedPasswordChecker(): CompromisedPasswordChecker = HaveIBeenPwnedRestApiPasswordChecker()
----
======
== `spring-security-rsa` is now part of Spring Security - https://github.com/spring-projects/spring-security/issues/14202[#14202]
Since 2017, Spring Security has been undergoing a long-standing initiative to fold various Spring Security extensions into Spring Security proper.
In 6.3, `spring-security-rsa` becomes the latest of these projects which will help the team maintain and add features to it, long-term.
`spring-security-rsa` provides a number of https://github.com/spring-projects/spring-security/blob/main/crypto/src/main/java/org/springframework/security/crypto/encrypt/RsaSecretEncryptor.java[handy `BytesEncryptor`] https://github.com/spring-projects/spring-security/blob/main/crypto/src/main/java/org/springframework/security/crypto/encrypt/RsaRawEncryptor.java[implementations] as well as https://github.com/spring-projects/spring-security/blob/main/crypto/src/main/java/org/springframework/security/crypto/encrypt/KeyStoreKeyFactory.java[a simpler API for working with ``KeyStore``s].
== OAuth 2.0 Token Exchange Grant - https://github.com/spring-projects/spring-security/issues/5199[#5199]
One of https://github.com/spring-projects/spring-security/issues/5199[the most highly-voted OAuth 2.0 features] in Spring Security is now in place in 6.3, which is the support for https://datatracker.ietf.org/doc/html/rfc8693#section-2[the OAuth 2.0 Token Exchange grant].
For xref:servlet/oauth2/client/authorization-grants.adoc#token-exchange-grant-access-token[any client configured for token exchange], you can activate it in Spring Security by adding a `TokenExchangeAuthorizedClientProvider` instance to your `OAuth2AuthorizedClientManager` like so:
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
public OAuth2AuthorizedClientProvider tokenExchange() {
return new TokenExchangeOAuth2AuthorizedClientProvider();
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
fun tokenExchange(): OAuth2AuthorizedClientProvider = TokenExchangeOAuth2AuthorizedClientProvider()
----
======
and then xref:servlet/oauth2/client/authorized-clients.adoc#oauth2Client-registered-authorized-client[use the `@RegisteredOAuth2AuthorizedClient` annotation] as per usual to retrieve the appropriate token with the expanded privileges your resource server needs.
== Additional Highlights
- https://github.com/spring-projects/spring-security/pull/14655[gh-14655] - Add `DelegatingAuthenticationConverter`
- https://github.com/spring-projects/spring-security/issues/6192[gh-6192] - Add Concurrent Sessions Control on WebFlux (xref:reactive/authentication/concurrent-sessions-control.adoc[docs])
- https://github.com/spring-projects/spring-security/pull/14193[gh-14193] - Added support for CAS Gateway Authentication
- https://github.com/spring-projects/spring-security/issues/13259[gh-13259] - Customize when UserInfo is called
- https://github.com/spring-projects/spring-security/pull/14168[gh-14168] - Introduce Customizable AuthorizationFailureHandler in OAuth2AuthorizationRequestRedirectFilter
- https://github.com/spring-projects/spring-security/issues/14672[gh-14672] - Customize mapping the OidcUser from OidcUserRequest and OidcUserInfo
- https://github.com/spring-projects/spring-security/issues/13763[gh-13763] - Simplify configuration of reactive OAuth2 Client component model
- https://github.com/spring-projects/spring-security/issues/14758[gh-14758] - Update reactive OAuth2 docs landing page with examples (xref:reactive/oauth2/index.adoc[docs])
- https://github.com/spring-projects/spring-security/issues/10538[gh-10538] - Support Certificate-Bound JWT Access Token Validation
- https://github.com/spring-projects/spring-security/pull/14265[gh-14265] - Support Nested username in UserInfo response
- https://github.com/spring-projects/spring-security/pull/14265[gh-14449] - Add `SecurityContext` argument resolver
- https://github.com/spring-projects/spring-security/issues/11440[gh-11440] - Simplify Disabling `application/x-www-form-urlencoded` Encoding Client ID and Secret (xref:servlet/oauth2/client/client-authentication.adoc#_authenticate_using_client_secret_basic[servlet docs], xref:reactive/oauth2/client/client-authentication.adoc#_authenticate_using_client_secret_basic[reactive docs])
And for an exhaustive list, please see the release notes for https://github.com/spring-projects/spring-security/releases/tag/6.3.0-RC1[6.3.0-RC1], https://github.com/spring-projects/spring-security/releases/tag/6.3.0-M3[6.3.0-M3], https://github.com/spring-projects/spring-security/releases/tag/6.3.0-M2[6.3.0-M2], and https://github.com/spring-projects/spring-security/releases/tag/6.3.0-M1[6.3.0-M1].
+12 -1
View File
@@ -15,11 +15,22 @@ antora {
]
}
tasks.register("syncAntoraAttachments", Sync) {
group = 'Documentation'
description = 'Syncs the Antora attachments'
from project.provider( { project.tasks.api.outputs } )
into project.layout.buildDirectory.dir('generated-antora-resources/modules/ROOT/assets/attachments/api/java')
}
tasks.named("generateAntoraYml") {
asciidocAttributes = project.provider( { generateAttributes() } )
asciidocAttributes.putAll(providers.provider( { resolvedVersions(project.configurations.testRuntimeClasspath) }))
}
tasks.register("generateAntoraResources") {
dependsOn 'generateAntoraYml', 'syncAntoraAttachments'
}
dependencies {
testImplementation platform(project(':spring-security-dependencies'))
testImplementation 'com.unboundid:unboundid-ldapsdk'
@@ -44,7 +55,7 @@ def generateAttributes() {
def securityApiUrl = "$securityDocsUrl/api/"
def securityReferenceUrl = "$securityDocsUrl/reference/html5/"
def springFrameworkApiUrl = "https://docs.spring.io/spring-framework/docs/$springFrameworkVersion/javadoc-api/"
def springFrameworkReferenceUrl = "https://docs.spring.io/spring-framework/docs/$springFrameworkVersion/reference/html/"
def springFrameworkReferenceUrl = "https://docs.spring.io/spring-framework/reference/$springFrameworkVersion/"
def springBootReferenceUrl = "https://docs.spring.io/spring-boot/docs/$springBootVersion/reference/html/"
def springBootApiUrl = "https://docs.spring.io/spring-boot/docs/$springBootVersion/api/"

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