1
0
mirror of synced 2026-05-22 21:33:16 +00:00

Fix OAuth2AuthorizationServerJacksonModule type validator configuration

Closes gh-18102
This commit is contained in:
Joe Grandja
2025-10-28 14:38:06 -04:00
parent 4daf089e46
commit e6b4d461e7
7 changed files with 60 additions and 34 deletions
@@ -36,10 +36,9 @@ import org.springframework.util.ClassUtils;
/**
* This utility class will find all the Jackson modules contributed by Spring Security in
* the classpath (except {@code OAuth2AuthorizationServerJacksonModule} and
* {@code WebauthnJacksonModule}), enable automatic inclusion of type information and
* configure a {@link PolymorphicTypeValidator} that handles the validation of class
* names.
* the classpath (except {@code WebauthnJacksonModule}), enable automatic inclusion of
* type information and configure a {@link PolymorphicTypeValidator} that handles the
* validation of class names.
*
* <p>
* <pre>
@@ -77,6 +76,8 @@ public final class SecurityJacksonModules {
private static final String oauth2ClientJacksonModuleClass = "org.springframework.security.oauth2.client.jackson.OAuth2ClientJacksonModule";
private static final String oauth2AuthorizationServerJacksonModuleClass = "org.springframework.security.oauth2.server.authorization.jackson.OAuth2AuthorizationServerJacksonModule";
private static final String ldapJacksonModuleClass = "org.springframework.security.ldap.jackson.LdapJacksonModule";
private static final String saml2JacksonModuleClass = "org.springframework.security.saml2.jackson.Saml2JacksonModule";
@@ -87,6 +88,8 @@ public final class SecurityJacksonModules {
private static final boolean oauth2ClientPresent;
private static final boolean oauth2AuthorizationServerPresent;
private static final boolean ldapJacksonPresent;
private static final boolean saml2JacksonPresent;
@@ -94,11 +97,12 @@ public final class SecurityJacksonModules {
private static final boolean casJacksonPresent;
static {
ClassLoader classLoader = SecurityJacksonModules.class.getClassLoader();
webServletPresent = ClassUtils.isPresent("jakarta.servlet.http.Cookie", classLoader);
oauth2ClientPresent = ClassUtils.isPresent("org.springframework.security.oauth2.client.OAuth2AuthorizedClient",
classLoader);
oauth2AuthorizationServerPresent = ClassUtils
.isPresent("org.springframework.security.oauth2.server.authorization.OAuth2Authorization", classLoader);
ldapJacksonPresent = ClassUtils.isPresent(ldapJacksonModuleClass, classLoader);
saml2JacksonPresent = ClassUtils.isPresent(saml2JacksonModuleClass, classLoader);
casJacksonPresent = ClassUtils.isPresent(casJacksonModuleClass, classLoader);
@@ -156,6 +160,9 @@ public final class SecurityJacksonModules {
if (oauth2ClientPresent) {
addToModulesList(loader, modules, oauth2ClientJacksonModuleClass);
}
if (oauth2AuthorizationServerPresent) {
addToModulesList(loader, modules, oauth2AuthorizationServerJacksonModuleClass);
}
if (ldapJacksonPresent) {
addToModulesList(loader, modules, ldapJacksonModuleClass);
}
@@ -86,6 +86,7 @@ The following Spring Security modules provide Jackson support:
- spring-security-core (javadoc:org.springframework.security.jackson.CoreJacksonModule[])
- spring-security-web (javadoc:org.springframework.security.web.jackson.WebJacksonModule[], javadoc:org.springframework.security.web.jackson.WebServletJacksonModule[], javadoc:org.springframework.security.web.server.jackson.WebServerJacksonModule[])
- spring-security-oauth2-client (javadoc:org.springframework.security.oauth2.client.jackson.OAuth2ClientJacksonModule[])
- spring-security-oauth2-authorization-server (javadoc:org.springframework.security.oauth2.server.authorization.jackson.OAuth2AuthorizationServerJacksonModule[])
- spring-security-cas (javadoc:org.springframework.security.cas.jackson.CasJacksonModule[])
- spring-security-ldap (javadoc:org.springframework.security.ldap.jackson.LdapJacksonModule[])
- spring-security-saml2 (javadoc:org.springframework.security.saml2.jackson.Saml2JacksonModule[])
@@ -13,9 +13,9 @@ dependencies {
api "com.nimbusds:nimbus-jose-jwt"
api 'tools.jackson.core:jackson-databind'
optional "com.fasterxml.jackson.core:jackson-databind"
optional "com.fasterxml.jackson.datatype:jackson-datatype-jsr310"
optional "org.springframework:spring-jdbc"
optional "com.fasterxml.jackson.core:jackson-databind"
testImplementation project(":spring-security-test")
testImplementation project(path : ':spring-security-oauth2-jose', configuration : 'tests')
@@ -68,7 +68,6 @@ import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.jackson.OAuth2AuthorizationServerJacksonModule;
import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
@@ -361,7 +360,7 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
/**
* Sets the {@link RowMapper} used for mapping the current row in
* {@code java.sql.ResultSet} to {@link OAuth2Authorization}. The default is
* {@link OAuth2AuthorizationRowMapper}.
* {@link JsonMapperOAuth2AuthorizationRowMapper}.
* @param authorizationRowMapper the {@link RowMapper} used for mapping the current
* row in {@code ResultSet} to {@link OAuth2Authorization}
*/
@@ -373,7 +372,7 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
/**
* Sets the {@code Function} used for mapping {@link OAuth2Authorization} to a
* {@code List} of {@link SqlParameterValue}. The default is
* {@link OAuth2AuthorizationParametersMapper}.
* {@link JsonMapperOAuth2AuthorizationParametersMapper}.
* @param authorizationParametersMapper the {@code Function} used for mapping
* {@link OAuth2Authorization} to a {@code List} of {@link SqlParameterValue}
*/
@@ -743,10 +742,7 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
static JsonMapper createJsonMapper() {
List<JacksonModule> modules = SecurityJacksonModules.getModules(Jackson3.class.getClassLoader());
return JsonMapper.builder()
.addModules(modules)
.addModules(new OAuth2AuthorizationServerJacksonModule())
.build();
return JsonMapper.builder().addModules(modules).build();
}
}
@@ -18,13 +18,11 @@ package org.springframework.security.oauth2.server.authorization.jackson;
import java.net.URL;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import tools.jackson.core.Version;
import tools.jackson.databind.DefaultTyping;
import tools.jackson.databind.cfg.MapperBuilder;
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import org.springframework.security.jackson.CoreJacksonModule;
import org.springframework.security.jackson.SecurityJacksonModule;
import org.springframework.security.jackson.SecurityJacksonModules;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
@@ -37,20 +35,22 @@ import org.springframework.security.oauth2.server.authorization.settings.OAuth2T
* registers the following mix-in annotations:
*
* <ul>
* <li>{@link OAuth2TokenExchangeActor}</li>
* <li>{@link OAuth2TokenExchangeActorMixin}</li>
* <li>{@link OAuth2AuthorizationRequestMixin}</li>
* <li>{@link OAuth2TokenExchangeCompositeAuthenticationTokenMixin}</li>
* <li>{@link JwsAlgorithmMixin}</li>
* <li>{@link OAuth2TokenFormatMixin}</li>
* </ul>
*
* If not already enabled, default typing will be automatically enabled as type info is
* required to properly serialize/deserialize objects. In order to use this module just
* add it to your {@code JsonMapper.Builder} configuration.
* <p>
* The recommended way to configure it is to use {@link SecurityJacksonModules} in order
* to enable properly automatic inclusion of type information with related validation.
*
* <pre>
* ClassLoader loader = getClass().getClassLoader();
* JsonMapper mapper = JsonMapper.builder()
* .addModules(new OAuth2AuthorizationServerJacksonModule()).build;
* .addModules(SecurityJacksonModules.getModules(loader))
* .build();
* </pre>
*
* @author Sebastien Deleuze
@@ -58,7 +58,7 @@ import org.springframework.security.oauth2.server.authorization.settings.OAuth2T
* @since 7.0
*/
@SuppressWarnings("serial")
public class OAuth2AuthorizationServerJacksonModule extends CoreJacksonModule {
public class OAuth2AuthorizationServerJacksonModule extends SecurityJacksonModule {
public OAuth2AuthorizationServerJacksonModule() {
super(OAuth2AuthorizationServerJacksonModule.class.getName(), new Version(1, 0, 0, null, null, null));
@@ -66,7 +66,6 @@ public class OAuth2AuthorizationServerJacksonModule extends CoreJacksonModule {
@Override
public void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {
super.configurePolymorphicTypeValidator(builder);
builder.allowIfSubType(OAuth2TokenFormat.class)
.allowIfSubType(OAuth2TokenExchangeActor.class)
.allowIfSubType(OAuth2TokenExchangeCompositeAuthenticationToken.class)
@@ -78,11 +77,6 @@ public class OAuth2AuthorizationServerJacksonModule extends CoreJacksonModule {
@Override
public void setupModule(SetupContext context) {
super.setupModule(context);
BasicPolymorphicTypeValidator.Builder builder = BasicPolymorphicTypeValidator.builder();
this.configurePolymorphicTypeValidator(builder);
((MapperBuilder<?, ?>) context.getOwner()).activateDefaultTyping(builder.build(), DefaultTyping.NON_FINAL,
JsonTypeInfo.As.PROPERTY);
context.setMixIn(OAuth2TokenExchangeActor.class, OAuth2TokenExchangeActorMixin.class);
context.setMixIn(OAuth2AuthorizationRequest.class, OAuth2AuthorizationRequestMixin.class);
context.setMixIn(OAuth2TokenExchangeCompositeAuthenticationToken.class,
@@ -16,6 +16,7 @@
package org.springframework.security.oauth2.server.authorization;
import java.security.Principal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
@@ -45,6 +46,7 @@ import org.springframework.jdbc.core.SqlParameterValue;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.jackson.SecurityJacksonModules;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
@@ -58,6 +60,7 @@ import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
@@ -227,10 +230,11 @@ public class JdbcOAuth2AuthorizationServiceTests {
.build();
RowMapper<OAuth2Authorization> authorizationRowMapper = spy(
new JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper(this.registeredClientRepository));
new JdbcOAuth2AuthorizationService.JsonMapperOAuth2AuthorizationRowMapper(
this.registeredClientRepository));
this.authorizationService.setAuthorizationRowMapper(authorizationRowMapper);
Function<OAuth2Authorization, List<SqlParameterValue>> authorizationParametersMapper = spy(
new JdbcOAuth2AuthorizationService.OAuth2AuthorizationParametersMapper());
new JdbcOAuth2AuthorizationService.JsonMapperOAuth2AuthorizationParametersMapper());
this.authorizationService.setAuthorizationParametersMapper(authorizationParametersMapper);
this.authorizationService.save(originalAuthorization);
@@ -461,6 +465,28 @@ public class JdbcOAuth2AuthorizationServiceTests {
assertThat(result).isNull();
}
// gh-18102
@Test
public void findByTokenWhenPrincipalHasWebAuthenticationDetailsThenDeserializes() {
given(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).willReturn(REGISTERED_CLIENT);
String state = "state";
TestingAuthenticationToken principal = new TestingAuthenticationToken(PRINCIPAL_NAME, "credentials");
principal.setDetails(new WebAuthenticationDetails("remoteAddress", "sessionId"));
OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)
.id(ID)
.principalName(PRINCIPAL_NAME)
.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)
.attribute(OAuth2ParameterNames.STATE, state)
.attribute(Principal.class.getName(), principal)
.build();
this.authorizationService.save(authorization);
OAuth2Authorization result = this.authorizationService.findByToken(state, STATE_TOKEN_TYPE);
assertThat(authorization).isEqualTo(result);
}
@Test
public void tableDefinitionWhenCustomThenAbleToOverride() {
given(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).willReturn(REGISTERED_CLIENT);
@@ -27,20 +27,20 @@ import tools.jackson.databind.json.JsonMapper;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.jackson.SecurityJacksonModules;
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
import org.springframework.security.oauth2.jwt.JwtClaimNames;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link OAuth2AuthorizationServerJackson2Module}.
* Tests for {@link OAuth2AuthorizationServerJacksonModule}.
*
* @author Steve Riesenberg
* @author Joe Grandja
@@ -55,7 +55,9 @@ public class OAuth2AuthorizationServerJacksonModuleTests {
@BeforeEach
public void setup() {
this.mapper = JsonMapper.builder().addModules(new OAuth2AuthorizationServerJacksonModule()).build();
this.mapper = JsonMapper.builder()
.addModules(SecurityJacksonModules.getModules(getClass().getClassLoader()))
.build();
}
@Test