Browse Source

Add support for JacksonJsonHttpMessageConverter

This commit introduces classpath checks and instantiation of
JacksonJsonHttpMessageConverter (based on Jackson 3) leveraging
a new GenericHttpMessageConverterAdapter which allows to adapt
SmartHttpMessageConverter to GenericHttpMessageConverter.

See gh-17832
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
Sébastien Deleuze 2 weeks ago
parent
commit
137f8fd670
13 changed files with 282 additions and 13 deletions
  1. 9 0
      config/src/main/java/org/springframework/security/config/web/server/HttpMessageConverters.java
  2. 9 0
      oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/HttpMessageConverters.java
  3. 10 1
      oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/HttpMessageConverters.java
  4. 9 0
      oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/HttpMessageConverters.java
  5. 2 2
      oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcUserInfoHttpMessageConverterTests.java
  6. 99 0
      oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/GenericHttpMessageConverterAdapter.java
  7. 8 0
      oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/HttpMessageConverters.java
  8. 2 1
      oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/OAuth2AccessTokenResponseHttpMessageConverter.java
  9. 2 1
      oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/OAuth2ErrorHttpMessageConverter.java
  10. 8 0
      oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/OAuth2ProtectedResourceMetadataFilter.java
  11. 99 0
      web/src/main/java/org/springframework/security/web/http/GenericHttpMessageConverterAdapter.java
  12. 20 7
      webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationFilter.java
  13. 5 1
      webauthn/src/test/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationFilterTests.java

+ 9 - 0
config/src/main/java/org/springframework/security/config/web/server/HttpMessageConverters.java

@@ -19,8 +19,10 @@ package org.springframework.security.config.web.server;
 import org.springframework.http.converter.GenericHttpMessageConverter;
 import org.springframework.http.converter.HttpMessageConverter;
 import org.springframework.http.converter.json.GsonHttpMessageConverter;
