From adf2a17722f2e4be26084129fd154f95c4c3eee4 Mon Sep 17 00:00:00 2001 From: hyeonjune Date: Tue, 28 Jul 2026 02:13:52 +0900 Subject: [PATCH] Add allowIfSubType for URL in CoreJacksonModule Signed-off-by: hyeonjune --- .../security/jackson/CoreJacksonModule.java | 2 ++ .../jackson/SecurityJacksonModulesTests.java | 16 ++++++++++++ .../OAuth2AuthenticationTokenMixinTests.java | 25 +++++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/core/src/main/java/org/springframework/security/jackson/CoreJacksonModule.java b/core/src/main/java/org/springframework/security/jackson/CoreJacksonModule.java index 5c6f27ba08..1d965d579e 100644 --- a/core/src/main/java/org/springframework/security/jackson/CoreJacksonModule.java +++ b/core/src/main/java/org/springframework/security/jackson/CoreJacksonModule.java @@ -16,6 +16,7 @@ package org.springframework.security.jackson; +import java.net.URL; import java.time.Duration; import java.time.Instant; @@ -73,6 +74,7 @@ protected CoreJacksonModule(String name, Version version) { public void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) { builder.allowIfSubType(Instant.class) .allowIfSubType(Duration.class) + .allowIfSubType(URL.class) .allowIfSubType(SimpleGrantedAuthority.class) .allowIfSubType(FactorGrantedAuthority.class) .allowIfSubType(UsernamePasswordAuthenticationToken.class) diff --git a/core/src/test/java/org/springframework/security/jackson/SecurityJacksonModulesTests.java b/core/src/test/java/org/springframework/security/jackson/SecurityJacksonModulesTests.java index b8cf960588..52a2297344 100644 --- a/core/src/test/java/org/springframework/security/jackson/SecurityJacksonModulesTests.java +++ b/core/src/test/java/org/springframework/security/jackson/SecurityJacksonModulesTests.java @@ -16,7 +16,11 @@ package org.springframework.security.jackson; +import java.net.URI; +import java.net.URL; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import com.fasterxml.jackson.annotation.JsonTypeInfo; import org.junit.jupiter.api.Test; @@ -72,6 +76,18 @@ public void addModulesWithCustomTypeValidator() { assertThat(deserializedUer).isEqualTo(user); } + @Test + public void deserializeWhenMapContainsUrlThenDeserializes() throws Exception { + ClassLoader loader = getClass().getClassLoader(); + List modules = SecurityJacksonModules.getModules(loader); + JsonMapper mapper = JsonMapper.builder().addModules(modules).build(); + Map map = new LinkedHashMap<>(); + map.put("url", URI.create("https://example.com").toURL()); + String json = mapper.writeValueAsString(map); + Map deserialized = mapper.readerFor(Map.class).readValue(json); + assertThat(deserialized.get("url")).isInstanceOf(URL.class).hasToString("https://example.com"); + } + @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) private static class TestGrantedAuthority implements GrantedAuthority { diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson/OAuth2AuthenticationTokenMixinTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson/OAuth2AuthenticationTokenMixinTests.java index d24cf351f8..2f5c3e09af 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson/OAuth2AuthenticationTokenMixinTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson/OAuth2AuthenticationTokenMixinTests.java @@ -16,11 +16,15 @@ package org.springframework.security.oauth2.client.jackson; +import java.net.URI; +import java.net.URL; import java.time.Instant; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import com.fasterxml.jackson.datatype.jsr310.DecimalUtils; @@ -171,6 +175,27 @@ public void deserializeWhenRequiredAttributesOnlyThenDeserializes() throws Excep assertThat(principal.getUserInfo()).isNull(); } + @Test + public void deserializeWhenClaimsContainUrlThenDeserializes() throws Exception { + Instant issuedAt = Instant.now(); + Instant expiresAt = issuedAt.plusSeconds(3600); + Map claims = new HashMap<>(); + claims.put(IdTokenClaimNames.ISS, URI.create("https://example.com/issuer").toURL()); + claims.put(IdTokenClaimNames.SUB, "subject"); + claims.put(IdTokenClaimNames.IAT, issuedAt); + claims.put(IdTokenClaimNames.EXP, expiresAt); + OidcIdToken idToken = new OidcIdToken("id-token", issuedAt, expiresAt, claims); + Collection authorities = Collections.singleton(new OidcUserAuthority(idToken)); + DefaultOidcUser principal = new DefaultOidcUser(authorities, idToken); + OAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(principal, authorities, + "registration-id"); + String json = this.mapper.writeValueAsString(authentication); + OAuth2AuthenticationToken deserialized = this.mapper.readValue(json, OAuth2AuthenticationToken.class); + DefaultOidcUser deserializedUser = (DefaultOidcUser) deserialized.getPrincipal(); + assertThat(deserializedUser.getIdToken().getClaims().get(IdTokenClaimNames.ISS)).isInstanceOf(URL.class) + .hasToString("https://example.com/issuer"); + } + private static String asJson(OAuth2AuthenticationToken authentication) { String principalJson = (authentication.getPrincipal() instanceof DefaultOidcUser) ? asJson((DefaultOidcUser) authentication.getPrincipal())