从数据库反序列化对象抛出StreamCorruptedException的解决方法
问题
从数据库表OAUTH_CLIENTS的SERIALIZATION列读取序列化数据,使用ObjectInputStream反序列化时抛出StreamCorruptedException,错误信息为invalid stream header: EFBFBDEF。
相关代码
ClassicClientsService代码
@Component public class ClassicClientsService implements ClientDetailsService { private final JdbcTemplate dataTemplate; private final ObjectMapper objectMapper = new ObjectMapper(); public ClassicClientsService(DataSource dataSource) { this.dataTemplate = new JdbcTemplate(dataSource); } @Override public ClientDetails loadClientByClientId(String clientId) throws OAuth2Exception { try { ClientDetails details = dataTemplate.queryForObject("SELECT SERIALIZATION FROM OAUTH_CLIENTS WHERE CLIENTID = ?", new ClientDetailsMapper(), new Object[]{clientId}); return details; } catch (EmptyResultDataAccessException ers) { throw new EmptyResultDataAccessException("Client " + clientId + " was not found", 1); } } private class ClientDetailsMapper implements RowMapper<ClientDetails> { @SneakyThrows @Override public ClientDetails mapRow(ResultSet rs, int rowNum) { byte[] temp = rs.getBytes("SERIALIZATION"); return (ClientDetails) new ObjectInputStream(new ByteArrayInputStream(temp)).readObject(); } } public boolean addClient(ClientDetails details) throws DataAccessException { ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); try { ObjectOutputStream outObject = new ObjectOutputStream(outBytes); outObject.writeObject(details); outObject.flush(); outObject.close(); } catch (IOException e) { e.printStackTrace(); } byte[] szDetails = outBytes.toByteArray(); dataTemplate.update("INSERT INTO OAUTH_CLIENTS (CLIENTID, PROTOCOL, SERIALIZATION) VALUES(?,1,?)", new Object[]{details.getClientId(), szDetails}); return false; } }
调用代码
@Configuration @ComponentScan public class Application { public static void main(String[] args) { migrateClients(args[0]); } private static void migrateClients(String clientId) { if (clientId.isEmpty()) { throw new RuntimeException("client id should be set as program argument"); } ApplicationContext context = new AnnotationConfigApplicationContext(Application.class); ClassicClientsService clientDetailsService = context.getBean(ClassicClientsService.class); OauthClientsManager oauthClientsManager = context.getBean(OauthClientsManager.class); clientDetailsService.loadClientByClientId(clientId); } }
日志信息
INFO: Loaded JDBC driver: com.mysql.jdbc.Driver Exception in thread "main" java.io.StreamCorruptedException: invalid stream header: EFBFBDEF at java.base/java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:935) at java.base/java.io.ObjectInputStream.<init>(ObjectInputStream.java:374)
解决方案
问题根源
错误码EFBFBDEF对应UTF-8编码的BOM头,说明数据库中存储的序列化字节流被非法转换成字符串格式,导致原始字节结构被篡改。核心原因是SERIALIZATION列使用了字符串类型(如VARCHAR/TEXT)而非二进制类型,或者JDBC驱动在存储/读取时自动执行了字节与字符串的编码转换。
修复步骤
修正数据库列类型
- 将
OAUTH_CLIENTS表的SERIALIZATION列修改为二进制类型,比如MySQL的BLOB或LONGBLOB。二进制列会直接存储原始字节流,避免编码转换导致的篡改。
- 将
验证插入逻辑的正确性
- 当前
addClient方法中直接传入字节数组的写法是正确的,但需确保列类型为二进制时,JDBC模板不会自动转换编码。若列类型是字符串,驱动会将字节转成字符串存储,读取时转回字节就会带上BOM头或乱码。
- 当前
临时兼容方案(无法修改列类型时)
如果暂时无法修改数据库列类型,需在存储和读取时统一使用不丢失字节的编码(如ISO-8859-1):- 插入时转换为字符串:
byte[] szDetails = outBytes.toByteArray(); String serializedStr = new String(szDetails, StandardCharsets.ISO_8859_1); dataTemplate.update("INSERT INTO OAUTH_CLIENTS (CLIENTID, PROTOCOL, SERIALIZATION) VALUES(?,1,?)", new Object[]{details.getClientId(), serializedStr}); - 读取时转回字节:
@SneakyThrows @Override public ClientDetails mapRow(ResultSet rs, int rowNum) { String serializedStr = rs.getString("SERIALIZATION"); byte[] temp = serializedStr.getBytes(StandardCharsets.ISO_8859_1); return (ClientDetails) new ObjectInputStream(new ByteArrayInputStream(temp)).readObject(); }
- 插入时转换为字符串:
验证序列化流完整性
- 插入数据后,用数据库工具查看
SERIALIZATION列的十六进制值,确认开头是Java序列化的标准标识AC ED 00 05,而非EF BB BF EF这类BOM头。
- 插入数据后,用数据库工具查看
内容的提问来源于stack exchange,提问作者Feel free
相关产品推荐
相关产品推荐

