1. 引言
在本教程中,我們將回顧 Spring Kafka 信任包特性。我們將瞭解其背後的動機,以及它的使用方法。所有內容都將通過實際示例進行演示,正如往常一樣。
2. 先決條件
一般來説,Spring Kafka模塊允許我們(作為用户)指定發送的POJO的元數據,通常以Kafka消息頭的形式呈現。例如,如果我們以這種方式配置ProducerFactory:
@Bean
public ProducerFactory<Object, SomeData> producerFactory() {
JsonSerializer<SomeData> jsonSerializer = new JsonSerializer<>();
jsonSerializer.setAddTypeInfo(true);
return new DefaultKafkaProducerFactory<>(
producerFactoryConfig(),
new StringOrBytesSerializer(),
jsonSerializer
);
}
@Data
@AllArgsConstructor
static class SomeData {
private String id;
private String type;
private String status;
private Instant timestamp;
}然後我們將一個新的消息生成到一個主題中,例如,使用配置了上面定義的 KafkaTemplate 生產器工廠的 KafkaTemplate:
public void sendDataIntoKafka() {
SomeData someData = new SomeData("1", "active", "sent", Instant.now());
kafkaTemplate.send(new ProducerRecord<>("sourceTopic", null, someData));
}
然後,在這種情況下,Kafka消費者控制枱中會收到以下消息:
CreateTime:1701021806470 __TypeId__:com.baeldung.example.SomeData null {"id":"1","type":"active","status":"sent","timestamp":1701021806.153965150}正如我們所見,消息內部的POJO所包含的類型信息位於頭部中。這當然是 Spring Kafka 特性所認可的,僅由 Spring 識別。這意味着這些頭部僅僅是 Kafka 或其他框架的元數據,從他們的角度來看。因此,我們可以假設消費者和生產者都使用 Spring 來處理 Kafka 消息。
3. 信任包特性
鑑於以上所述,我們可以説,在某些情況下,此功能相當有用。當主題中的消息具有不同的 payload 模式時,向消費者提示 payload 類型將非常有益。
然而,一般來説,我們知道主題中消息的 schema 類型可能是什麼。因此,限制消費者接受的可能 payload schema 也是一個不錯的想法。這就是 Spring Kafka 信任包特性的核心所在。
4. 示例用法
Spring Kafka 特性配置在解序列化器級別。如果已配置了受信任的包,則 Spring 會在傳入消息的類型頭中進行查找。然後,它會檢查消息中提供的所有類型(包括鍵和值)是否都已信任。
這意味着鍵和值對應的 Java 類必須位於受信任的包內。如果一切正常,Spring 將消息傳遞到進一步的解序列化。如果未提供任何頭信息,則 Spring 將直接解序列化對象,而不會檢查受信任的包。
@Bean
public ConsumerFactory<String, SomeData> someDataConsumerFactory() {
JsonDeserializer<SomeData> payloadJsonDeserializer = new JsonDeserializer<>();
payloadJsonDeserializer.addTrustedPackages("com.baeldung.example");
return new DefaultKafkaConsumerFactory<>(
consumerConfigs(),
new StringDeserializer(),
payloadJsonDeserializer
);
}
此外,Spring 可以信任所有包,如果我們將具體的包替換為星號 (*):
JsonDeserializer<SomeData> payloadJsonDeserializer = new JsonDeserializer<>();
payloadJsonDeserializer.trustedPackages("*");
然而,在這些情況下,使用受信任的包並不會產生任何效果,只會增加額外的開銷。現在讓我們來探討我們剛剛看到的該功能的動機。
5.1. 第一驅動因素:一致性
該功能之所以出色,主要有兩個原因。首先,我們可以快速失敗,如果集羣中出現問題。例如,某個生產者可能會意外地將消息發佈到他本不應該發佈的主題中。這可能會導致很多問題,尤其是在我們成功地反序列化傳入的消息時。在這種情況下,整個系統行為可能是不確定的。
因此,如果生產者包含類型信息並知道消費者信任哪些類型,那麼就可以避免這種情況。當然,這假設生產者發出的消息類型與消費者期望的不同。但考慮到生產者根本不應該將消息發佈到該主題中,這種假設是合理的。
5.2. 第二個動機:安全
但最重要的是安全問題。在之前的例子中,我們強調了生產者無意中將消息發佈到主題中的情況。但這也可能是一種有意的攻擊。惡意生產者可能會故意將消息發佈到特定的主題中,以利用反序列化漏洞。因此,通過阻止反序列化不受歡迎的消息,Spring 提供額外的安全措施,以降低安全風險。
真正重要的是要理解的是,受信任的包特性並不能解決“頭信息偽造”攻擊。在這種情況下,攻擊者操縱消息頭,欺騙接收者相信消息是合法的,並且來自一個受信任的來源。因此,通過提供正確的類型頭信息,攻擊者可能會欺騙 Spring,後者將繼續進行消息反序列化。但這個問題相當複雜,不在討論範圍內。通常,Spring 僅提供額外的安全措施,以最大限度地降低黑客成功的風險。
6. 結論
在本文中,我們探討了 Spring Kafka 信任包特性。該特性為我們的分佈式消息系統提供了額外的一致性和安全性。然而,需要注意的是,信任包仍然可能受到 HTTP 頭部欺騙攻擊。儘管如此,Spring Kafka 在提供額外的安全措施方面做得非常出色。