Setting a Filter Factory with setSerialFilterFactory
When you set a filter factory by calling the method ObjectInputFilter.Config.setSerialFilterFactory, the filter factory's method
BinaryOperator<ObjectInputFilter>.apply(ObjectInputFilter t,
ObjectInputFilter u)
will be invoked when an ObjectInputStream
is constructed and when a stream-specific filter is set on an
ObjectInputStream
. The parameter t
is the current
filter and u
is the requested filter. When apply is first invoked, t
will be null. If a JVM-wide filter
has been set, then when apply is first invoked,
u
will be the JVM-wide filter. Otherwise, u
will be
null. The apply method (which you must implement yourself)
returns the filter to be used for the stream. If apply is
invoked again, then the parameter t
will be this returned filter. When you
set a filter with the method ObjectInputStream.setObjectInputFilter(ObjectInputFilter), then parameter
u
will be this filter.
The following example implements a simple filter factory that prints its
ObjectInputFilter parameters every time its
apply
method is invoked, merges these parameters into one combined
filter, then returns this merged filter.
public class SimpleFilterFactory {
static class MySimpleFilterFactory implements BinaryOperator<ObjectInputFilter> {
public ObjectInputFilter apply(
ObjectInputFilter curr, ObjectInputFilter next) {
System.out.println("Current filter: " + curr);
System.out.println("Requested filter: " + next);
return ObjectInputFilter.merge(next, curr);
}
}
private static byte[] createSimpleStream(Object obj) {
ByteArrayOutputStream boas = new ByteArrayOutputStream();
try (ObjectOutputStream ois = new ObjectOutputStream(boas)) {
ois.writeObject(obj);
return boas.toByteArray();
} catch (IOException ioe) {
ioe.printStackTrace();
}
throw new RuntimeException();
}
public static void main(String[] args) throws IOException {
// Set a filter factory
MySimpleFilterFactory contextFilterFactory = new MySimpleFilterFactory();
ObjectInputFilter.Config.setSerialFilterFactory(contextFilterFactory);
// Set a stream-specific filter
ObjectInputFilter filter1 =
ObjectInputFilter.Config.createFilter("example.*;java.base/*;!*");
ObjectInputFilter.Config.setSerialFilter(filter1);
// Create another filter
ObjectInputFilter intFilter = ObjectInputFilter.allowFilter(
cl -> cl.equals(Integer.class), ObjectInputFilter.Status.UNDECIDED);
// Create input stream
byte[] intByteStream = createSimpleStream(42);
InputStream is = new ByteArrayInputStream(intByteStream);
ObjectInputStream ois = new ObjectInputStream(is);
ois.setObjectInputFilter(intFilter);
try {
Object obj = ois.readObject();
System.out.println("Read obj: " + obj);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
This example prints output similar to the following (line breaks have been added for clarity):
Current filter: null
Requested filter: example.*;java.base/*;!*
Current filter: example.*;java.base/*;!*
Requested filter:
merge(
predicate(
SimpleFilterFactory$$Lambda$8/0x0000000800c00c60@76ed5528,
ifTrue: ALLOWED, ifFalse: UNDECIDED),
predicate(
SimpleFilterFactory$$Lambda$9/0x0000000800c01800@2c7b84de,
ifTrue: REJECTED, ifFalse: UNDECIDED))
Read obj: 42
The apply
method is invoked twice: when the ObjectInputStream
ois
is created and when the method
setObjectInputFilter
is called.
Note:
- You can set a filter on an ObjectInputStream only once. An
IllegalStateException
will be thrown otherwise. - To protect against unexpected deserializations, ensure that security experts thoroughly review how your filter factories select and combine filters.