1
0
mirror of synced 2026-07-15 15:49:09 +00:00

Compare commits

...

36 Commits

Author SHA1 Message Date
github-actions[bot] d2179e0e78 Release 6.4.10 2025-09-15 16:03:26 +00:00
Josh Cummings e5694ac7b5 Fallback to Object When Determining Overridden Methods
Closes gh-17898
2025-09-15 09:31:50 -06:00
Joe Grandja 9de0aad31b Allow patch version updates for nimbus-jose-jwt
Issue gh-17875
2025-09-15 10:09:59 -04:00
Joe Grandja 7293fc023b Update to nimbus-jose-jwt:9.37.4
Closes gh-17875
2025-09-15 10:09:10 -04:00
Rob Winch ed3ed28b3c Bump org.springframework:spring-framework-bom from 6.2.10 to 6.2.11 2025-09-15 08:54:37 -05:00
Rob Winch 704a324273 Bump org.hibernate.orm:hibernate-core from 6.6.23.Final to 6.6.29.Final 2025-09-15 08:54:35 -05:00
Rob Winch 0fc884f592 Bump org.springframework.data:spring-data-bom from 2024.1.9 to 2024.1.10 2025-09-15 08:54:33 -05:00
Rob Winch 6162e71e18 Bump io.micrometer:micrometer-observation from 1.14.9 to 1.14.11 2025-09-15 08:54:29 -05:00
dependabot[bot] 627de76f67 Bump io.micrometer:micrometer-observation from 1.14.9 to 1.14.11
Bumps [io.micrometer:micrometer-observation](https://github.com/micrometer-metrics/micrometer) from 1.14.9 to 1.14.11.
- [Release notes](https://github.com/micrometer-metrics/micrometer/releases)
- [Commits](https://github.com/micrometer-metrics/micrometer/compare/v1.14.9...v1.14.11)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-15 03:16:34 +00:00
dependabot[bot] 5d55c8d13a Bump org.springframework.data:spring-data-bom from 2024.1.9 to 2024.1.10
Bumps [org.springframework.data:spring-data-bom](https://github.com/spring-projects/spring-data-bom) from 2024.1.9 to 2024.1.10.
- [Release notes](https://github.com/spring-projects/spring-data-bom/releases)
- [Commits](https://github.com/spring-projects/spring-data-bom/compare/2024.1.9...2024.1.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-15 03:16:26 +00:00
dependabot[bot] 342b9c3539 Bump org.hibernate.orm:hibernate-core from 6.6.23.Final to 6.6.29.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.23.Final to 6.6.29.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.29/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.23...6.6.29)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-15 03:16:21 +00:00
dependabot[bot] 537845491e Bump org.springframework:spring-framework-bom from 6.2.10 to 6.2.11
Bumps [org.springframework:spring-framework-bom](https://github.com/spring-projects/spring-framework) from 6.2.10 to 6.2.11.
- [Release notes](https://github.com/spring-projects/spring-framework/releases)
- [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-15 03:15:51 +00:00
Rob Winch e68738dd94 Bump org.hibernate.orm:hibernate-core from 6.6.23.Final to 6.6.28.Final 2025-09-11 22:13:31 -05:00
Rob Winch 9f32f62d34 Bump io.micrometer:micrometer-observation from 1.14.9 to 1.14.11 2025-09-11 22:13:30 -05:00
Rob Winch 9fb4db12a5 Bump org.springframework:spring-framework-bom from 6.2.10 to 6.2.11 2025-09-11 22:13:26 -05:00
dependabot[bot] f493388513 Bump org.springframework:spring-framework-bom from 6.2.10 to 6.2.11
Bumps [org.springframework:spring-framework-bom](https://github.com/spring-projects/spring-framework) from 6.2.10 to 6.2.11.
- [Release notes](https://github.com/spring-projects/spring-framework/releases)
- [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-12 03:12:05 +00:00
Rob Winch a79a2b031a Remove MockWebServer from JwtIssuerAuthenticationManagerResolverTests
This prevents timeouts on GitHub Windows runners due to overtaxed
systems.

Closes gh-17869
2025-09-10 11:56:07 -05:00
dependabot[bot] cc30c901c7 Bump io.micrometer:micrometer-observation from 1.14.9 to 1.14.11
Bumps [io.micrometer:micrometer-observation](https://github.com/micrometer-metrics/micrometer) from 1.14.9 to 1.14.11.
- [Release notes](https://github.com/micrometer-metrics/micrometer/releases)
- [Commits](https://github.com/micrometer-metrics/micrometer/compare/v1.14.9...v1.14.11)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-09 03:13:37 +00:00
dependabot[bot] dd986b0932 Bump org.hibernate.orm:hibernate-core from 6.6.23.Final to 6.6.28.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.23.Final to 6.6.28.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.28/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.23...6.6.28)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-08 03:13:45 +00:00
Rob Winch 8429c23108 Bump io.micrometer:micrometer-observation from 1.14.9 to 1.14.10 2025-09-04 22:38:50 -05:00
Rob Winch 97f3567702 Bump org.hibernate.orm:hibernate-core from 6.6.23.Final to 6.6.28.Final 2025-09-04 22:38:46 -05:00
dependabot[bot] b4fc01194f Bump org.hibernate.orm:hibernate-core from 6.6.23.Final to 6.6.28.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.23.Final to 6.6.28.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.28/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.23...6.6.28)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-02 22:32:54 +00:00
Josh Cummings 7e3bf9662c Polish InteractiveAuthenticationSuccessEvent Sample
The sample better matches a value that would be used in the constructor

Issue gh-16276
2025-08-30 10:00:24 -06:00
Josh Cummings 8b2a453301 Advise Favoring PostAuthorize on Reads
Closes gh-17797
2025-08-22 12:39:51 -06:00
Nikita Konev 894105aab5 Fix traceId discrepancy in case error in servlet web
Signed-off-by: Nikita Konev <nikit.cpp@yandex.ru>
2025-08-22 11:06:37 -06:00
dependabot[bot] 312c2049f3 Bump io.micrometer:micrometer-observation from 1.14.9 to 1.14.10
Bumps [io.micrometer:micrometer-observation](https://github.com/micrometer-metrics/micrometer) from 1.14.9 to 1.14.10.
- [Release notes](https://github.com/micrometer-metrics/micrometer/releases)
- [Commits](https://github.com/micrometer-metrics/micrometer/compare/v1.14.9...v1.14.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-22 04:10:44 +00:00
Rob Winch b9653346a1 Bump org.hibernate.orm:hibernate-core from 6.6.23.Final to 6.6.26.Final 2025-08-21 08:54:19 -05:00
Rob Winch 9e6bcbd1d0 Bump io.micrometer:micrometer-observation from 1.14.9 to 1.14.10 2025-08-21 08:54:18 -05:00
dependabot[bot] 5453224377 Bump io.micrometer:micrometer-observation from 1.14.9 to 1.14.10
Bumps [io.micrometer:micrometer-observation](https://github.com/micrometer-metrics/micrometer) from 1.14.9 to 1.14.10.
- [Release notes](https://github.com/micrometer-metrics/micrometer/releases)
- [Commits](https://github.com/micrometer-metrics/micrometer/compare/v1.14.9...v1.14.10)

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

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

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-20 04:41:59 +00:00
Rob Winch 20a6863e07 Bump org.hibernate.orm:hibernate-core from 6.6.23.Final to 6.6.26.Final 2025-08-19 22:15:40 -05:00
Rob Winch e145c07d5b Bump io.micrometer:micrometer-observation from 1.14.9 to 1.14.10 2025-08-19 22:15:38 -05:00
dependabot[bot] a933089ec2 Bump io.micrometer:micrometer-observation from 1.14.9 to 1.14.10
Bumps [io.micrometer:micrometer-observation](https://github.com/micrometer-metrics/micrometer) from 1.14.9 to 1.14.10.
- [Release notes](https://github.com/micrometer-metrics/micrometer/releases)
- [Commits](https://github.com/micrometer-metrics/micrometer/compare/v1.14.9...v1.14.10)

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

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

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-19 14:40:42 +00:00
github-actions[bot] acaed1ad96 Next development version 2025-08-18 18:15:02 +00:00
github-actions[bot] 01c8cea00f Release 6.4.9 2025-08-18 17:37:33 +00:00
11 changed files with 160 additions and 148 deletions
@@ -83,8 +83,12 @@ public class VerifyDependenciesVersionsPlugin implements Plugin<Project> {
String transitiveNimbusJoseJwtVersion = TransitiveDependencyLookupUtils.lookupJwtVersion(oauth2OidcSdkVersion);
String expectedNimbusJoseJwtVersion = this.getExpectedNimbusJoseJwtVersion().get();
if (!transitiveNimbusJoseJwtVersion.equals(expectedNimbusJoseJwtVersion)) {
String message = String.format("Found transitive nimbus-jose-jwt:%s in oauth2-oidc-sdk:%s, but the project contains a different version of nimbus-jose-jwt [%s]. Please align the versions.", transitiveNimbusJoseJwtVersion, oauth2OidcSdkVersion, expectedNimbusJoseJwtVersion);
throw new VerificationException(message);
String transitiveNimbusJoseJwtMajorMinorVersion = transitiveNimbusJoseJwtVersion.substring(0, transitiveNimbusJoseJwtVersion.lastIndexOf("."));
String expectedNimbusJoseJwtMajorMinorVersion = expectedNimbusJoseJwtVersion.substring(0, expectedNimbusJoseJwtVersion.lastIndexOf("."));
if (!transitiveNimbusJoseJwtMajorMinorVersion.equals(expectedNimbusJoseJwtMajorMinorVersion)) {
String message = String.format("Found transitive nimbus-jose-jwt:%s in oauth2-oidc-sdk:%s, but the project contains a different version of nimbus-jose-jwt [%s]. Please align the major/minor versions.", transitiveNimbusJoseJwtVersion, oauth2OidcSdkVersion, expectedNimbusJoseJwtVersion);
throw new VerificationException(message);
}
}
String message = String.format("Found transitive nimbus-jose-jwt:%s in oauth2-oidc-sdk:%s, the project contains expected version of nimbus-jose-jwt [%s]. Verified all versions align.", transitiveNimbusJoseJwtVersion, oauth2OidcSdkVersion, expectedNimbusJoseJwtVersion);
try {
@@ -182,6 +182,7 @@ import org.springframework.security.saml2.provider.service.registration.OpenSaml
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;
import org.springframework.security.web.PortResolverImpl;
import org.springframework.security.web.authentication.AuthenticationFilter;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException;
@@ -442,7 +443,7 @@ final class SerializationSamples {
generatorByClassName.put(AuthenticationSuccessEvent.class,
(r) -> new AuthenticationSuccessEvent(authentication));
generatorByClassName.put(InteractiveAuthenticationSuccessEvent.class,
(r) -> new InteractiveAuthenticationSuccessEvent(authentication, Authentication.class));
(r) -> new InteractiveAuthenticationSuccessEvent(authentication, AuthenticationFilter.class));
generatorByClassName.put(LogoutSuccessEvent.class, (r) -> new LogoutSuccessEvent(authentication));
generatorByClassName.put(JaasAuthenticationFailedEvent.class,
(r) -> new JaasAuthenticationFailedEvent(authentication, new RuntimeException("message")));
@@ -252,7 +252,7 @@ final class UniqueSecurityAnnotationScanner<A extends Annotation> extends Abstra
}
for (int i = 0; i < rootParameterTypes.length; i++) {
Class<?> resolvedParameterType = ResolvableType.forMethodParameter(candidateMethod, i, sourceDeclaringClass)
.resolve();
.toClass();
if (rootParameterTypes[i] != resolvedParameterType) {
return false;
}
@@ -22,6 +22,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.core.annotation.AnnotationConfigurationException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.ClassUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -275,6 +276,14 @@ public class UniqueSecurityAnnotationScannerTests {
assertThat(pre).isNotNull();
}
// gh-17898
@Test
void scanWhenAnnotationOnParameterizedUndeclaredMethodAndThenLocates() throws Exception {
Method method = ClassUtils.getMethod(GenericInterfaceImpl.class, "processOneAndTwo", Long.class, Object.class);
PreAuthorize pre = this.scanner.scan(method, method.getDeclaringClass());
assertThat(pre).isNotNull();
}
@PreAuthorize("one")
private interface AnnotationOnInterface {
@@ -637,4 +646,27 @@ public class UniqueSecurityAnnotationScannerTests {
}
interface GenericInterface<A, B> {
@PreAuthorize("hasAuthority('thirtythree')")
void processOneAndTwo(A value1, B value2);
}
abstract static class GenericAbstractSuperclass<C> implements GenericInterface<Long, C> {
@Override
public void processOneAndTwo(Long value1, C value2) {
}
}
static class GenericInterfaceImpl extends GenericAbstractSuperclass<String> {
// The compiler does not require us to declare a concrete
// processOneAndTwo(Long, String) method, and we intentionally
// do not declare one here.
}
}
@@ -544,6 +544,14 @@ open class BankService {
The result is that the above method will only return the `Account` if its `owner` attribute matches the logged-in user's `name`.
If not, Spring Security will throw an `AccessDeniedException` and return a 403 status code.
[NOTE]
=====
Note that `@PostAuthorize` is not recommended for classes that perform database writes since that typically means that a database change was made before the security invariants were checked.
A common example of doing this is if you have `@Transactional` and `@PostAuthorize` on the same method.
Instead, read the value first, using `@PostAuthorize` on the read, and then perform the database write, should that read is authorized.
If you must do something like this, you can <<changing-the-order, ensure that `@EnableTransactionManagement` comes before `@EnableMethodSecurity`>>.
=====
[[use-prefilter]]
=== Filtering Method Parameters with `@PreFilter`
@@ -1795,39 +1803,7 @@ As already noted, there is a Spring AOP method interceptor for each annotation,
Namely, the `@PreFilter` method interceptor's order is 100, ``@PreAuthorize``'s is 200, and so on.
The reason this is important to note is that there are other AOP-based annotations like `@EnableTransactionManagement` that have an order of `Integer.MAX_VALUE`.
In other words, they are located at the end of the advisor chain by default.
At times, it can be valuable to have other advice execute before Spring Security.
For example, if you have a method annotated with `@Transactional` and `@PostAuthorize`, you might want the transaction to still be open when `@PostAuthorize` runs so that an `AccessDeniedException` will cause a rollback.
To get `@EnableTransactionManagement` to open a transaction before method authorization advice runs, you can set ``@EnableTransactionManagement``'s order like so:
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@EnableTransactionManagement(order = 0)
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@EnableTransactionManagement(order = 0)
----
Xml::
+
[source,xml,role="secondary"]
----
<tx:annotation-driven ref="txManager" order="0"/>
----
======
Since the earliest method interceptor (`@PreFilter`) is set to an order of 100, a setting of zero means that the transaction advice will run before all Spring Security advice.
You can use the `offset` parameter on `@EnableMethodSecurity` to move all interceptors en masse to provide their advice earlier or later in a method invocation.
[[authorization-expressions]]
== Expressing Authorization with SpEL
+1 -1
View File
@@ -14,7 +14,7 @@
# limitations under the License.
#
springBootVersion=3.3.3
version=6.4.9-SNAPSHOT
version=6.4.10
samplesBranch=6.4.x
org.gradle.jvmargs=-Xmx3g -XX:+HeapDumpOnOutOfMemoryError
org.gradle.parallel=true
+1 -1
View File
@@ -21,7 +21,7 @@ ch-qos-logback-logback-classic = "ch.qos.logback:logback-classic:1.5.18"
com-fasterxml-jackson-jackson-bom = "com.fasterxml.jackson:jackson-bom:2.18.4.1"
com-google-inject-guice = "com.google.inject:guice:3.0"
com-netflix-nebula-nebula-project-plugin = "com.netflix.nebula:nebula-project-plugin:8.2.0"
com-nimbusds-nimbus-jose-jwt = "com.nimbusds:nimbus-jose-jwt:9.37.3"
com-nimbusds-nimbus-jose-jwt = "com.nimbusds:nimbus-jose-jwt:9.37.4"
com-nimbusds-oauth2-oidc-sdk = "com.nimbusds:oauth2-oidc-sdk:9.43.6"
com-squareup-okhttp3-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "com-squareup-okhttp3" }
com-squareup-okhttp3-okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "com-squareup-okhttp3" }
@@ -30,34 +30,33 @@ import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.PlainJWT;
import net.minidev.json.JSONObject;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationManagerResolver;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.jose.TestKeys;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtClaimNames;
import org.springframework.security.oauth2.server.resource.authentication.JwtIssuerAuthenticationManagerResolver.TrustedIssuerJwtAuthenticationManagerResolver;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtDecoders;
import org.springframework.security.oauth2.jwt.TestJwts;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.mock;
import static org.mockito.BDDMockito.verify;
import static org.mockito.Mockito.mockStatic;
/**
* Tests for {@link JwtIssuerAuthenticationManagerResolver}
*/
public class JwtIssuerAuthenticationManagerResolverTests {
private static final String DEFAULT_RESPONSE_TEMPLATE = "{\n" + " \"issuer\": \"%s\", \n"
+ " \"jwks_uri\": \"%s/.well-known/jwks.json\" \n" + "}";
private static final String JWK_SET = "{\"keys\":[{\"kty\":\"RSA\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"one\",\"n\":\"3FlqJr5TRskIQIgdE3Dd7D9lboWdcTUT8a-fJR7MAvQm7XXNoYkm3v7MQL1NYtDvL2l8CAnc0WdSTINU6IRvc5Kqo2Q4csNX9SHOmEfzoROjQqahEcve1jBXluoCXdYuYpx4_1tfRgG6ii4Uhxh6iI8qNMJQX-fLfqhbfYfxBQVRPywBkAbIP4x1EAsbC6FSNmkhCxiMNqEgxaIpY8C2kJdJ_ZIV-WW4noDdzpKqHcwmB8FsrumlVY_DNVvUSDIipiq9PbP4H99TXN1o746oRaNa07rq1hoCgMSSy-85SagCoxlmyE-D-of9SsMY8Ol9t0rdzpobBuhyJ_o5dfvjKw\"}]}";
private String jwt = jwt("iss", "trusted");
private String evil = jwt("iss", "\"");
@@ -66,31 +65,22 @@ public class JwtIssuerAuthenticationManagerResolverTests {
@Test
public void resolveWhenUsingFromTrustedIssuersThenReturnsAuthenticationManager() throws Exception {
try (MockWebServer server = new MockWebServer()) {
server.start();
String issuer = server.url("").toString();
// @formatter:off
server.enqueue(new MockResponse().setResponseCode(200)
.setHeader("Content-Type", "application/json")
.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer)
));
server.enqueue(new MockResponse().setResponseCode(200)
.setHeader("Content-Type", "application/json")
.setBody(JWK_SET)
);
server.enqueue(new MockResponse().setResponseCode(200)
.setHeader("Content-Type", "application/json")
.setBody(JWK_SET)
);
// @formatter:on
JWSObject jws = new JWSObject(new JWSHeader(JWSAlgorithm.RS256),
new Payload(new JSONObject(Collections.singletonMap(JwtClaimNames.ISS, issuer))));
jws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));
JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerAuthenticationManagerResolver
.fromTrustedIssuers(issuer);
Authentication token = withBearerToken(jws.serialize());
AuthenticationManager authenticationManager = authenticationManagerResolver.resolve(null);
assertThat(authenticationManager).isNotNull();
String issuer = "https://idp.example";
// @formatter:on
JWSObject jws = new JWSObject(new JWSHeader(JWSAlgorithm.RS256),
new Payload(new JSONObject(Collections.singletonMap(JwtClaimNames.ISS, issuer))));
jws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));
JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerAuthenticationManagerResolver
.fromTrustedIssuers(issuer);
Authentication token = withBearerToken(jws.serialize());
AuthenticationManager authenticationManager = authenticationManagerResolver.resolve(null);
assertThat(authenticationManager).isNotNull();
JwtDecoder decoder = mock(JwtDecoder.class);
Jwt jwt = TestJwts.user();
given(decoder.decode(token.getName())).willReturn(jwt);
try (MockedStatic<JwtDecoders> jwtDecoders = mockStatic(JwtDecoders.class)) {
given(JwtDecoders.fromIssuerLocation(issuer)).willReturn(decoder);
Authentication authentication = authenticationManager.authenticate(token);
assertThat(authentication.isAuthenticated()).isTrue();
}
@@ -98,29 +88,20 @@ public class JwtIssuerAuthenticationManagerResolverTests {
@Test
public void resolveWhenUsingFromTrustedIssuersPredicateThenReturnsAuthenticationManager() throws Exception {
try (MockWebServer server = new MockWebServer()) {
server.start();
String issuer = server.url("").toString();
// @formatter:off
server.enqueue(new MockResponse().setResponseCode(200)
.setHeader("Content-Type", "application/json")
.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer)
));
server.enqueue(new MockResponse().setResponseCode(200)
.setHeader("Content-Type", "application/json")
.setBody(JWK_SET)
);
server.enqueue(new MockResponse().setResponseCode(200)
.setHeader("Content-Type", "application/json")
.setBody(JWK_SET)
);
// @formatter:on
JWSObject jws = new JWSObject(new JWSHeader(JWSAlgorithm.RS256),
new Payload(new JSONObject(Collections.singletonMap(JwtClaimNames.ISS, issuer))));
jws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));
JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerAuthenticationManagerResolver
.fromTrustedIssuers(issuer::equals);
Authentication token = withBearerToken(jws.serialize());
String issuer = "https://idp.example";
// @formatter:on
JWSObject jws = new JWSObject(new JWSHeader(JWSAlgorithm.RS256),
new Payload(new JSONObject(Collections.singletonMap(JwtClaimNames.ISS, issuer))));
jws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));
JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerAuthenticationManagerResolver
.fromTrustedIssuers(issuer::equals);
Authentication token = withBearerToken(jws.serialize());
JwtDecoder decoder = mock(JwtDecoder.class);
Jwt jwt = TestJwts.user();
given(decoder.decode(token.getName())).willReturn(jwt);
try (MockedStatic<JwtDecoders> jwtDecoders = mockStatic(JwtDecoders.class)) {
given(JwtDecoders.fromIssuerLocation(issuer)).willReturn(decoder);
AuthenticationManager authenticationManager = authenticationManagerResolver.resolve(null);
assertThat(authenticationManager).isNotNull();
Authentication authentication = authenticationManager.authenticate(token);
@@ -128,58 +109,6 @@ public class JwtIssuerAuthenticationManagerResolverTests {
}
}
@Test
public void resolveWhednUsingTrustedIssuerThenReturnsAuthenticationManager() throws Exception {
try (MockWebServer server = new MockWebServer()) {
server.start();
String issuer = server.url("").toString();
// @formatter:off
server.enqueue(new MockResponse().setResponseCode(500)
.setHeader("Content-Type", "application/json")
.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer))
);
server.enqueue(new MockResponse().setResponseCode(200)
.setHeader("Content-Type", "application/json")
.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer))
);
server.enqueue(new MockResponse().setResponseCode(200)
.setHeader("Content-Type", "application/json")
.setBody(JWK_SET)
);
// @formatter:on
JWSObject jws = new JWSObject(new JWSHeader(JWSAlgorithm.RS256),
new Payload(new JSONObject(Collections.singletonMap(JwtClaimNames.ISS, issuer))));
jws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));
JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerAuthenticationManagerResolver
.fromTrustedIssuers(issuer);
Authentication token = withBearerToken(jws.serialize());
AuthenticationManager authenticationManager = authenticationManagerResolver.resolve(null);
assertThat(authenticationManager).isNotNull();
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> authenticationManager.authenticate(token));
Authentication authentication = authenticationManager.authenticate(token);
assertThat(authentication.isAuthenticated()).isTrue();
}
}
@Test
public void resolveWhenUsingSameIssuerThenReturnsSameAuthenticationManager() throws Exception {
try (MockWebServer server = new MockWebServer()) {
String issuer = server.url("").toString();
server.enqueue(new MockResponse().setResponseCode(200)
.setHeader("Content-Type", "application/json")
.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer)));
server.enqueue(new MockResponse().setResponseCode(200)
.setHeader("Content-Type", "application/json")
.setBody(JWK_SET));
TrustedIssuerJwtAuthenticationManagerResolver resolver = new TrustedIssuerJwtAuthenticationManagerResolver(
(iss) -> iss.equals(issuer));
AuthenticationManager authenticationManager = resolver.resolve(issuer);
AuthenticationManager cachedAuthenticationManager = resolver.resolve(issuer);
assertThat(authenticationManager).isSameAs(cachedAuthenticationManager);
}
}
@Test
public void resolveWhenUsingUntrustedIssuerThenException() {
JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerAuthenticationManagerResolver
@@ -46,13 +46,14 @@ import org.springframework.util.StringUtils;
* wraps the chain in before and after observations
*
* @author Josh Cummings
* @author Nikita Konev
* @since 6.0
*/
public final class ObservationFilterChainDecorator implements FilterChainProxy.FilterChainDecorator {
private static final Log logger = LogFactory.getLog(FilterChainProxy.class);
private static final String ATTRIBUTE = ObservationFilterChainDecorator.class + ".observation";
static final String ATTRIBUTE = ObservationFilterChainDecorator.class + ".observation";
static final String UNSECURED_OBSERVATION_NAME = "spring.security.http.unsecured.requests";
@@ -250,6 +251,16 @@ public final class ObservationFilterChainDecorator implements FilterChainProxy.F
private AroundFilterObservation parent(HttpServletRequest request) {
FilterChainObservationContext beforeContext = FilterChainObservationContext.before();
FilterChainObservationContext afterContext = FilterChainObservationContext.after();
AroundFilterObservation existingParentObservation = (AroundFilterObservation) request
.getAttribute(ATTRIBUTE);
if (existingParentObservation != null) {
beforeContext
.setParentObservation(existingParentObservation.before().getContext().getParentObservation());
afterContext
.setParentObservation(existingParentObservation.after().getContext().getParentObservation());
}
Observation before = Observation.createNotStarted(this.convention, () -> beforeContext, this.registry);
Observation after = Observation.createNotStarted(this.convention, () -> afterContext, this.registry);
AroundFilterObservation parent = AroundFilterObservation.create(before, after);
@@ -310,6 +310,65 @@ public class FilterChainProxyTests {
assertFilterChainObservation(contexts.next(), "after", 1);
}
// gh-12610
@Test
void parentObservationIsTakenIntoAccountDuringDispatchError() throws Exception {
ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);
given(handler.supportsContext(any())).willReturn(true);
ObservationRegistry registry = ObservationRegistry.create();
registry.observationConfig().observationHandler(handler);
given(this.matcher.matches(any())).willReturn(true);
SecurityFilterChain sec = new DefaultSecurityFilterChain(this.matcher, Arrays.asList(this.filter));
FilterChainProxy fcp = new FilterChainProxy(sec);
fcp.setFilterChainDecorator(new ObservationFilterChainDecorator(registry));
Filter initialFilter = ObservationFilterChainDecorator.FilterObservation
.create(Observation.createNotStarted("wrap", registry))
.wrap(fcp);
ServletRequest initialRequest = new MockHttpServletRequest("GET", "/");
initialFilter.doFilter(initialRequest, new MockHttpServletResponse(), this.chain);
// simulate request attribute copying in case dispatching to ERROR
ObservationFilterChainDecorator.AroundFilterObservation parentObservation = (ObservationFilterChainDecorator.AroundFilterObservation) initialRequest
.getAttribute(ObservationFilterChainDecorator.ATTRIBUTE);
assertThat(parentObservation).isNotNull();
// simulate dispatching error-related request
Filter errorRelatedFilter = ObservationFilterChainDecorator.FilterObservation
.create(Observation.createNotStarted("wrap", registry))
.wrap(fcp);
ServletRequest errorRelatedRequest = new MockHttpServletRequest("GET", "/error");
errorRelatedRequest.setAttribute(ObservationFilterChainDecorator.ATTRIBUTE, parentObservation);
errorRelatedFilter.doFilter(errorRelatedRequest, new MockHttpServletResponse(), this.chain);
ArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);
verify(handler, times(8)).onStart(captor.capture());
verify(handler, times(8)).onStop(any());
List<Observation.Context> contexts = captor.getAllValues();
Observation.Context initialRequestObservationContextBefore = contexts.get(1);
Observation.Context initialRequestObservationContextAfter = contexts.get(3);
assertFilterChainObservation(initialRequestObservationContextBefore, "before", 1);
assertFilterChainObservation(initialRequestObservationContextAfter, "after", 1);
assertThat(initialRequestObservationContextBefore.getParentObservation()).isNotNull();
assertThat(initialRequestObservationContextBefore.getParentObservation())
.isSameAs(initialRequestObservationContextAfter.getParentObservation());
Observation.Context errorRelatedRequestObservationContextBefore = contexts.get(5);
Observation.Context errorRelatedRequestObservationContextAfter = contexts.get(7);
assertFilterChainObservation(errorRelatedRequestObservationContextBefore, "before", 1);
assertFilterChainObservation(errorRelatedRequestObservationContextAfter, "after", 1);
assertThat(errorRelatedRequestObservationContextBefore.getParentObservation()).isNotNull();
assertThat(errorRelatedRequestObservationContextBefore.getParentObservation())
.isSameAs(initialRequestObservationContextBefore.getParentObservation());
assertThat(errorRelatedRequestObservationContextAfter.getParentObservation()).isNotNull();
assertThat(errorRelatedRequestObservationContextAfter.getParentObservation())
.isSameAs(initialRequestObservationContextBefore.getParentObservation());
}
@Test
public void doFilterWhenMultipleFiltersThenObservationRegistryObserves() throws Exception {
ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);