【问题标题】:Unable to deserialize ExecutionContext: the class with [... Class] and name of [... Class] is not trusted无法反序列化 ExecutionContext:具有 [... Class] 且名称为 [... Class] 的类不受信任
【发布时间】:2021-06-25 15:16:55
【问题描述】:

我正在使用 SpringBoot 2.4.2 和 SpringBatch。反序列化失败,堆栈跟踪如下:

2021-03-29 15:51:28.529 ERROR 30308 --- [           main] o.s.boot.SpringApplication               : Application run failed
java.lang.IllegalArgumentException: Unable to deserialize the execution context
    at org.springframework.batch.core.repository.dao.JdbcExecutionContextDao$ExecutionContextRowMapper.mapRow(JdbcExecutionContextDao.java:328) ~[spring-batch-core-4.3.1.jar:4.3.1]
    at org.springframework.batch.core.repository.dao.JdbcExecutionContextDao$ExecutionContextRowMapper.mapRow(JdbcExecutionContextDao.java:312) ~[spring-batch-core-4.3.1.jar:4.3.1]
    at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:94) ~[spring-jdbc-5.3.3.jar:5.3.3]
    at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:61) ~[spring-jdbc-5.3.3.jar:5.3.3]
    at org.springframework.jdbc.core.JdbcTemplate$1.doInPreparedStatement(JdbcTemplate.java:723) ~[spring-jdbc-5.3.3.jar:5.3.3]
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:651) ~[spring-jdbc-5.3.3.jar:5.3.3]
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:713) ~[spring-jdbc-5.3.3.jar:5.3.3]
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:744) ~[spring-jdbc-5.3.3.jar:5.3.3]
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:757) ~[spring-jdbc-5.3.3.jar:5.3.3]
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:815) ~[spring-jdbc-5.3.3.jar:5.3.3]
    at org.springframework.batch.core.repository.dao.JdbcExecutionContextDao.getExecutionContext(JdbcExecutionContextDao.java:129) ~[spring-batch-core-4.3.1.jar:4.3.1]
    at org.springframework.batch.core.explore.support.SimpleJobExplorer.getStepExecutionDependencies(SimpleJobExplorer.java:238) ~[spring-batch-core-4.3.1.jar:4.3.1]
    at org.springframework.batch.core.explore.support.SimpleJobExplorer.findRunningJobExecutions(SimpleJobExplorer.java:118) ~[spring-batch-core-4.3.1.jar:4.3.1]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:128) ~[spring-batch-core-4.3.1.jar:4.3.1]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.3.jar:5.3.3]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.3.jar:5.3.3]
    at com.sun.proxy.$Proxy222.findRunningJobExecutions(Unknown Source) ~[na:na]
    at com.xyz.onApplicationEvent(HandleJobsOnAppReady.java:50) ~[classes/:na]
    at com.xyz.onApplicationEvent(HandleJobsOnAppReady.java:24) ~[classes/:na]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176) ~[spring-context-5.3.3.jar:5.3.3]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169) ~[spring-context-5.3.3.jar:5.3.3]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143) ~[spring-context-5.3.3.jar:5.3.3]
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:426) ~[spring-context-5.3.3.jar:5.3.3]
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:383) ~[spring-context-5.3.3.jar:5.3.3]
    at org.springframework.boot.context.event.EventPublishingRunListener.running(EventPublishingRunListener.java:111) ~[spring-boot-2.4.2.jar:2.4.2]
    at org.springframework.boot.SpringApplicationRunListeners.lambda$running$6(SpringApplicationRunListeners.java:79) ~[spring-boot-2.4.2.jar:2.4.2]
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540) ~[na:na]
    at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:117) ~[spring-boot-2.4.2.jar:2.4.2]
    at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:111) ~[spring-boot-2.4.2.jar:2.4.2]
    at org.springframework.boot.SpringApplicationRunListeners.running(SpringApplicationRunListeners.java:79) ~[spring-boot-2.4.2.jar:2.4.2]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:341) ~[spring-boot-2.4.2.jar:2.4.2]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1311) ~[spring-boot-2.4.2.jar:2.4.2]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1300) ~[spring-boot-2.4.2.jar:2.4.2]
    at com.xyz.Application.main(XYZApplication.java:18) ~[classes/:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run(AbstractRunMojo.java:578) ~[na:na]
    at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]
