You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从数据库反序列化对象抛出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驱动在存储/读取时自动执行了字节与字符串的编码转换。

修复步骤

  1. 修正数据库列类型

    • 将OAUTH_CLIENTS表的SERIALIZATION列修改为二进制类型,比如MySQL的BLOB或LONGBLOB。二进制列会直接存储原始字节流,避免编码转换导致的篡改。
  2. 验证插入逻辑的正确性

    • 当前addClient方法中直接传入字节数组的写法是正确的,但需确保列类型为二进制时,JDBC模板不会自动转换编码。若列类型是字符串,驱动会将字节转成字符串存储,读取时转回字节就会带上BOM头或乱码。
  3. 临时兼容方案(无法修改列类型时)
    如果暂时无法修改数据库列类型,需在存储和读取时统一使用不丢失字节的编码(如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();
      }
      
  4. 验证序列化流完整性

    • 插入数据后,用数据库工具查看SERIALIZATION列的十六进制值,确认开头是Java序列化的标准标识AC ED 00 05,而非EF BB BF EF这类BOM头。

内容的提问来源于stack exchange,提问作者Feel free

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 21:25:37