在配置Kafka生产者的时候遇到SSL证书无法识别的问题,看到网上说这是因为 Kafka 客户端在运行时通常期望从文件系统中加载 SSL 证书文件,而不是从类路径加载。
但是我们可以通过.getAbsolutePath()去获取文件在Spring Boot项目中的绝对路径在赋值给Kafka配置
public String getJKS() {
// 获取文件在类路径中的 URL
URL truststoreUrl = KafkaProducerConfig.class.getClassLoader().getResource("client.truststore.jks");
File file = null;
if (truststoreUrl != null) {
// 转换 URL 到路径
try {
InputStream inputStream = truststoreUrl.openStream();
// 生成目标文件
file = File.createTempFile("client.truststore", ".jks");
FileUtils.copyInputStreamToFile(inputStream, file);
} catch (Exception e) {
throw new RuntimeException(e);
}
String jksFile = file.getAbsolutePath();
// log.info("Truststore file path: " + truststoreFile.getAbsolutePath());
return jksFile;
} else {
// log.error("Truststore file not found in classpath.");
return null;
}
}
首先通过类加载器获取文件在项目中的URL,然后将这个路径赋给File类,最后就可以通过getAbsolutePath()
方法获取文件的绝对路径。绝对路径是文件在文件系统中的完整路径,包括该文件所在的文件夹路径和文件名。这个方法返回一个字符串,表示调用该方法的文件的绝对路径。
最后附上通过spring-kafka配置SSL认证并实现发送消息的代码
1.坐标依赖:
<!-- spring-kafka -->
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>2.9.13</version>
</dependency>
2.配置文件(yml)
spring:
application:
name: KafkaDemo
kafka:
#bootstrap-servers: 127.0.0.1
security:
protocol: SASL_SSL
properties:
sasl:
#SASL鉴权方式
mechanism: PLAIN
jaas:
config: org.apache.kafka.common.security.plain.PlainLoginModule required username="用户名" password="密码";
ssl:
truststore:
#这里如果不需要再项目中配置证书,可以直接引用本地证书
location: D:\KafkaDemo\src\main\resources\client.truststore.jks
#ssl truststore文件的密码,固定,请勿修改。配置此密码是为了访问Java生成的jks文件。
password: 密码
endpoint:
identification:
algorithm:
producer:
# 发生错误后,消息重发的次数。
retries: 0
# 键的序列化方式
key-serializer: org.apache.kafka.common.serialization.StringSerializer
# 值的序列化方式
value-serializer: org.apache.kafka.common.serialization.StringSerializer
# 发送确认参数
acks: all
buffer-memory: 33554432
3.编写生产者配置类
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.CommonClientConfigs;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.config.SaslConfigs;
import org.apache.kafka.common.config.SslConfigs;
import org.apache.kafka.common.serialization.StringSerializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.core.DefaultKafkaProducerFactory;
import org.springframework.kafka.core.KafkaTemplate;
import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
@Slf4j
@Configuration
public class KafkaProducerConfig {
@Value("${spring.kafka.bootstrap-servers}")
private String addressStr;
@Value("${spring.kafka.properties.ssl.truststore.password}")
private String password;
@Value("${spring.kafka.properties.sasl.mechanism}")
private String mechanism;
@Value("${spring.kafka.properties.sasl.jaas.config}")
private String config;
//注入kafkaTemplate 如果项目中没有其他的kafkaTemplate
//这样就可以覆盖掉默认的kafkaTemplate了
@Bean
KafkaTemplate<String,String> myKafkaTemplate(){
DefaultKafkaProducerFactory<String, String> producerFactory =
new DefaultKafkaProducerFactory<>(producerProperties(addressStr));
return new KafkaTemplate<>(producerFactory);
}
//kafka的配置
private Map<String,Object> producerProperties(String addressStr){
// kafka的相关参数 比如ip地址和分组这些参数
Map<String,Object> properties = new HashMap<>();
//生产者
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, addressStr);
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
//ssl加密和认证配置
properties.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "SASL_SSL");
properties.put(SslConfigs.SSL_TRUSTSTORE_TYPE_CONFIG,"JKS");
//设置为空字符串来禁用服务器主机名验证
properties.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG ,"");
properties.put(SslConfigs.SSL_KEY_PASSWORD_CONFIG, password);
//获取Resources配置文件中client.truststore.jks
properties.put(SslConfigs.SSL_TRUSTSTORE_LOCATION_CONFIG, getJKS());
properties.put(SslConfigs.SSL_TRUSTSTORE_PASSWORD_CONFIG, password);
//SASL鉴权方式
properties.put(SaslConfigs.SASL_MECHANISM,mechanism);
properties.put(SaslConfigs.SASL_JAAS_CONFIG,config);
return properties;
}
public String getJKS(){
// 获取文件在类路径中的 URL
URL truststoreUrl = KafkaProducerConfig.class.getClassLoader().getResource("client.truststore.jks");
File truststoreFile ;
if (truststoreUrl != null) {
// 转换 URL 到路径
try {
truststoreFile = new File(truststoreUrl.toURI());
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
String jksFile = truststoreFile.getAbsolutePath();
log.info("Truststore file path:"+ truststoreFile.getAbsolutePath());
return jksFile;
} else {
log.info("Truststore file not found in classpath.");
return null;
}
}
}
4.这样就将所有配置完成了,接下来编写一个发送消息方法
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class KafkaProducer {
private final KafkaTemplate<String,String> kafkaTemplate;
@Autowired
public KafkaProducer(KafkaTemplate<String, String> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
/**
* kafka消息发送
* @param topic 目标主题
* @param message 消息
*/
public void sendMessage(String topic, String message) {
kafkaTemplate.send(topic, message);
log.info("生产者发送完成!");
}
}
在项目中调用sendMessage()方法,传入要发送的topic和消息体就可以发送了!