+import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
 import org.springframework.http.converter.json.JsonbHttpMessageConverter;
 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.security.oauth2.core.http.converter.GenericHttpMessageConverterAdapter;
 import org.springframework.util.ClassUtils;
 
 /**
@@ -32,6 +34,8 @@ import org.springframework.util.ClassUtils;
  */
 final class HttpMessageConverters {
 
+	private static final boolean jacksonPresent;
+
 	private static final boolean jackson2Present;
 
 	private static final boolean gsonPresent;
@@ -40,6 +44,7 @@ final class HttpMessageConverters {
 
 	static {
 		ClassLoader classLoader = HttpMessageConverters.class.getClassLoader();
+		jacksonPresent = ClassUtils.isPresent("tools.jackson.databind.json.JsonMapper", classLoader);
 		jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader)
 				&& ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
 		gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
@@ -49,7 +54,11 @@ final class HttpMessageConverters {
 	private HttpMessageConverters() {
 	}
 
+	@SuppressWarnings("removal")
 	static GenericHttpMessageConverter<Object> getJsonMessageConverter() {
+		if (jacksonPresent) {
+			return new GenericHttpMessageConverterAdapter<>(new JacksonJsonHttpMessageConverter());
+		}
 		if (jackson2Present) {
 			return new MappingJackson2HttpMessageConverter();
 		}

+ 9 - 0
oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/HttpMessageConverters.java

@@ -19,8 +19,10 @@ package org.springframework.security.oauth2.server.authorization.http.converter;
 import org.springframework.http.converter.GenericHttpMessageConverter;
 import org.springframework.http.converter.HttpMessageConverter;
 import org.springframework.http.converter.json.GsonHttpMessageConverter;
+import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
 import org.springframework.http.converter.json.JsonbHttpMessageConverter;
 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.security.web.http.GenericHttpMessageConverterAdapter;
 import org.springframework.util.ClassUtils;
 
 /**
@@ -32,6 +34,8 @@ import org.springframework.util.ClassUtils;
  */
 final class HttpMessageConverters {
 
+	private static final boolean jacksonPresent;
+
 	private static final boolean jackson2Present;
 
 	private static final boolean gsonPresent;
@@ -40,6 +44,7 @@ final class HttpMessageConverters {
 
 	static {
 		ClassLoader classLoader = HttpMessageConverters.class.getClassLoader();
+		jacksonPresent = ClassUtils.isPresent("tools.jackson.databind.json.JsonMapper", classLoader);
 		jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader)
 				&& ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
 		gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
@@ -49,7 +54,11 @@ final class HttpMessageConverters {
 	private HttpMessageConverters() {
 	}
 
+	@SuppressWarnings("removal")
 	static GenericHttpMessageConverter<Object> getJsonMessageConverter() {
+		if (jacksonPresent) {
+			return new GenericHttpMessageConverterAdapter<>(new JacksonJsonHttpMessageConverter());
+		}
 		if (jackson2Present) {
 			return new MappingJackson2HttpMessageConverter();
 		}

+ 10 - 1
oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/HttpMessageConverters.java

@@ -19,19 +19,23 @@ package org.springframework.security.oauth2.server.authorization.oidc.http.conve
 import org.springframework.http.converter.GenericHttpMessageConverter;
 import org.springframework.http.converter.HttpMessageConverter;
 import org.springframework.http.converter.json.GsonHttpMessageConverter;
+import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
 import org.springframework.http.converter.json.JsonbHttpMessageConverter;
 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.security.web.http.GenericHttpMessageConverterAdapter;
 import org.springframework.util.ClassUtils;
 
 /**
  * Utility methods for {@link HttpMessageConverter}'s.
  *
  * @author Joe Grandja
- * @author l uamas
+ * @author luamas
  * @since 7.0
  */
 final class HttpMessageConverters {
 
+	private static final boolean jacksonPresent;
+
 	private static final boolean jackson2Present;
 
 	private static final boolean gsonPresent;
@@ -40,6 +44,7 @@ final class HttpMessageConverters {
 
 	static {
 		ClassLoader classLoader = HttpMessageConverters.class.getClassLoader();
+		jacksonPresent = ClassUtils.isPresent("tools.jackson.databind.json.JsonMapper", classLoader);
 		jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader)
 				&& ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
 		gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
@@ -49,7 +54,11 @@ final class HttpMessageConverters {
 	private HttpMessageConverters() {
 	}
 
+	@SuppressWarnings("removal")
 	static GenericHttpMessageConverter<Object> getJsonMessageConverter() {
+		if (jacksonPresent) {
+			return new GenericHttpMessageConverterAdapter<>(new JacksonJsonHttpMessageConverter());
+		}
 		if (jackson2Present) {
 			return new MappingJackson2HttpMessageConverter();
 		}

+ 9 - 0
oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/HttpMessageConverters.java

@@ -19,8 +19,10 @@ package org.springframework.security.oauth2.server.authorization.web;
 import org.springframework.http.converter.GenericHttpMessageConverter;
 import org.springframework.http.converter.HttpMessageConverter;
 import org.springframework.http.converter.json.GsonHttpMessageConverter;
+import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
 import org.springframework.http.converter.json.JsonbHttpMessageConverter;
 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.security.web.http.GenericHttpMessageConverterAdapter;
 import org.springframework.util.ClassUtils;
 
 /**
@@ -31,6 +33,8 @@ import org.springframework.util.ClassUtils;
  */
 final class HttpMessageConverters {
 
+	private static final boolean jacksonPresent;
+
 	private static final boolean jackson2Present;
 
 	private static final boolean gsonPresent;
@@ -39,6 +43,7 @@ final class HttpMessageConverters {
 
 	static {
 		ClassLoader classLoader = HttpMessageConverters.class.getClassLoader();
+		jacksonPresent = ClassUtils.isPresent("tools.jackson.databind.json.JsonMapper", classLoader);
 		jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader)
 				&& ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
 		gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
@@ -48,7 +53,11 @@ final class HttpMessageConverters {
 	private HttpMessageConverters() {
 	}
 
+	@SuppressWarnings("removal")
 	static GenericHttpMessageConverter<Object> getJsonMessageConverter() {
+		if (jacksonPresent) {
+			return new GenericHttpMessageConverterAdapter<>(new JacksonJsonHttpMessageConverter());
+		}
 		if (jackson2Present) {
 			return new MappingJackson2HttpMessageConverter();
 		}

+ 2 - 2
oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcUserInfoHttpMessageConverterTests.java

@@ -90,7 +90,7 @@ public class OidcUserInfoHttpMessageConverterTests {
 				"		\"postal_code\": \"75007\",\n" +
 				"		\"country\": \"France\"\n" +
 				"	},\n" +
-				"	\"updated_at\": 1607633867\n" +
+				"	\"updated_at\": \"2020-12-10T20:57:47Z\"\n" +
 				"}\n";
 		// @formatter:on
 
@@ -178,7 +178,7 @@ public class OidcUserInfoHttpMessageConverterTests {
 		assertThat(userInfoResponse).contains("\"address\":");
 		assertThat(userInfoResponse)
 			.contains("\"formatted\":\"Champ de Mars\\n5 Av. Anatole France\\n75007 Paris\\nFrance\"");
-		assertThat(userInfoResponse).contains("\"updated_at\":1607633867");
+		assertThat(userInfoResponse).contains("\"updated_at\":\"2020-12-10T20:57:47Z\"");
 		assertThat(userInfoResponse).contains("\"custom_claim\":\"value\"");
 		assertThat(userInfoResponse).contains("\"custom_collection_claim\":[\"value1\",\"value2\"]");
 	}

+ 99 - 0
oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/GenericHttpMessageConverterAdapter.java

@@ -0,0 +1,99 @@
+/*
+ * Copyright 2004-present 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.oauth2.core.http.converter;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.List;
+
+import org.jspecify.annotations.Nullable;
+
+import org.springframework.core.ResolvableType;
+import org.springframework.http.HttpInputMessage;
+import org.springframework.http.HttpOutputMessage;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.GenericHttpMessageConverter;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.http.converter.HttpMessageNotWritableException;
+import org.springframework.http.converter.SmartHttpMessageConverter;
+
+/**
+ * {@link GenericHttpMessageConverter} implementation that delegates to a
+ * {@link SmartHttpMessageConverter}.
+ *
+ * @param <T> the converted object type
+ * @author Sebastien Deleuze
+ * @since 7.0
+ */
+public class GenericHttpMessageConverterAdapter<T> implements GenericHttpMessageConverter<T> {
+
+	private final SmartHttpMessageConverter<T> smartConverter;
+
+	public GenericHttpMessageConverterAdapter(SmartHttpMessageConverter<T> smartConverter) {
+		this.smartConverter = smartConverter;
+	}
+
+	@Override
+	public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {
+		return this.smartConverter.canRead(ResolvableType.forType(type), mediaType);
+	}
+
+	@Override
+	public T read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
+			throws IOException, HttpMessageNotReadableException {
+		return this.smartConverter.read(ResolvableType.forType(type), inputMessage, null);
+	}
+
+	@Override
+	public boolean canWrite(@Nullable Type type, Class<?> clazz, @Nullable MediaType mediaType) {
+		return this.smartConverter.canWrite(ResolvableType.forType(type), clazz, mediaType);
+	}
+
+	@Override
+	public void write(T t, @Nullable Type type, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
+			throws IOException, HttpMessageNotWritableException {
+		this.smartConverter.write(t, ResolvableType.forType(type), contentType, outputMessage, null);
+	}
+
+	@Override
+	public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {
+		return this.smartConverter.canRead(ResolvableType.forClass(clazz), mediaType);
+	}
+
+	@Override
+	public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
+		return this.smartConverter.canWrite(clazz, mediaType);
+	}
+
+	@Override
+	public List<MediaType> getSupportedMediaTypes() {
+		return this.smartConverter.getSupportedMediaTypes();
+	}
+
+	@Override
+	public T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
+			throws IOException, HttpMessageNotReadableException {
+		return this.smartConverter.read(clazz, inputMessage);
+	}
+
+	@Override
+	public void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
+			throws IOException, HttpMessageNotWritableException {
+		this.smartConverter.write(t, contentType, outputMessage);
+	}
+
+}

+ 8 - 0
oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/HttpMessageConverters.java

@@ -19,6 +19,7 @@ package org.springframework.security.oauth2.core.http.converter;
 import org.springframework.http.converter.GenericHttpMessageConverter;
 import org.springframework.http.converter.HttpMessageConverter;
 import org.springframework.http.converter.json.GsonHttpMessageConverter;
+import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
 import org.springframework.http.converter.json.JsonbHttpMessageConverter;
 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
 import org.springframework.util.ClassUtils;
@@ -32,6 +33,8 @@ import org.springframework.util.ClassUtils;
  */
 final class HttpMessageConverters {
 
+	private static final boolean jacksonPresent;
+
 	private static final boolean jackson2Present;
 
 	private static final boolean gsonPresent;
@@ -40,6 +43,7 @@ final class HttpMessageConverters {
 
 	static {
 		ClassLoader classLoader = HttpMessageConverters.class.getClassLoader();
+		jacksonPresent = ClassUtils.isPresent("tools.jackson.databind.json.JsonMapper", classLoader);
 		jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader)
 				&& ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
 		gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
@@ -49,7 +53,11 @@ final class HttpMessageConverters {
 	private HttpMessageConverters() {
 	}
 
+	@SuppressWarnings("removal")
 	static GenericHttpMessageConverter<Object> getJsonMessageConverter() {
+		if (jacksonPresent) {
+			return new GenericHttpMessageConverterAdapter<>(new JacksonJsonHttpMessageConverter());
+		}
 		if (jackson2Present) {
 			return new MappingJackson2HttpMessageConverter();
 		}

+ 2 - 1
oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/OAuth2AccessTokenResponseHttpMessageConverter.java

@@ -52,7 +52,8 @@ public class OAuth2AccessTokenResponseHttpMessageConverter
 	private static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() {
 	};
 
-	private GenericHttpMessageConverter<Object> jsonMessageConverter = HttpMessageConverters.getJsonMessageConverter();
+	private final GenericHttpMessageConverter<Object> jsonMessageConverter = HttpMessageConverters
+		.getJsonMessageConverter();
 
 	private Converter<Map<String, Object>, OAuth2AccessTokenResponse> accessTokenResponseConverter = new DefaultMapOAuth2AccessTokenResponseConverter();
 

+ 2 - 1
oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/OAuth2ErrorHttpMessageConverter.java

@@ -52,7 +52,8 @@ public class OAuth2ErrorHttpMessageConverter extends AbstractHttpMessageConverte
 	private static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() {
 	};
 
-	private GenericHttpMessageConverter<Object> jsonMessageConverter = HttpMessageConverters.getJsonMessageConverter();
+	private final GenericHttpMessageConverter<Object> jsonMessageConverter = HttpMessageConverters
+		.getJsonMessageConverter();
 
 	protected Converter<Map<String, String>, OAuth2Error> errorConverter = new OAuth2ErrorConverter();
 

+ 8 - 0
oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/OAuth2ProtectedResourceMetadataFilter.java

@@ -31,9 +31,11 @@ import org.springframework.http.MediaType;
 import org.springframework.http.converter.GenericHttpMessageConverter;
 import org.springframework.http.converter.HttpMessageNotWritableException;
 import org.springframework.http.converter.json.GsonHttpMessageConverter;
+import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
 import org.springframework.http.converter.json.JsonbHttpMessageConverter;
 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
 import org.springframework.http.server.ServletServerHttpResponse;
+import org.springframework.security.oauth2.core.http.converter.GenericHttpMessageConverterAdapter;
 import org.springframework.security.oauth2.server.resource.OAuth2ProtectedResourceMetadata;
 import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
 import org.springframework.security.web.util.UrlUtils;
@@ -139,6 +141,8 @@ public final class OAuth2ProtectedResourceMetadataFilter extends OncePerRequestF
 
 	private static final class HttpMessageConverters {
 
+		private static final boolean jacksonPresent;
+
 		private static final boolean jackson2Present;
 
 		private static final boolean gsonPresent;
@@ -147,6 +151,7 @@ public final class OAuth2ProtectedResourceMetadataFilter extends OncePerRequestF
 
 		static {
 			ClassLoader classLoader = HttpMessageConverters.class.getClassLoader();
+			jacksonPresent = ClassUtils.isPresent("tools.jackson.databind.json.JsonMapper", classLoader);
 			jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader)
 					&& ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
 			gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
@@ -158,6 +163,9 @@ public final class OAuth2ProtectedResourceMetadataFilter extends OncePerRequestF
 
 		@SuppressWarnings("removal")
 		private static GenericHttpMessageConverter<Object> getJsonMessageConverter() {
+			if (jacksonPresent) {
+				return new GenericHttpMessageConverterAdapter<>(new JacksonJsonHttpMessageConverter());
+			}
 			if (jackson2Present) {
 				return new MappingJackson2HttpMessageConverter();
 			}

+ 99 - 0
web/src/main/java/org/springframework/security/web/http/GenericHttpMessageConverterAdapter.java

@@ -0,0 +1,99 @@
+/*
+ * Copyright 2004-present 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.web.http;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.List;
+
+import org.jspecify.annotations.Nullable;
+
+import org.springframework.core.ResolvableType;
+import org.springframework.http.HttpInputMessage;
+import org.springframework.http.HttpOutputMessage;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.GenericHttpMessageConverter;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.http.converter.HttpMessageNotWritableException;
+import org.springframework.http.converter.SmartHttpMessageConverter;
+
+/**
+ * {@link GenericHttpMessageConverter} implementation that delegates to a
+ * {@link SmartHttpMessageConverter}.
+ *
+ * @param <T> the converted object type
+ * @author Sebastien Deleuze
+ * @since 7.0
+ */
+public class GenericHttpMessageConverterAdapter<T> implements GenericHttpMessageConverter<T> {
+
+	private final SmartHttpMessageConverter<T> smartConverter;
+
+	public GenericHttpMessageConverterAdapter(SmartHttpMessageConverter<T> smartConverter) {
+		this.smartConverter = smartConverter;
+	}
+
+	@Override
+	public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {
+		return this.smartConverter.canRead(ResolvableType.forType(type), mediaType);
+	}
+
+	@Override
+	public T read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
+			throws IOException, HttpMessageNotReadableException {
+		return this.smartConverter.read(ResolvableType.forType(type), inputMessage, null);
+	}
+
+	@Override
+	public boolean canWrite(@Nullable Type type, Class<?> clazz, @Nullable MediaType mediaType) {
+		return this.smartConverter.canWrite(ResolvableType.forType(type), clazz, mediaType);
+	}
+
+	@Override
+	public void write(T t, @Nullable Type type, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
+			throws IOException, HttpMessageNotWritableException {
+		this.smartConverter.write(t, ResolvableType.forType(type), contentType, outputMessage, null);
+	}
+
+	@Override
+	public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {
+		return this.smartConverter.canRead(ResolvableType.forClass(clazz), mediaType);
+	}
+
+	@Override
+	public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
+		return this.smartConverter.canWrite(clazz, mediaType);
+	}
+
+	@Override
+	public List<MediaType> getSupportedMediaTypes() {
+		return this.smartConverter.getSupportedMediaTypes();
+	}
+
+	@Override
+	public T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
+			throws IOException, HttpMessageNotReadableException {
+		return this.smartConverter.read(clazz, inputMessage);
+	}
+
+	@Override
+	public void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
+			throws IOException, HttpMessageNotWritableException {
+		this.smartConverter.write(t, contentType, outputMessage);
+	}
+
+}

+ 20 - 7
webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationFilter.java

@@ -38,6 +38,7 @@ import org.springframework.security.web.authentication.AuthenticationEntryPointF
 import org.springframework.security.web.authentication.HttpMessageConverterAuthenticationSuccessHandler;
 import org.springframework.security.web.authentication.HttpStatusEntryPoint;
 import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
+import org.springframework.security.web.http.GenericHttpMessageConverterAdapter;
 import org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;
 import org.springframework.security.web.webauthn.api.PublicKeyCredential;
 import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
@@ -50,7 +51,8 @@ import static org.springframework.security.web.servlet.util.matcher.PathPatternR
 /**
  * Authenticates {@code PublicKeyCredential<AuthenticatorAssertionResponse>} that is
  * parsed from the body of the {@link HttpServletRequest} using the
- * {@link #setConverter(SmartHttpMessageConverter)}. An example request is provided below:
+ * {@link #setConverter(GenericHttpMessageConverter)}. An example request is provided
+ * below:
  *
  * <pre>
  * {
@@ -72,8 +74,8 @@ import static org.springframework.security.web.servlet.util.matcher.PathPatternR
  */
 public class WebAuthnAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
 
-	private SmartHttpMessageConverter<Object> converter = new JacksonJsonHttpMessageConverter(
-			JsonMapper.builder().addModule(new WebauthnJacksonModule()).build());
+	private GenericHttpMessageConverter<Object> converter = new GenericHttpMessageConverterAdapter<>(
+			new JacksonJsonHttpMessageConverter(JsonMapper.builder().addModule(new WebauthnJacksonModule()).build()));
 
 	private PublicKeyCredentialRequestOptionsRepository requestOptionsRepository = new HttpSessionPublicKeyCredentialRequestOptionsRepository();
 
@@ -94,7 +96,7 @@ public class WebAuthnAuthenticationFilter extends AbstractAuthenticationProcessi
 		PublicKeyCredential<AuthenticatorAssertionResponse> publicKeyCredential = null;
 		try {
 			publicKeyCredential = (PublicKeyCredential<AuthenticatorAssertionResponse>) this.converter
-				.read(resolvableType, httpRequest, null);
+				.read(resolvableType.getType(), getClass(), httpRequest);
 		}
 		catch (Exception ex) {
 			throw new BadCredentialsException("Unable to authenticate the PublicKeyCredential", ex);
@@ -114,15 +116,26 @@ public class WebAuthnAuthenticationFilter extends AbstractAuthenticationProcessi
 	/**
 	 * Sets the {@link GenericHttpMessageConverter} to use for writing
 	 * {@code PublicKeyCredential<AuthenticatorAssertionResponse>} to the response. The
-	 * default is @{code Jackson2HttpMessageConverter}
+	 * default is @{code MappingJackson2HttpMessageConverter}
 	 * @param converter the {@link GenericHttpMessageConverter} to use. Cannot be null.
 	 */
-	// TODO Accept HttpMessageConverter
-	public void setConverter(SmartHttpMessageConverter<Object> converter) {
+	public void setConverter(GenericHttpMessageConverter<Object> converter) {
 		Assert.notNull(converter, "converter cannot be null");
 		this.converter = converter;
 	}
 
+	/**
+	 * Sets the {@link SmartHttpMessageConverter} to use for writing
+	 * {@code PublicKeyCredential<AuthenticatorAssertionResponse>} to the response. The
+	 * default is @{code MappingJackson2HttpMessageConverter}
+	 * @param converter the {@link SmartHttpMessageConverter} to use. Cannot be null.
+	 * @since 7.0
+	 */
+	public void setConverter(SmartHttpMessageConverter<Object> converter) {
+		Assert.notNull(converter, "converter cannot be null");
+		this.converter = new GenericHttpMessageConverterAdapter<>(converter);
+	}
+
 	/**
 	 * Sets the {@link PublicKeyCredentialRequestOptionsRepository} to use. The default is
 	 * {@link HttpSessionPublicKeyCredentialRequestOptionsRepository}.

+ 5 - 1
webauthn/src/test/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationFilterTests.java

@@ -27,6 +27,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
 import org.skyscreamer.jsonassert.JSONAssert;
 
 import org.springframework.http.HttpStatus;
+import org.springframework.http.converter.GenericHttpMessageConverter;
 import org.springframework.http.converter.SmartHttpMessageConverter;
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
@@ -101,7 +102,10 @@ class WebAuthnAuthenticationFilterTests {
 
 	@Test
 	void setConverterWhenNullThenIllegalArgumentException() {
-		assertThatIllegalArgumentException().isThrownBy(() -> this.filter.setConverter(null));
+		assertThatIllegalArgumentException()
+			.isThrownBy(() -> this.filter.setConverter((GenericHttpMessageConverter<Object>) null));
+		assertThatIllegalArgumentException()
+			.isThrownBy(() -> this.filter.setConverter((SmartHttpMessageConverter<Object>) null));
 	}
 
 	@Test