Implementing a custom pattern-based ObjectInputFilter – Java I/O: Context-Specific Deserialization Filters
128. Implementing a custom pattern-based ObjectInputFilter
Let’s assume that we already have the Melon class and the helper methods for serializing/deserializing objects to/from byte arrays from Problem 124.Creating a pattern-based filter via the ObjectInputFilter API can be done by calling the Config.createFilter(String pattern) method. For instance, the following filter rejects the modern.challenge.Melon class:
ObjectInputFilter melonFilter = ObjectInputFilter.Config
.createFilter(“!modern.challenge.Melon;”);
We can set this filter as a stream-global filter via setSerialFilter() as follows:
ObjectInputFilter.Config.setSerialFilter(melonFilter);
If need to get access to a stream-global filter then call getSerialFilter():
ObjectInputFilter serialFilter =
ObjectInputFilter.Config.getSerialFilter();
Any stream deserialization in this application will pass through this filter which will reject any instance of modern.challenge.Melon. You can practice this filter in the bundled code.On the other hand, if we want to set this on a specific stream then we can modify our Converters.bytesToObject() method to accept a filter as follows:
public static Object bytesToObject(byte[] bytes,
ObjectInputFilter filter)
throws IOException, ClassNotFoundException {
try ( InputStream is = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(is)) {
// set the filter
ois.setObjectInputFilter(filter);
return ois.readObject();
}
}
If we pass null as filter then the filter will not be applied. Otherwise, the passed filter will be applied to the current stream:
Melon melon = new Melon(“Gac”, 2500);
// serialization works as usual
byte[] melonSer = Converters.objectToBytes(melon);
// here, we pass the melonFilter, which rejects the instances // of modern.challenge.Melon, so deserialization is rejected
Melon melonDeser = (Melon) Converters.bytesToObject(
melonSer, melonFilter);
In this example, the melonFilter will reject deserialization and the output will be as follows:
Exception in thread “main” java.io.InvalidClassException: filter status: REJECTED
…
You can also practice this filter in the bundled code.
129. Implementing a custom class ObjectInputFilter
Let’s assume that we already have the Melon class and the helper methods for serializing/deserializing objects to/from byte arrays from Problem 124.An ObjectInputFilter can be written via a dedicated class by implementing the ObjectInputFilter functional interface as in the following example:
public final class MelonFilter implements ObjectInputFilter {
@Override
public Status checkInput(FilterInfo filterInfo) {
Class<?> clazz = filterInfo.serialClass();
if (clazz != null) {
// or, clazz.getName().equals(“modern.challenge.Melon”)
return
!(clazz.getPackage().getName().equals(“modern.challenge”)
&& clazz.getSimpleName().equals(“Melon”))
? Status.ALLOWED : Status.REJECTED;
}
return Status.UNDECIDED;
}
}
This filter is exactly the same as the pattern-based filter, !modern.challenge.Melon, only that it is expressed via Java Reflection.We can set this filter as a stream-global filter as follows:
ObjectInputFilter.Config.setSerialFilter(new MelonFilter());
Or, as a stream-specific filter as follows:
Melon melonDeser = (Melon) Converters.bytesToObject(
melonSer, new MelonFilter());
Of course, the bytesToObject() accepts an argument of type ObjectInputFilter and sets this filter accordingly (ois is the specific ObjectInputStream):
ois.setObjectInputFilter(filter);
In this example, the MelonFilter will reject deserialization and the output will be as follows:
Exception in thread “main” java.io.InvalidClassException: filter status: REJECTED
…
You can practice both approaches (stream-global and stream-specific) in the bundled code.