Caused by: com.fasterxml.jackson.databind.JsonMappingException: The class with [xyz.Class; and name of [xyz.Class; is not trusted. If you believe this class is safe to deserialize, you can add it to the base set of trusted classes at construction time or provide an explicit mapping using Jackson annotations or a custom ObjectMapper. If the serialization is only done by a trusted source, you can also enable default typing. (through reference chain: java.util.HashMap["partition"])
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:397) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:356) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase.wrapAndThrow(ContainerDeserializerBase.java:181) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.deser.std.MapDeserializer._readAndBindStringKeyMap(MapDeserializer.java:552) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:377) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:29) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedForId(AsPropertyTypeDeserializer.java:132) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:99) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserializeWithType(MapDeserializer.java:413) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:68) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4526) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3513) ~[jackson-databind-2.11.4.jar:2.11.4]
    at org.springframework.batch.core.repository.dao.Jackson2ExecutionContextStringSerializer.deserialize(Jackson2ExecutionContextStringSerializer.java:133) ~[spring-batch-core-4.3.1.jar:4.3.1]
    at org.springframework.batch.core.repository.dao.Jackson2ExecutionContextStringSerializer.deserialize(Jackson2ExecutionContextStringSerializer.java:104) ~[spring-batch-core-4.3.1.jar:4.3.1]
    at org.springframework.batch.core.repository.dao.JdbcExecutionContextDao$ExecutionContextRowMapper.mapRow(JdbcExecutionContextDao.java:325) ~[spring-batch-core-4.3.1.jar:4.3.1]
    ... 46 common frames omitted
