2828import java .util .Set ;
2929import java .util .function .Function ;
3030
31- import com .fasterxml .jackson .core .type . TypeReference ;
31+ import com .fasterxml .jackson .core .JsonProcessingException ;
3232import com .fasterxml .jackson .databind .Module ;
3333import com .fasterxml .jackson .databind .ObjectMapper ;
34+ import tools .jackson .databind .JacksonModule ;
35+ import tools .jackson .databind .json .JsonMapper ;
3436
3537import org .springframework .aot .hint .RuntimeHints ;
3638import org .springframework .aot .hint .RuntimeHintsRegistrar ;
3739import org .springframework .context .annotation .ImportRuntimeHints ;
40+ import org .springframework .core .ParameterizedTypeReference ;
3841import org .springframework .core .io .ClassPathResource ;
3942import org .springframework .jdbc .core .ArgumentPreparedStatementSetter ;
4043import org .springframework .jdbc .core .JdbcOperations ;
4144import org .springframework .jdbc .core .PreparedStatementSetter ;
4245import org .springframework .jdbc .core .RowMapper ;
4346import org .springframework .jdbc .core .SqlParameterValue ;
47+ import org .springframework .security .jackson .SecurityJacksonModules ;
4448import org .springframework .security .jackson2 .SecurityJackson2Modules ;
4549import org .springframework .security .oauth2 .core .AuthorizationGrantType ;
4650import org .springframework .security .oauth2 .core .ClientAuthenticationMethod ;
@@ -134,8 +138,8 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
134138 public JdbcRegisteredClientRepository (JdbcOperations jdbcOperations ) {
135139 Assert .notNull (jdbcOperations , "jdbcOperations cannot be null" );
136140 this .jdbcOperations = jdbcOperations ;
137- this .registeredClientRowMapper = new RegisteredClientRowMapper ();
138- this .registeredClientParametersMapper = new RegisteredClientParametersMapper ();
141+ this .registeredClientRowMapper = new JsonMapperRegisteredClientRowMapper ();
142+ this .registeredClientParametersMapper = new JsonMapperRegisteredClientParametersMapper ();
139143 }
140144
141145 @ Override
@@ -206,7 +210,7 @@ private RegisteredClient findBy(String filter, Object... args) {
206210 /**
207211 * Sets the {@link RowMapper} used for mapping the current row in
208212 * {@code java.sql.ResultSet} to {@link RegisteredClient}. The default is
209- * {@link RegisteredClientRowMapper }.
213+ * {@link JsonMapperRegisteredClientRowMapper }.
210214 * @param registeredClientRowMapper the {@link RowMapper} used for mapping the current
211215 * row in {@code ResultSet} to {@link RegisteredClient}
212216 */
@@ -218,7 +222,7 @@ public final void setRegisteredClientRowMapper(RowMapper<RegisteredClient> regis
218222 /**
219223 * Sets the {@code Function} used for mapping {@link RegisteredClient} to a
220224 * {@code List} of {@link SqlParameterValue}. The default is
221- * {@link RegisteredClientParametersMapper }.
225+ * {@link JsonMapperRegisteredClientParametersMapper }.
222226 * @param registeredClientParametersMapper the {@code Function} used for mapping
223227 * {@link RegisteredClient} to a {@code List} of {@link SqlParameterValue}
224228 */
@@ -242,17 +246,76 @@ protected final Function<RegisteredClient, List<SqlParameterValue>> getRegistere
242246
243247 /**
244248 * The default {@link RowMapper} that maps the current row in
245- * {@code java.sql.ResultSet} to {@link RegisteredClient}.
249+ * {@code java.sql.ResultSet} to {@link RegisteredClient} using Jackson 3's
250+ * {@link JsonMapper}.
251+ *
252+ * @author Joe Grandja
253+ * @since 7.0
246254 */
247- public static class RegisteredClientRowMapper implements RowMapper < RegisteredClient > {
255+ public static class JsonMapperRegisteredClientRowMapper extends AbstractRegisteredClientRowMapper {
248256
249- private ObjectMapper objectMapper = new ObjectMapper () ;
257+ private final JsonMapper jsonMapper ;
250258
251- public RegisteredClientRowMapper () {
252- ClassLoader classLoader = JdbcRegisteredClientRepository .class .getClassLoader ();
253- List <Module > securityModules = SecurityJackson2Modules .getModules (classLoader );
254- this .objectMapper .registerModules (securityModules );
255- this .objectMapper .registerModule (new OAuth2AuthorizationServerJackson2Module ());
259+ public JsonMapperRegisteredClientRowMapper () {
260+ this (Jackson3 .createJsonMapper ());
261+ }
262+
263+ public JsonMapperRegisteredClientRowMapper (JsonMapper jsonMapper ) {
264+ Assert .notNull (jsonMapper , "jsonMapper cannot be null" );
265+ this .jsonMapper = jsonMapper ;
266+ }
267+
268+ @ Override
269+ Map <String , Object > readValue (String data ) {
270+ final ParameterizedTypeReference <Map <String , Object >> typeReference = new ParameterizedTypeReference <>() {
271+ };
272+ tools .jackson .databind .JavaType javaType = this .jsonMapper .getTypeFactory ()
273+ .constructType (typeReference .getType ());
274+ return this .jsonMapper .readValue (data , javaType );
275+ }
276+
277+ }
278+
279+ /**
280+ * A {@link RowMapper} that maps the current row in {@code java.sql.ResultSet} to
281+ * {@link RegisteredClient} using Jackson 2's {@link ObjectMapper}.
282+ *
283+ * @deprecated Use {@link JsonMapperRegisteredClientRowMapper} to switch to Jackson 3.
284+ */
285+ @ Deprecated (forRemoval = true , since = "7.0" )
286+ public static class RegisteredClientRowMapper extends AbstractRegisteredClientRowMapper {
287+
288+ private ObjectMapper objectMapper = Jackson2 .createObjectMapper ();
289+
290+ public final void setObjectMapper (ObjectMapper objectMapper ) {
291+ Assert .notNull (objectMapper , "objectMapper cannot be null" );
292+ this .objectMapper = objectMapper ;
293+ }
294+
295+ protected final ObjectMapper getObjectMapper () {
296+ return this .objectMapper ;
297+ }
298+
299+ @ Override
300+ Map <String , Object > readValue (String data ) throws JsonProcessingException {
301+ final ParameterizedTypeReference <Map <String , Object >> typeReference = new ParameterizedTypeReference <>() {
302+ };
303+ com .fasterxml .jackson .databind .JavaType javaType = this .objectMapper .getTypeFactory ()
304+ .constructType (typeReference .getType ());
305+ return this .objectMapper .readValue (data , javaType );
306+ }
307+
308+ }
309+
310+ /**
311+ * The base {@link RowMapper} that maps the current row in {@code java.sql.ResultSet}
312+ * to {@link RegisteredClient}. This is extracted to a distinct class so that
313+ * {@link RegisteredClientRowMapper} can be deprecated in favor of
314+ * {@link JsonMapperRegisteredClientRowMapper}.
315+ */
316+ private abstract static class AbstractRegisteredClientRowMapper implements RowMapper <RegisteredClient > {
317+
318+ private AbstractRegisteredClientRowMapper () {
256319 }
257320
258321 @ Override
@@ -299,25 +362,17 @@ public RegisteredClient mapRow(ResultSet rs, int rowNum) throws SQLException {
299362 return builder .build ();
300363 }
301364
302- public final void setObjectMapper (ObjectMapper objectMapper ) {
303- Assert .notNull (objectMapper , "objectMapper cannot be null" );
304- this .objectMapper = objectMapper ;
305- }
306-
307- protected final ObjectMapper getObjectMapper () {
308- return this .objectMapper ;
309- }
310-
311365 private Map <String , Object > parseMap (String data ) {
312366 try {
313- return this .objectMapper .readValue (data , new TypeReference <>() {
314- });
367+ return readValue (data );
315368 }
316369 catch (Exception ex ) {
317370 throw new IllegalArgumentException (ex .getMessage (), ex );
318371 }
319372 }
320373
374+ abstract Map <String , Object > readValue (String data ) throws Exception ;
375+
321376 private static AuthorizationGrantType resolveAuthorizationGrantType (String authorizationGrantType ) {
322377 if (AuthorizationGrantType .AUTHORIZATION_CODE .getValue ().equals (authorizationGrantType )) {
323378 return AuthorizationGrantType .AUTHORIZATION_CODE ;
@@ -350,18 +405,64 @@ else if (ClientAuthenticationMethod.NONE.getValue().equals(clientAuthenticationM
350405
351406 /**
352407 * The default {@code Function} that maps {@link RegisteredClient} to a {@code List}
353- * of {@link SqlParameterValue}.
408+ * of {@link SqlParameterValue} using an instance of Jackson 3's {@link JsonMapper} .
354409 */
355- public static class RegisteredClientParametersMapper
356- implements Function <RegisteredClient , List <SqlParameterValue >> {
410+ public static class JsonMapperRegisteredClientParametersMapper extends AbstractRegisteredClientParametersMapper {
357411
358- private ObjectMapper objectMapper = new ObjectMapper () ;
412+ private final JsonMapper jsonMapper ;
359413
360- public RegisteredClientParametersMapper () {
361- ClassLoader classLoader = JdbcRegisteredClientRepository .class .getClassLoader ();
362- List <Module > securityModules = SecurityJackson2Modules .getModules (classLoader );
363- this .objectMapper .registerModules (securityModules );
364- this .objectMapper .registerModule (new OAuth2AuthorizationServerJackson2Module ());
414+ public JsonMapperRegisteredClientParametersMapper () {
415+ this (Jackson3 .createJsonMapper ());
416+ }
417+
418+ public JsonMapperRegisteredClientParametersMapper (JsonMapper jsonMapper ) {
419+ Assert .notNull (jsonMapper , "jsonMapper cannot be null" );
420+ this .jsonMapper = jsonMapper ;
421+ }
422+
423+ @ Override
424+ String writeValueAsString (Map <String , Object > data ) throws Exception {
425+ return this .jsonMapper .writeValueAsString (data );
426+ }
427+
428+ }
429+
430+ /**
431+ * A {@code Function} that maps {@link RegisteredClient} to a {@code List} of
432+ * {@link SqlParameterValue} using an instance of Jackson 2's {@link ObjectMapper}.
433+ *
434+ * @deprecated Use {@link JsonMapperRegisteredClientParametersMapper} to switch to
435+ * Jackson 3.
436+ */
437+ @ Deprecated (forRemoval = true , since = "7.0" )
438+ public static class RegisteredClientParametersMapper extends AbstractRegisteredClientParametersMapper {
439+
440+ private ObjectMapper objectMapper = Jackson2 .createObjectMapper ();
441+
442+ public final void setObjectMapper (ObjectMapper objectMapper ) {
443+ Assert .notNull (objectMapper , "objectMapper cannot be null" );
444+ this .objectMapper = objectMapper ;
445+ }
446+
447+ protected final ObjectMapper getObjectMapper () {
448+ return this .objectMapper ;
449+ }
450+
451+ @ Override
452+ String writeValueAsString (Map <String , Object > data ) throws JsonProcessingException {
453+ return this .objectMapper .writeValueAsString (data );
454+ }
455+
456+ }
457+
458+ /**
459+ * The base {@code Function} that maps {@link RegisteredClient} to a {@code List} of
460+ * {@link SqlParameterValue}.
461+ */
462+ private abstract static class AbstractRegisteredClientParametersMapper
463+ implements Function <RegisteredClient , List <SqlParameterValue >> {
464+
465+ private AbstractRegisteredClientParametersMapper () {
365466 }
366467
367468 @ Override
@@ -403,24 +504,52 @@ public List<SqlParameterValue> apply(RegisteredClient registeredClient) {
403504 new SqlParameterValue (Types .VARCHAR , writeMap (registeredClient .getTokenSettings ().getSettings ())));
404505 }
405506
406- public final void setObjectMapper (ObjectMapper objectMapper ) {
407- Assert .notNull (objectMapper , "objectMapper cannot be null" );
408- this .objectMapper = objectMapper ;
409- }
410-
411- protected final ObjectMapper getObjectMapper () {
412- return this .objectMapper ;
413- }
414-
415507 private String writeMap (Map <String , Object > data ) {
416508 try {
417- return this . objectMapper . writeValueAsString (data );
509+ return writeValueAsString (data );
418510 }
419511 catch (Exception ex ) {
420512 throw new IllegalArgumentException (ex .getMessage (), ex );
421513 }
422514 }
423515
516+ abstract String writeValueAsString (Map <String , Object > data ) throws Exception ;
517+
518+ }
519+
520+ /**
521+ * Nested class to protect from getting {@link NoClassDefFoundError} when Jackson 2 is
522+ * not on the classpath.
523+ *
524+ * @deprecated This is used to allow transition to Jackson 3. Use {@link Jackson3}
525+ * instead.
526+ */
527+ @ Deprecated (forRemoval = true , since = "7.0" )
528+ private static final class Jackson2 {
529+
530+ private static ObjectMapper createObjectMapper () {
531+ ObjectMapper objectMapper = new ObjectMapper ();
532+ ClassLoader classLoader = Jackson2 .class .getClassLoader ();
533+ List <Module > securityModules = SecurityJackson2Modules .getModules (classLoader );
534+ objectMapper .registerModules (securityModules );
535+ objectMapper .registerModule (new OAuth2AuthorizationServerJackson2Module ());
536+ return objectMapper ;
537+ }
538+
539+ }
540+
541+ /**
542+ * Nested class used to get a common default instance of {@link JsonMapper}. It is in
543+ * a nested class to protect from getting {@link NoClassDefFoundError} when Jackson 3
544+ * is not on the classpath.
545+ */
546+ private static final class Jackson3 {
547+
548+ private static JsonMapper createJsonMapper () {
549+ List <JacksonModule > modules = SecurityJacksonModules .getModules (Jackson3 .class .getClassLoader ());
550+ return JsonMapper .builder ().addModules (modules ).build ();
551+ }
552+
424553 }
425554
426555 static class JdbcRegisteredClientRepositoryRuntimeHintsRegistrar implements RuntimeHintsRegistrar {
0 commit comments