INT-4342: White List for Payload Deserializer
JIRA: https://jira.spring.io/browse/INT-4342 Use similar code to Spring AMQP to add white list support for Integration's use of the `DeserializingMessageConverter`; introduce the `WhiteListDeserializingMessageConverter`. Polishing Missed this change in PR. Fix XSD attribute
This commit is contained in:
committed by
Artem Bilan
parent
5749c5b237
commit
0d495294ed
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -26,6 +26,7 @@ import org.springframework.integration.transformer.PayloadDeserializingTransform
|
||||
* Parser for the 'payload-deserializing-transformer' element.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class PayloadDeserializingTransformerParser extends AbstractTransformerParser {
|
||||
|
||||
@@ -37,6 +38,7 @@ public class PayloadDeserializingTransformerParser extends AbstractTransformerPa
|
||||
@Override
|
||||
protected void parseTransformer(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "deserializer");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "white-list", "whiteListPatterns");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -165,12 +165,14 @@ public abstract class Transformers {
|
||||
return transformer;
|
||||
}
|
||||
|
||||
public static PayloadDeserializingTransformer deserializer() {
|
||||
return deserializer(null);
|
||||
public static PayloadDeserializingTransformer deserializer(String... whiteListPatterns) {
|
||||
return deserializer(null, whiteListPatterns);
|
||||
}
|
||||
|
||||
public static PayloadDeserializingTransformer deserializer(Deserializer<Object> deserializer) {
|
||||
public static PayloadDeserializingTransformer deserializer(Deserializer<Object> deserializer,
|
||||
String... whiteListPatterns) {
|
||||
PayloadDeserializingTransformer transformer = new PayloadDeserializingTransformer();
|
||||
transformer.setWhiteListPatterns(whiteListPatterns);
|
||||
if (deserializer != null) {
|
||||
transformer.setDeserializer(deserializer);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright 2002-2017 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
|
||||
*
|
||||
* http://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.integration.support.converter;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectStreamClass;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.core.ConfigurableObjectInputStream;
|
||||
import org.springframework.core.NestedIOException;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.serializer.DefaultDeserializer;
|
||||
import org.springframework.core.serializer.Deserializer;
|
||||
import org.springframework.core.serializer.support.SerializationFailedException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.PatternMatchUtils;
|
||||
|
||||
/**
|
||||
* A {@link Converter} that delegates to a
|
||||
* {@link org.springframework.core.serializer.Deserializer} to convert data in a byte
|
||||
* array to an object. By default, if using a {@link DefaultDeserializer} all
|
||||
* classes/packages are deserialized. If you receive data from untrusted sources, consider
|
||||
* adding trusted classes/packages using {@link #setWhiteListPatterns(String...)} or
|
||||
* {@link #addWhiteListPatterns(String...)}.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @author Mark Fisher
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.2.13
|
||||
*/
|
||||
public class WhiteListDeserializingConverter implements Converter<byte[], Object> {
|
||||
|
||||
private final Deserializer<Object> deserializer;
|
||||
|
||||
private final ClassLoader defaultDeserializerClassLoader;
|
||||
|
||||
private final boolean usingDefaultDeserializer;
|
||||
|
||||
private final Set<String> whiteListPatterns = new LinkedHashSet<String>();
|
||||
|
||||
|
||||
/**
|
||||
* Create a {@code WhiteListDeserializingConverter} with default
|
||||
* {@link java.io.ObjectInputStream} configuration, using the "latest user-defined
|
||||
* ClassLoader".
|
||||
*/
|
||||
public WhiteListDeserializingConverter() {
|
||||
this(new DefaultDeserializer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code WhiteListDeserializingConverter} for using an
|
||||
* {@link java.io.ObjectInputStream} with the given {@code ClassLoader}.
|
||||
* @param classLoader the class loader to use for deserialization.
|
||||
*/
|
||||
public WhiteListDeserializingConverter(ClassLoader classLoader) {
|
||||
this(new DefaultDeserializer(classLoader));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code WhiteListDeserializingConverter} that delegates to the provided
|
||||
* {@link Deserializer}.
|
||||
* @param deserializer the deserializer to use.
|
||||
*/
|
||||
public WhiteListDeserializingConverter(Deserializer<Object> deserializer) {
|
||||
Assert.notNull(deserializer, "Deserializer must not be null");
|
||||
this.deserializer = deserializer;
|
||||
if (deserializer instanceof DefaultDeserializer) {
|
||||
ClassLoader classLoader = null;
|
||||
try {
|
||||
classLoader = (ClassLoader) new DirectFieldAccessor(deserializer).getPropertyValue("classLoader");
|
||||
}
|
||||
catch (Exception e) {
|
||||
// no-op
|
||||
}
|
||||
this.defaultDeserializerClassLoader = classLoader;
|
||||
this.usingDefaultDeserializer = true;
|
||||
}
|
||||
else {
|
||||
this.defaultDeserializerClassLoader = null;
|
||||
this.usingDefaultDeserializer = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set simple patterns for allowable packages/classes for deserialization.
|
||||
* The patterns will be applied in order until a match is found.
|
||||
* A class can be fully qualified or a wildcard '*' is allowed at the
|
||||
* beginning or end of the class name.
|
||||
* Examples: {@code com.foo.*}, {@code *.MyClass}.
|
||||
* @param whiteListPatterns the patterns.
|
||||
*/
|
||||
public void setWhiteListPatterns(String... whiteListPatterns) {
|
||||
this.whiteListPatterns.clear();
|
||||
Collections.addAll(this.whiteListPatterns, whiteListPatterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add package/class patterns to the white list.
|
||||
* @param patterns the patterns to add.
|
||||
* @see #setWhiteListPatterns(String...)
|
||||
*/
|
||||
public void addWhiteListPatterns(String... patterns) {
|
||||
Collections.addAll(this.whiteListPatterns, patterns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convert(byte[] source) {
|
||||
ByteArrayInputStream byteStream = new ByteArrayInputStream(source);
|
||||
try {
|
||||
if (this.usingDefaultDeserializer) {
|
||||
return deserialize(byteStream);
|
||||
}
|
||||
else {
|
||||
return this.deserializer.deserialize(byteStream);
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new SerializationFailedException("Failed to deserialize payload. " +
|
||||
"Is the byte array a result of corresponding serialization for " +
|
||||
this.deserializer.getClass().getSimpleName() + "?", ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected Object deserialize(ByteArrayInputStream inputStream) throws IOException {
|
||||
try {
|
||||
ObjectInputStream objectInputStream = new ConfigurableObjectInputStream(inputStream,
|
||||
this.defaultDeserializerClassLoader) {
|
||||
|
||||
@Override
|
||||
protected Class<?> resolveClass(ObjectStreamClass classDesc)
|
||||
throws IOException, ClassNotFoundException {
|
||||
Class<?> clazz = super.resolveClass(classDesc);
|
||||
checkWhiteList(clazz);
|
||||
return clazz;
|
||||
}
|
||||
|
||||
};
|
||||
return objectInputStream.readObject();
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new NestedIOException("Failed to deserialize object type", ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkWhiteList(Class<?> clazz) throws IOException {
|
||||
if (this.whiteListPatterns.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (clazz.isArray() || clazz.isPrimitive() || clazz.equals(String.class)
|
||||
|| Number.class.isAssignableFrom(clazz)) {
|
||||
return;
|
||||
}
|
||||
String className = clazz.getName();
|
||||
for (String pattern : this.whiteListPatterns) {
|
||||
if (PatternMatchUtils.simpleMatch(pattern, className)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new SecurityException("Attempt to deserialize unauthorized " + clazz);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -16,15 +16,18 @@
|
||||
|
||||
package org.springframework.integration.transformer;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.serializer.Deserializer;
|
||||
import org.springframework.core.serializer.support.DeserializingConverter;
|
||||
import org.springframework.integration.support.converter.WhiteListDeserializingConverter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Transformer that deserializes the inbound byte array payload to an object by delegating to a
|
||||
* Converter<byte[], Object>. Default delegate is a {@link DeserializingConverter} using
|
||||
* Java serialization.
|
||||
* Transformer that deserializes the inbound byte array payload to an object by delegating
|
||||
* to a Converter<byte[], Object>. Default delegate is a
|
||||
* {@link WhiteListDeserializingConverter} using Java serialization.
|
||||
*
|
||||
* <p>The byte array payload must be a result of equivalent serialization.
|
||||
* <p>
|
||||
* The byte array payload must be a result of equivalent serialization.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
@@ -32,15 +35,36 @@ import org.springframework.core.serializer.support.DeserializingConverter;
|
||||
*/
|
||||
public class PayloadDeserializingTransformer extends PayloadTypeConvertingTransformer<byte[], Object> {
|
||||
|
||||
|
||||
public PayloadDeserializingTransformer() {
|
||||
doSetConverter(new WhiteListDeserializingConverter());
|
||||
}
|
||||
|
||||
private void doSetConverter(Converter<byte[], Object> converter) {
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
public void setDeserializer(Deserializer<Object> deserializer) {
|
||||
this.setConverter(new DeserializingConverter(deserializer));
|
||||
setConverter(new WhiteListDeserializingConverter(deserializer));
|
||||
}
|
||||
|
||||
/**
|
||||
* When using a {@link WhiteListDeserializingConverter} (the default) add patterns
|
||||
* for packages/classes that are allowed to be deserialized.
|
||||
* A class can be fully qualified or a wildcard '*' is allowed at the
|
||||
* beginning or end of the class name.
|
||||
* Examples: {@code com.foo.*}, {@code *.MyClass}.
|
||||
* @param patterns the patterns.
|
||||
* @since 4.2.13
|
||||
*/
|
||||
public void setWhiteListPatterns(String... patterns) {
|
||||
Assert.isTrue(this.converter instanceof WhiteListDeserializingConverter,
|
||||
"Patterns can only be provided when using a 'WhiteListDeserializingConverter'");
|
||||
((WhiteListDeserializingConverter) this.converter).setWhiteListPatterns(patterns);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object transformPayload(byte[] payload) throws Exception {
|
||||
if (this.converter == null) {
|
||||
this.setConverter(new DeserializingConverter());
|
||||
}
|
||||
return this.converter.convert(payload);
|
||||
}
|
||||
|
||||
|
||||
@@ -2696,7 +2696,7 @@
|
||||
<xsd:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:element ref="poller" />
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="deserializer" use="optional">
|
||||
<xsd:attribute name="deserializer">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Reference to a Deserializer instance to convert from a byte array to an object.
|
||||
@@ -2710,6 +2710,15 @@
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="white-list">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
When using the default Deserializer, a list of package/class patterns indicating
|
||||
classes that are allowed to be deserialized. Consider providing this if you receive
|
||||
data from untrusted sources. Example: "com.mycom.*, com.yourcom.*".
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="id" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
|
||||
|
||||
@@ -19,14 +19,17 @@
|
||||
<queue capacity="1"/>
|
||||
</channel>
|
||||
|
||||
<payload-deserializing-transformer input-channel="directInput" output-channel="output"/>
|
||||
<payload-deserializing-transformer id="direct" input-channel="directInput" output-channel="output"
|
||||
white-list="*" />
|
||||
|
||||
<payload-deserializing-transformer input-channel="queueInput" output-channel="output">
|
||||
<poller fixed-delay="10000"/>
|
||||
</payload-deserializing-transformer>
|
||||
|
||||
<payload-deserializing-transformer input-channel="customDeserializerInput" output-channel="output" deserializer="customDeserializer"/>
|
||||
<payload-deserializing-transformer input-channel="customDeserializerInput" output-channel="output"
|
||||
deserializer="customDeserializer"/>
|
||||
|
||||
<beans:bean id="customDeserializer" class="org.springframework.integration.config.xml.PayloadDeserializingTransformerParserTests$TestDeserializer"/>
|
||||
<beans:bean id="customDeserializer"
|
||||
class="org.springframework.integration.config.xml.PayloadDeserializingTransformerParserTests$TestDeserializer"/>
|
||||
|
||||
</beans:beans>
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
|
||||
package org.springframework.integration.config.xml;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -26,15 +28,19 @@ import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.core.serializer.Deserializer;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.integration.transformer.MessageTransformationException;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
@@ -60,6 +66,10 @@ public class PayloadDeserializingTransformerParserTests {
|
||||
@Autowired
|
||||
private PollableChannel output;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("direct.handler")
|
||||
private MessageHandler handler;
|
||||
|
||||
|
||||
@Test
|
||||
public void directChannelWithSerializedStringMessage() throws Exception {
|
||||
@@ -69,6 +79,10 @@ public class PayloadDeserializingTransformerParserTests {
|
||||
assertNotNull(result);
|
||||
assertTrue(result.getPayload() instanceof String);
|
||||
assertEquals("foo", result.getPayload());
|
||||
Set<?> patterns = TestUtils.getPropertyValue(this.handler, "transformer.converter.whiteListPatterns",
|
||||
Set.class);
|
||||
assertThat(patterns.size(), equalTo(1));
|
||||
assertThat(patterns.iterator().next(), equalTo("*"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -26,6 +26,7 @@ import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -61,6 +62,7 @@ import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.integration.dsl.IntegrationFlow;
|
||||
import org.springframework.integration.dsl.IntegrationFlows;
|
||||
import org.springframework.integration.dsl.Pollers;
|
||||
import org.springframework.integration.dsl.Transformers;
|
||||
import org.springframework.integration.dsl.channel.MessageChannels;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.handler.GenericHandler;
|
||||
@@ -72,7 +74,6 @@ import org.springframework.integration.store.MessageStore;
|
||||
import org.springframework.integration.store.SimpleMessageStore;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.support.MutableMessageBuilder;
|
||||
import org.springframework.integration.transformer.PayloadDeserializingTransformer;
|
||||
import org.springframework.integration.transformer.PayloadSerializingTransformer;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
@@ -592,10 +593,12 @@ public class IntegrationFlowTests {
|
||||
.channel("foo")
|
||||
.fixedSubscriberChannel()
|
||||
.<String, Integer>transform(Integer::parseInt)
|
||||
.<Integer, Foo>transform(i -> new Foo(i))
|
||||
.transform(new PayloadSerializingTransformer(),
|
||||
c -> c.autoStartup(false).id("payloadSerializingTransformer"))
|
||||
.channel(MessageChannels.queue(new SimpleMessageStore(), "fooQueue"))
|
||||
.transform(new PayloadDeserializingTransformer())
|
||||
.transform(Transformers.deserializer(Foo.class.getName()))
|
||||
.<Foo, Integer>transform(f -> f.value)
|
||||
.filter("true", e -> e.id("expressionFilter"))
|
||||
.channel(publishSubscribeChannel())
|
||||
.transform((Integer p) -> p * 2, c -> c.advice(this.expressionAdvice()))
|
||||
@@ -863,5 +866,16 @@ public class IntegrationFlowTests {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static class Foo implements Serializable {
|
||||
|
||||
private final Integer value;
|
||||
|
||||
public Foo(Integer value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,12 @@
|
||||
|
||||
package org.springframework.integration.transformer;
|
||||
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
@@ -63,6 +67,31 @@ public class PayloadDeserializingTransformerTests {
|
||||
assertEquals(testBean.name, ((TestBean) payload).name);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeObjectWhiteList() throws Exception {
|
||||
TestBean testBean = new TestBean("test");
|
||||
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||
ObjectOutputStream objectStream = new ObjectOutputStream(byteStream);
|
||||
objectStream.writeObject(testBean);
|
||||
byte[] serialized = byteStream.toByteArray();
|
||||
PayloadDeserializingTransformer transformer = new PayloadDeserializingTransformer();
|
||||
transformer.setWhiteListPatterns("com.*");
|
||||
try {
|
||||
transformer.transform(new GenericMessage<byte[]>(serialized));
|
||||
fail("expected security exception");
|
||||
}
|
||||
catch (MessageTransformationException e) {
|
||||
assertThat(e.getCause().getCause(), instanceOf(SecurityException.class));
|
||||
assertThat(e.getCause().getCause().getMessage(), startsWith("Attempt to deserialize unauthorized"));
|
||||
}
|
||||
transformer.setWhiteListPatterns("org.*");
|
||||
Message<?> result = transformer.transform(new GenericMessage<byte[]>(serialized));
|
||||
Object payload = result.getPayload();
|
||||
assertNotNull(payload);
|
||||
assertEquals(TestBean.class, payload.getClass());
|
||||
assertEquals(testBean.name, ((TestBean) payload).name);
|
||||
}
|
||||
|
||||
@Test(expected = MessageTransformationException.class)
|
||||
public void invalidPayload() {
|
||||
byte[] bytes = new byte[] { 1, 2, 3 };
|
||||
|
||||
@@ -38,7 +38,6 @@ import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.serializer.Deserializer;
|
||||
import org.springframework.core.serializer.Serializer;
|
||||
import org.springframework.core.serializer.support.DeserializingConverter;
|
||||
import org.springframework.core.serializer.support.SerializingConverter;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.integration.IntegrationMessageHeaderAccessor;
|
||||
@@ -51,6 +50,7 @@ import org.springframework.integration.store.MessageGroupStore;
|
||||
import org.springframework.integration.store.MessageStore;
|
||||
import org.springframework.integration.store.PriorityCapableChannelMessageStore;
|
||||
import org.springframework.integration.store.SimpleMessageGroupFactory;
|
||||
import org.springframework.integration.support.converter.WhiteListDeserializingConverter;
|
||||
import org.springframework.integration.transaction.TransactionSynchronizationFactory;
|
||||
import org.springframework.integration.util.UUIDConverter;
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
@@ -137,7 +137,7 @@ public class JdbcChannelMessageStore implements PriorityCapableChannelMessageSto
|
||||
|
||||
private volatile JdbcTemplate jdbcTemplate;
|
||||
|
||||
private volatile DeserializingConverter deserializer;
|
||||
private volatile WhiteListDeserializingConverter deserializer;
|
||||
|
||||
private volatile SerializingConverter serializer;
|
||||
|
||||
@@ -159,7 +159,7 @@ public class JdbcChannelMessageStore implements PriorityCapableChannelMessageSto
|
||||
* Convenient constructor for configuration use.
|
||||
*/
|
||||
public JdbcChannelMessageStore() {
|
||||
this.deserializer = new DeserializingConverter();
|
||||
this.deserializer = new WhiteListDeserializingConverter();
|
||||
this.serializer = new SerializingConverter();
|
||||
}
|
||||
|
||||
@@ -202,7 +202,18 @@ public class JdbcChannelMessageStore implements PriorityCapableChannelMessageSto
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void setDeserializer(Deserializer<? extends Message<?>> deserializer) {
|
||||
this.deserializer = new DeserializingConverter((Deserializer) deserializer);
|
||||
this.deserializer = new WhiteListDeserializingConverter((Deserializer) deserializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add patterns for packages/classes that are allowed to be deserialized. A class can
|
||||
* be fully qualified or a wildcard '*' is allowed at the beginning or end of the
|
||||
* class name. Examples: {@code com.foo.*}, {@code *.MyClass}.
|
||||
* @param patterns the patterns.
|
||||
* @since 4.2.13
|
||||
*/
|
||||
public void addWhiteListPatterns(String... patterns) {
|
||||
this.deserializer.addWhiteListPatterns(patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,7 +37,6 @@ import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.serializer.Deserializer;
|
||||
import org.springframework.core.serializer.Serializer;
|
||||
import org.springframework.core.serializer.support.DeserializingConverter;
|
||||
import org.springframework.core.serializer.support.SerializingConverter;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.integration.store.AbstractMessageGroupStore;
|
||||
@@ -45,6 +44,7 @@ import org.springframework.integration.store.MessageGroup;
|
||||
import org.springframework.integration.store.MessageMetadata;
|
||||
import org.springframework.integration.store.MessageStore;
|
||||
import org.springframework.integration.store.SimpleMessageGroup;
|
||||
import org.springframework.integration.support.converter.WhiteListDeserializingConverter;
|
||||
import org.springframework.integration.util.UUIDConverter;
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
@@ -189,7 +189,7 @@ public class JdbcMessageStore extends AbstractMessageGroupStore implements Messa
|
||||
|
||||
private final JdbcOperations jdbcTemplate;
|
||||
|
||||
private volatile DeserializingConverter deserializer;
|
||||
private volatile WhiteListDeserializingConverter deserializer;
|
||||
|
||||
private volatile SerializingConverter serializer;
|
||||
|
||||
@@ -213,7 +213,7 @@ public class JdbcMessageStore extends AbstractMessageGroupStore implements Messa
|
||||
public JdbcMessageStore(JdbcOperations jdbcOperations) {
|
||||
Assert.notNull(jdbcOperations, "'dataSource' must not be null");
|
||||
this.jdbcTemplate = jdbcOperations;
|
||||
this.deserializer = new DeserializingConverter();
|
||||
this.deserializer = new WhiteListDeserializingConverter();
|
||||
this.serializer = new SerializingConverter();
|
||||
}
|
||||
|
||||
@@ -265,7 +265,18 @@ public class JdbcMessageStore extends AbstractMessageGroupStore implements Messa
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void setDeserializer(Deserializer<? extends Message<?>> deserializer) {
|
||||
this.deserializer = new DeserializingConverter((Deserializer) deserializer);
|
||||
this.deserializer = new WhiteListDeserializingConverter((Deserializer) deserializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add patterns for packages/classes that are allowed to be deserialized. A class can
|
||||
* be fully qualified or a wildcard '*' is allowed at the beginning or end of the
|
||||
* class name. Examples: {@code com.foo.*}, {@code *.MyClass}.
|
||||
* @param patterns the patterns.
|
||||
* @since 4.2.13
|
||||
*/
|
||||
public void addWhiteListPatterns(String... patterns) {
|
||||
this.deserializer.addWhiteListPatterns(patterns);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,7 +19,7 @@ package org.springframework.integration.jdbc.store.channel;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.springframework.core.serializer.support.DeserializingConverter;
|
||||
import org.springframework.integration.support.converter.WhiteListDeserializingConverter;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.support.lob.LobHandler;
|
||||
import org.springframework.messaging.Message;
|
||||
@@ -30,20 +30,22 @@ import org.springframework.messaging.Message;
|
||||
* that select clause ordering is unimportant.
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
* @author Gary Russell
|
||||
* @since 2.2
|
||||
*
|
||||
*/
|
||||
public class MessageRowMapper implements RowMapper<Message<?>> {
|
||||
|
||||
private final DeserializingConverter deserializer;
|
||||
private final WhiteListDeserializingConverter deserializer;
|
||||
|
||||
private final LobHandler lobHandler;
|
||||
|
||||
public MessageRowMapper(DeserializingConverter deserializer, LobHandler lobHandler) {
|
||||
public MessageRowMapper(WhiteListDeserializingConverter deserializer, LobHandler lobHandler) {
|
||||
this.deserializer = deserializer;
|
||||
this.lobHandler = lobHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<?> mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
return (Message<?>) this.deserializer.convert(this.lobHandler.getBlobAsBytes(rs, "MESSAGE_BYTES"));
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.serializer.support.DeserializingConverter;
|
||||
import org.springframework.core.serializer.support.SerializingConverter;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Transient;
|
||||
@@ -71,6 +70,7 @@ import org.springframework.integration.store.MessageStore;
|
||||
import org.springframework.integration.store.SimpleMessageGroup;
|
||||
import org.springframework.integration.support.MutableMessage;
|
||||
import org.springframework.integration.support.MutableMessageBuilder;
|
||||
import org.springframework.integration.support.converter.WhiteListDeserializingConverter;
|
||||
import org.springframework.jmx.export.annotation.ManagedAttribute;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
@@ -144,6 +144,8 @@ public class MongoDbMessageStore extends AbstractMessageGroupStore
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private String[] whiteListPatterns;
|
||||
|
||||
|
||||
/**
|
||||
* Create a MongoDbMessageStore using the provided {@link MongoDbFactory}.and the default collection name.
|
||||
@@ -177,6 +179,16 @@ public class MongoDbMessageStore extends AbstractMessageGroupStore
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add patterns for packages/classes that are allowed to be deserialized. A class can
|
||||
* be fully qualified or a wildcard '*' is allowed at the beginning or end of the
|
||||
* class name. Examples: {@code com.foo.*}, {@code *.MyClass}.
|
||||
* @param patterns the patterns.
|
||||
*/
|
||||
public void addWhiteListPatterns(String... patterns) {
|
||||
this.whiteListPatterns = patterns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (this.applicationContext != null) {
|
||||
@@ -504,7 +516,12 @@ public class MongoDbMessageStore extends AbstractMessageGroupStore
|
||||
customConverters.add(new MessageHistoryToDocumentConverter());
|
||||
customConverters.add(new DocumentToGenericMessageConverter());
|
||||
customConverters.add(new DocumentToMutableMessageConverter());
|
||||
customConverters.add(new DocumentToErrorMessageConverter());
|
||||
DocumentToErrorMessageConverter docToErrorMessageConverter = new DocumentToErrorMessageConverter();
|
||||
if (MongoDbMessageStore.this.whiteListPatterns != null) {
|
||||
docToErrorMessageConverter.deserializingConverter
|
||||
.addWhiteListPatterns(MongoDbMessageStore.this.whiteListPatterns);
|
||||
}
|
||||
customConverters.add(docToErrorMessageConverter);
|
||||
customConverters.add(new DocumentToAdviceMessageConverter());
|
||||
customConverters.add(new ThrowableToBytesConverter());
|
||||
this.setCustomConversions(new MongoCustomConversions(customConverters));
|
||||
@@ -730,7 +747,7 @@ public class MongoDbMessageStore extends AbstractMessageGroupStore
|
||||
@ReadingConverter
|
||||
private class DocumentToErrorMessageConverter implements Converter<Document, ErrorMessage> {
|
||||
|
||||
private final Converter<byte[], Object> deserializingConverter = new DeserializingConverter();
|
||||
private final WhiteListDeserializingConverter deserializingConverter = new WhiteListDeserializingConverter();
|
||||
|
||||
DocumentToErrorMessageConverter() {
|
||||
super();
|
||||
|
||||
@@ -19,22 +19,33 @@ package org.springframework.integration.mongodb.support;
|
||||
import org.bson.types.Binary;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.serializer.support.DeserializingConverter;
|
||||
import org.springframework.data.convert.ReadingConverter;
|
||||
import org.springframework.integration.support.converter.WhiteListDeserializingConverter;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
* @author Gary Russell
|
||||
* @since 5.0
|
||||
*/
|
||||
@ReadingConverter
|
||||
public class BinaryToMessageConverter implements Converter<Binary, Message<?>> {
|
||||
|
||||
private final Converter<byte[], Object> deserializingConverter = new DeserializingConverter();
|
||||
private final WhiteListDeserializingConverter deserializingConverter = new WhiteListDeserializingConverter();
|
||||
|
||||
@Override
|
||||
public Message<?> convert(Binary source) {
|
||||
return (Message<?>) this.deserializingConverter.convert(source.getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add patterns for packages/classes that are allowed to be deserialized. A class can
|
||||
* be fully qualified or a wildcard '*' is allowed at the beginning or end of the
|
||||
* class name. Examples: {@code com.foo.*}, {@code *.MyClass}.
|
||||
* @param patterns the patterns.
|
||||
*/
|
||||
public void addWhiteListPatterns(String... patterns) {
|
||||
this.deserializingConverter.addWhiteListPatterns(patterns);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,10 +24,10 @@ import org.bson.types.Binary;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.GenericConverter;
|
||||
import org.springframework.core.serializer.support.DeserializingConverter;
|
||||
import org.springframework.core.serializer.support.SerializingConverter;
|
||||
import org.springframework.data.convert.ReadingConverter;
|
||||
import org.springframework.data.convert.WritingConverter;
|
||||
import org.springframework.integration.support.converter.WhiteListDeserializingConverter;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
@@ -36,6 +36,7 @@ import org.springframework.messaging.Message;
|
||||
* And vice versa - to convert {@link byte[]} from the MongoDB to the {@link Message}.
|
||||
|
||||
* @author Artem Bilan
|
||||
* @author Gary Russell
|
||||
* @since 4.2.10
|
||||
* @deprecated since 5.0 in favor of {@link MessageToBinaryConverter} and {@link BinaryToMessageConverter}
|
||||
*/
|
||||
@@ -46,7 +47,7 @@ public class MongoDbMessageBytesConverter implements GenericConverter {
|
||||
|
||||
private final Converter<Object, byte[]> serializingConverter = new SerializingConverter();
|
||||
|
||||
private final Converter<byte[], Object> deserializingConverter = new DeserializingConverter();
|
||||
private final WhiteListDeserializingConverter deserializingConverter = new WhiteListDeserializingConverter();
|
||||
|
||||
@Override
|
||||
public Set<ConvertiblePair> getConvertibleTypes() {
|
||||
@@ -66,4 +67,14 @@ public class MongoDbMessageBytesConverter implements GenericConverter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add patterns for packages/classes that are allowed to be deserialized. A class can
|
||||
* be fully qualified or a wildcard '*' is allowed at the beginning or end of the
|
||||
* class name. Examples: {@code com.foo.*}, {@code *.MyClass}.
|
||||
* @param patterns the patterns.
|
||||
*/
|
||||
public void addWhiteListPatterns(String... patterns) {
|
||||
this.deserializingConverter.addWhiteListPatterns(patterns);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -116,9 +116,13 @@ These will use standard Java serialization by default, but you can provide an im
|
||||
----
|
||||
<int:payload-serializing-transformer input-channel="objectsIn" output-channel="bytesOut"/>
|
||||
|
||||
<int:payload-deserializing-transformer input-channel="bytesIn" output-channel="objectsOut"/>
|
||||
<int:payload-deserializing-transformer input-channel="bytesIn" output-channel="objectsOut"
|
||||
white-list="com.mycom.*,com.yourcom.*"/>
|
||||
----
|
||||
|
||||
IMPORTANT: When deserializing data from untrusted sources, you should consider adding a `white-list` of package/class patterns.
|
||||
By default, all classes will be deserialized.
|
||||
|
||||
====== Object-to-Map and Map-to-Object Transformers
|
||||
|
||||
Spring Integration also provides _Object-to-Map_ and _Map-to-Object_ transformers which utilize the JSON to serialize and de-serialize the object graphs.
|
||||
|
||||
Reference in New Issue
Block a user