Spring Data Mongo自定义类显式加密读取失败问题
Spring Boot MongoDB 自定义类字段显式加密解密异常修复
问题原因
MongoEncryptionConverter 默认仅处理基础Bson类型(如字符串、整数对应的BsonString、BsonInt32)。当加密自定义类或其列表时,写入前对象会被序列化为Document,但解密逻辑错误地尝试将Document直接转换为BsonValue,触发类型转换异常。
修复方案
1. 实现自定义加密转换器
扩展MongoEncryptionConverter,先将复杂类型序列化为BsonDocument再加密,解密后再反序列化为目标类型:
class CustomMongoEncryptionConverter( encryption: MongoClientEncryption, keyResolver: EncryptionKeyResolver, private val mappingConverter: MappingMongoConverter ) : MongoEncryptionConverter(encryption, keyResolver) { override fun read(source: Any, type: TypeDescriptor): Any? { if (source is BsonBinary && source.type == BsonBinaryType.ENCRYPTED) { val decrypted = super.read(source, type) // 解密后若为BsonDocument,反序列化为目标类型 return if (decrypted is BsonDocument && !isSimpleType(type.type)) { mappingConverter.read(type.type as Class<*>, decrypted) } else { decrypted } } return super.read(source, type) } override fun write(source: Any?, type: TypeDescriptor): Any? { val bsonValue = source?.takeIf { !isSimpleType(it.javaClass) } ?.let { mappingConverter.write(it) } ?: super.write(source, type) return super.write(bsonValue, type) } private fun isSimpleType(clazz: Class<*>): Boolean { return clazz.isPrimitive || clazz.isAssignableFrom(String::class.java) || Number::class.java.isAssignableFrom(clazz) } }
2. 更新Mongo配置,使用自定义转换器
在MongoConfig中注入MappingMongoConverter,替换默认的加密转换器:
@Configuration class MongoConfig( private val clientEncryption: ClientEncryption, private val masterKeyProvider: MasterKeyProvider, private val appContext: ApplicationContext, private val mappingMongoConverter: MappingMongoConverter // 新增注入 ) : AbstractMongoClientConfiguration() { // 原有静态常量、mongoClient方法保持不变 companion object { val vaultCollectionName = "__keyVault" val dbName = "dbName" val keyVaultNamespace = "$dbName.$vaultCollectionName" val connectionString = ConnectionString("dummy-connection-string") } override fun mongoClient(): MongoClient { val autoEncryptionSettings = AutoEncryptionSettings.builder() .keyVaultNamespace(keyVaultNamespace) .kmsProviders(masterKeyProvider.kmsProviders()) .bypassAutoEncryption(true) .build() return MongoClients.create( MongoClientSettings.builder() .applyConnectionString(connectionString) .autoEncryptionSettings(autoEncryptionSettings) .build() ) } @Bean fun encryptingConverter(): MongoEncryptionConverter { val encryption = MongoClientEncryption.just(clientEncryption) val keyResolver = EncryptionKeyResolver.annotated { _ -> EncryptionKey.keyAltName("demo-data-key") } return CustomMongoEncryptionConverter(encryption, keyResolver, mappingMongoConverter) } override fun configureConverters(converterConfigurationAdapter: MongoCustomConversions.MongoConverterConfigurationAdapter) { converterConfigurationAdapter.registerPropertyValueConverterFactory(PropertyValueConverterFactory.beanFactoryAware(appContext)) } override fun getDatabaseName(): String { return "databaseName" } }
3. 确保自定义类的映射配置
自定义类需被Spring Data MongoDB正确识别,嵌套类无需@Document,但需保证字段可被映射:
// 嵌套自定义类 data class UserProfile( val fullName: String, val phone: String ) // 主实体 @Document(collection = "customers") data class Customer( @Id val id: String?, @ExplicitEncrypted val profile: UserProfile, // 加密自定义类 @ExplicitEncrypted val pastProfiles: List<UserProfile> // 加密自定义类列表 )
4. 版本兼容性确认
当前使用的Spring Boot 3.1.1(对应Spring Data MongoDB 4.1.1)与mongodb-crypt 1.8.0版本兼容,无需调整依赖版本。
验证步骤
- 插入包含自定义类字段的实体,通过MongoDB Compass确认数据为加密的
BinData(6, ...)格式 - 读取实体,检查是否无类型转换异常,且自定义类字段值正确
内容的提问来源于stack exchange,提问作者Christian
相关产品推荐
相关产品推荐