Caused by: java.lang.IllegalArgumentException: The class with [xyz.Class; and name of [xyz.Class; is not trusted. If you believe this class is safe to deserialize, you can add it to the base set of trusted classes at construction time or provide an explicit mapping using Jackson annotations or a custom ObjectMapper. If the serialization is only done by a trusted source, you can also enable default typing.
    at org.springframework.batch.core.repository.dao.Jackson2ExecutionContextStringSerializer$TrustedTypeIdResolver.typeFromId(Jackson2ExecutionContextStringSerializer.java:349) ~[spring-batch-core-4.3.1.jar:4.3.1]
    at com.fasterxml.jackson.databind.jsontype.impl.TypeDeserializerBase._findDeserializer(TypeDeserializerBase.java:154) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:97) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromArray(AsArrayTypeDeserializer.java:53) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromAny(AsPropertyTypeDeserializer.java:193) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer$Vanilla.deserializeWithType(UntypedObjectDeserializer.java:710) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.deser.std.MapDeserializer._readAndBindStringKeyMap(MapDeserializer.java:542) ~[jackson-databind-2.11.4.jar:2.11.4]
    ... 57 common frames omitted

这个错误很清楚,我读了很多关于它的内容。但是我无法解决它。

我在 BatchConfiguration 中添加了一个 ExecutionContextSerializer 类型的 bean,但它似乎不起作用:

@Bean
public ExecutionContextSerializer customSerializer(){
    return new Jackson2ExecutionContextStringSerializer(ClassToBeTrusted.class.getName());
}


@Bean
public JobRepository createJobRepository(DataSource dataSource,ExecutionContextSerializer executionContextSerializer) throws Exception {
    JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
    factory.setDataSource(dataSource);
    factory.setTransactionManager(getTransactionManager());
    factory.setSerializer(executionContextSerializer);
    factory.afterPropertiesSet();
    return factory.getObject();
}

错误发生在:

@Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {

        Set<JobExecution> runningJobExecutions = jobExplorer.findRunningJobExecutions(job.getName());

被执行。

更新:BatchConfiguration 类现在实现了DefaultBatchConfigurer 类并覆盖了 getJobRepository 和 getJobExplorer(不确定这是否必要?)如下:

    @Override
    public JobRepository getJobRepository() {
        try {
            JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
            factory.setDataSource(datasource);
            factory.setTransactionManager(getTransactionManager());
            factory.setSerializer(customSerializer());
            factory.afterPropertiesSet();
            return factory.getObject();
        }

        catch(Exception e){
            throw new BatchConfigurationException(e);
        }
    }

    @Override
    public JobExplorer getJobExplorer(){
        try{
            JobExplorerFactoryBean factory = new JobExplorerFactoryBean();
            factory.setSerializer(customSerializer());
            factory.setDataSource(datasource);
            factory.afterPropertiesSet();
            return factory.getObject();
        }
        catch(Exception e ){
            throw new BatchConfigurationException(e);
        }
    }

不幸的是,我仍然看到反序列化错误:Myclass is not trust ... 如上所述。

【问题讨论】:

    标签: spring-boot spring-batch


    【解决方案1】:

    根据 Christian 的 回答,这是您覆盖默认序列化程序的方法。

    package com.my.path.to.config;
    
    import com.my.path.to.entity.MyEntity;
    import org.springframework.batch.core.configuration.annotation.BatchConfigurer;
    import org.springframework.batch.core.configuration.annotation.DefaultBatchConfigurer;
    import org.springframework.batch.core.explore.JobExplorer;
    import org.springframework.batch.core.explore.support.JobExplorerFactoryBean;
    import org.springframework.batch.core.launch.JobLauncher;
    import org.springframework.batch.core.repository.ExecutionContextSerializer;
    import org.springframework.batch.core.repository.JobRepository;
    import org.springframework.batch.core.repository.dao.Jackson2ExecutionContextStringSerializer;
    import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.transaction.PlatformTransactionManager;
    
    import javax.sql.DataSource;
    
    @Configuration
    public class BatchConfiguration {
    
        @Bean
        public ExecutionContextSerializer customSerializer() {
            return new Jackson2ExecutionContextStringSerializer(MyEntity.class.getName());
        }
    
        @Bean
        BatchConfigurer myBatchConfigurer(@Qualifier("batchDataSource") DataSource dataSource,
                                          ExecutionContextSerializer executionContextSerializer,
                                          PlatformTransactionManager transactionManager) {
            return new DefaultBatchConfigurer(dataSource) {
                @Override
                protected JobExplorer createJobExplorer() throws Exception {
                    JobExplorerFactoryBean jobExplorerFactoryBean = new JobExplorerFactoryBean();
                    jobExplorerFactoryBean.setDataSource(dataSource);
                    jobExplorerFactoryBean
                                    .setSerializer(executionContextSerializer);
                    jobExplorerFactoryBean.afterPropertiesSet();
                    return jobExplorerFactoryBean.getObject();
                }
    
                @Override
                protected JobRepository createJobRepository() throws Exception {
                    JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean();
                    jobRepositoryFactoryBean.setDataSource(dataSource);
                    jobRepositoryFactoryBean
                                    .setSerializer(executionContextSerializer);
                    jobRepositoryFactoryBean.setTransactionManager(transactionManager);
                    jobRepositoryFactoryBean.afterPropertiesSet();
                    return jobRepositoryFactoryBean.getObject();
                }
            };
        }
    }
    
    

    【讨论】:

      【解决方案2】:

      我可以通过调试Jackson2ExecutionContextStringSerializer 及其isTrusted 逻辑来解决问题。最后,我不得不将类型化数组和类添加到可信类列表中,如下所示:

      public ExecutionContextSerializer customSerializer() {
          return new Jackson2ExecutionContextStringSerializer(new MyClass[0].getClass().getName(), MyClass.class.getName());
      }
      

      当我看到[L&lt;fullyQualifiedClassName&gt;fullyQualifiedClassName 进行比较时,我意识到了这个问题

      【讨论】:

      • 如何覆盖 ExecutionContextSerializer ?创建一个 @Bean 是不够的。我试图覆盖 BatchConfiguration,但之后数据库连接出现问题。
      【解决方案3】:

      要注册自定义JobRepository,您需要提供Batchconfigurer 并覆盖getJobRepositoryConfiguring and Running a Job 部分对此进行了解释:

      You can customize any of these beans by creating a custom implementation
      of the BatchConfigurer interface. Typically, extending the DefaultBatchConfigurer 
      (which is provided if a BatchConfigurer is not found) and overriding
      the required getter is sufficient.
      

      在您的情况下,您声明了一个 JobRepository 类型的 bean 并期望 Spring Batch 将其考虑在内,这还不够。您需要使您的批处理配置类实现BatchConfigurer 或扩展DefaultBatchConfigurer

      【讨论】:

      • 感谢您的回答。我错过了文档的那部分。我用我的更改更新了我的答案。但是,我仍然收到反序列化错误。
      • 您的班级是否声明为@Component?您需要在应用程序上下文中有一个 BatchConfigurer 类型的 Spring bean。这可能会有所帮助:stackoverflow.com/questions/62718203/…
      • 类没有用@Component注释,而是@Configuration@ComponentScan。我添加了@Component,但并没有解决问题。我之前看到了链接的帖子。但是,似乎所描述的解决方案仅适用于 ExecutionContexts 已经使用旧版本持久化的 Spring Boot 更新场景,而在我的场景中并非如此。感谢任何提示提示,我有点卡住了。
      • 如果没有minimal example,我无法确定问题的根本原因。如果您在应用程序上下文中显式声明 BatchConfigurer bean 并覆盖您想要的方法作为代码示例:docs.spring.io/spring-batch/docs/4.3.x/reference/html/…,它应该可以按预期工作。
      • 我可以解决这个问题,这是因为只有类而不是类型化数组被添加到杰克逊序列化程序的受信任类列表中。感谢您的帮助!
      猜你喜欢
      • 2017-05-26
      • 2020-06-11
      • 2023-03-06
      • 2011-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-12
      • 1970-01-01
      相关资源
      最近更新 更多