Use bean CL for JdbcMessageStore.deserializer

Related to https://stackoverflow.com/questions/72305387/spring-integration-delayer-starts-sending-null-message-payloads-when-switched

In some async use-cases (e.g. `DelayHandler`), the context classloader
might be different for the data to be deserialized from message store.

* Fix `JdbcMessageStore` to populate a bean `ClassLoader` into default
`AllowListDeserializingConverter` from the application context.
The provided `Deserializer` must ensure such a `ClassLoader` itself
* Add warning message to the `LambdaMessageProcessor` when converter
returns `null` for the payload it cannot convert to expected type.
Cannot be raised as error since some applications may already rely
on the `null` conversion result in their method arguments

**Cherry-pick to `5.5.x`**
This commit is contained in:
Artem Bilan
2022-05-20 11:37:43 -04:00
committed by Gary Russell
parent 469bd6ba04
commit 9f4f91d5f9
5 changed files with 61 additions and 8 deletions

View File

@@ -29,6 +29,7 @@ import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.log.LogMessage;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
@@ -151,7 +152,14 @@ public class LambdaMessageProcessor implements MessageProcessor<Object>, BeanFac
args[i] = message;
}
else {
args[i] = this.messageConverter.fromMessage(message, this.expectedType);
Object payload = this.messageConverter.fromMessage(message, this.expectedType);
if (payload == null && LOGGER.isWarnEnabled()) {
LOGGER.warn(LogMessage.format(
"The '%s' returned 'null' for the payload conversion from the " +
"'%s' and expected type '%s'.",
this.messageConverter, message, this.expectedType));
}
args[i] = payload;
}
}
else {

View File

@@ -41,6 +41,9 @@ import org.springframework.util.PatternMatchUtils;
* classes/packages are deserialized. If you receive data from untrusted sources, consider
* adding trusted classes/packages using {@link #setAllowedPatterns(String...)} or
* {@link #addAllowedPatterns(String...)}.
* <p>
* If a delegate deserializer is a {@link DefaultDeserializer}, only its {@link ClassLoader}
* is used for a {@link ConfigurableObjectInputStream} logic.
*
* @author Gary Russell
* @author Mark Fisher
@@ -133,7 +136,13 @@ public class AllowListDeserializingConverter implements Converter<byte[], Object
return deserialize(byteStream);
}
else {
return this.deserializer.deserialize(byteStream);
Object result = this.deserializer.deserialize(byteStream);
/* Even if there is no knowledge what is the target deserialization algorithm
and malicious code may be executed already, it is still better to fail
with untrusted data rather than just let it pass downstream.
*/
checkAllowList(result.getClass());
return result;
}
}
catch (Exception ex) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2021 the original author or authors.
* Copyright 2016-2022 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.
@@ -19,6 +19,7 @@ package org.springframework.integration.dsl;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import java.util.Date;
import java.util.Objects;
import java.util.function.Function;
@@ -31,6 +32,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.config.IntegrationConverter;
import org.springframework.integration.core.GenericSelector;
import org.springframework.integration.handler.GenericHandler;
import org.springframework.integration.handler.LambdaMessageProcessor;
import org.springframework.integration.transformer.GenericTransformer;
@@ -85,6 +87,24 @@ public class LambdaMessageProcessorTests {
.isThrownBy(() -> lmp.processMessage(testMessage));
}
@Test
public void testConversionToNull() {
LambdaMessageProcessor lmp = new LambdaMessageProcessor(
new GenericSelector<Date>() { // Must not be lambda
@Override
public boolean accept(Date payload) {
return payload == null;
}
}, Date.class);
lmp.setBeanFactory(this.beanFactory);
GenericMessage<String> testMessage = new GenericMessage<>("foo");
Object result = lmp.processMessage(testMessage);
assertThat(result).isEqualTo(Boolean.TRUE);
}
@Test
public void testCustomConverter() {
LambdaMessageProcessor lmp = new LambdaMessageProcessor(Function.identity(), TestPojo.class);