跳到主要内容

连接器安全最佳实践

本指南介绍 TDengine 连接器的安全最佳实践,包括 Token 认证、SSL/TLS 客户端配置、动态 Token 轮换以及各语言连接器的实现示例。


1. 安全架构设计

1.1 为什么需要安全架构

安全威胁

在不可信的网络环境中,TDengine 连接面临以下安全威胁:

  • 数据泄露:敏感时序数据在传输过程中被窃取
  • 中间人攻击:攻击者冒充服务端或客户端,窃取或篡改数据
  • 凭证泄露:用户名/密码硬编码在代码中,泄露后无法快速撤销
  • 重放攻击:捕获并重放合法请求,执行未授权操作

设计目标

TDengine 连接器安全架构的设计目标:

  • 机密性:所有数据传输加密,防止窃听
  • 完整性:防止数据在传输中被篡改
  • 可用性:Token 轮换确保业务连续性,避免服务中断
  • 可审计性:Token 独立追踪,支持访问审计
  • 最小权限:不同应用使用不同 Token,权限隔离

1.2 三层组件架构

TDengine 连接器安全架构由以下三层组成:

  1. 应用层:业务应用管理连接池和 Token 生命周期,包括业务应用(执行 SQL、参数绑定、无模式写入、数据订阅)、连接池(HikariCP 等管理物理连接)、Token 轮换器(监听配置中心变更,更新 Token)。

  2. 配置中心:集中存储和分发 Token,支持 Nacos、Vault、K8s Secret,动态推送 Token 更新,多实例共享配置。

  3. 服务端:TDengine 企业版,验证 Token 有效性,处理加密请求,可选 Nginx/HAProxy 负载均衡。

1.3 整体架构图

1.4 核心设计原则

1. WebSocket + Token + SSL

TDengine 连接器安全架构基于:

  • WebSocket 连接:跨平台支持;高性能、稳定;支持 SSL/TLS 加密
  • Token 认证:替代用户名/密码;有 TTL(24 小时),过期自动失效;可主动撤销,安全性高;支持动态轮换,避免服务中断
  • SSL/TLS 加密:客户端验证服务端证书,防止中间人攻击;所有数据加密传输

2. 连接器直连优于 Nginx 转发

维度方案 A:Nginx 转发方案 B:连接器直连(推荐)
负载均衡Nginx/HAProxy连接器内置算法
SSL 处理Nginx SSL 卸载客户端直连 SSL
故障转移5-10 秒检测自动重连,业务无感知
性能增加 1 跳延迟极致性能
证书管理集中管理多节点配置
TMQ 稳定性存在不稳定更稳定
运维复杂度需要额外组件配置简单

选择建议

  • 优先使用方案 B(连接器直连)
  • 仅在以下场景选择方案 A(Nginx 转发)
    • 需要与现有基础设施集成
    • 需要集中管理证书
    • 需要统一的入口管控

3. Token 动态轮换确保业务连续性

Token 有 TTL(通常 24 小时),过期后连接将失败。动态 Token 轮换机制确保:

  • Token 过期前自动更新(80% TTL 触发)
  • 业务无感知切换
  • 失败自动回滚

2. SSL/TLS 配置

启用 SSL/TLS 可确保数据在传输过程中的机密性和完整性。

配置分工
  • 服务端配置:证书生成、taosAdapter SSL 配置(taosadapter.toml),请参考 SSL 配置指南
  • 客户端配置:TrustStore 配置、SSL 连接参数,在本章说明

2.1 证书验证原理

客户端在建立 SSL/TLS 连接时,会验证服务端证书:

  1. 证书链验证:服务端证书是否由可信 CA 签发
  2. 主机名验证:证书中的 CN 或 SAN 是否与连接的主机名/IP 匹配
  3. 有效期验证:证书是否在有效期内
  4. 吊销状态验证:证书是否已被吊销(可选)

2.2 客户端 SSL/TLS 配置

Java 使用 truststore 存储可信证书。建议优先使用独立 truststore,避免污染系统默认证书库。

导入服务端证书:

# 方式一:导入到 JVM 系统证书库(需先正确设置 JAVA_HOME)
sudo keytool -import -alias tdengine-server \
-file /etc/taos/server.crt \
-keystore $JAVA_HOME/lib/security/cacerts \
-storepass changeit -noprompt

# 方式二(推荐):创建独立 truststore
keytool -import -alias tdengine-server \
-file server.crt \
-keystore ./tdengine-truststore.jks \
-storepass tdengine -noprompt

# 查看导入结果
keytool -list -keystore ./tdengine-truststore.jks -storepass tdengine

运行 Java 应用时指定 truststore:

java -Djavax.net.ssl.trustStore=/path/to/tdengine-truststore.jks \
-Djavax.net.ssl.trustStorePassword=tdengine \
-jar your-app.jar

验证 SSL 连接:

# 使用 openssl 验证服务端 SSL 配置
openssl s_client \
-connect td1.internal.taosdata.com:6041 \
-servername td1.internal.taosdata.com \
-CAfile ./server.crt \
-verify_hostname td1.internal.taosdata.com \
-verify_return_error < /dev/null

# 预期输出正常无错误
Verify return code: 0 (ok)

3. Token 认证

3.1 为什么使用 Token

Token 认证是 TDengine Enterprise 提供的一种轻量级身份验证机制,相比传统的用户名/密码方式具有以下优势:

  • 限时有效性:Token 有 TTL(生存时间),过期自动失效
  • 可撤销性:可以主动撤销 Token,立即停止访问权限
  • 最小权限:可为不同应用创建不同权限的 Token
  • 审计友好:每个 Token 可独立追踪使用情况

3.2 创建 Token

-- 创建 Token,有效期 1 天
CREATE TOKEN my_app_token FROM USER root ENABLE 1 PROVIDER 'root' TTL 1;

-- 查询当前用户创建的 Token
SHOW TOKENS;

3.3 使用 Token 连接

Token 认证是推荐的方式,相比用户名/密码认证具有更高的安全性。

使用 Token 认证时,只需要设置 Token 参数,无需设置用户名和密码


4. 动态 Token 轮换

4.1 轮换场景概述

Token 有有效期限制(通常 24 小时),过期后连接将失败。动态 Token 轮换确保业务连续性。

两种轮换场景:

  1. 普通连接(执行 SQL、参数绑定、无模式写入)

    • 依赖连接池(如 HikariCP)的 maxLifetime 机制
    • 新连接使用新 Token,旧连接自然淘汰
    • 业务无感知
  2. 数据订阅(TMQ Consumer)

    • 需要主动重建 Consumer
    • 轮换前必须 commit offset
    • 优雅切换避免消息丢失

4.2 普通连接 Token 轮换

轮换原理

连接池管理的物理连接有生命周期(maxLifetime),Token 轮换利用这一机制:配置中心 Token 更新后监听器收到通知 → 更新连接池配置中的 Token → 新建连接使用新 Token → 旧连接在 maxLifetime 到期后自然淘汰 → 业务无感知切换。

轮换策略

提前轮换:在 Token 过期前 20% 时间发起轮换。例如 Token TTL = 24 小时时,在第 19-20 小时触发轮换(80% TTL),新 Token 生效后旧 Token 继续有效 5 分钟(宽限期)。

灰度验证:新 Token 生成后先在少量连接上验证,确认新 Token 有效后再全面切换,失败时自动回滚到旧 Token。

回滚机制:轮换失败时记录错误日志、发送告警通知、自动回滚到旧 Token、延迟 1 小时后重试。

配置中心集成:Nacos

Nacos 服务端部署:

# 下载并启动 Nacos,示例使用 standalone 方式,正式环境请使用集群
cd /tmp
wget https://github.com/alibaba/nacos/releases/download/2.3.2/nacos-server-2.3.2.zip
unzip nacos-server-2.3.2.zip
cd nacos/bin
./startup.sh -m standalone

# 验证启动成功
curl http://localhost:8848/nacos/v1/ns/instance/list?serviceName=nacos

写入 Token 到 Nacos:

# 创建配置
curl -X POST "http://localhost:8848/nacos/v1/cs/configs" \
-d "dataId=tdengine-credential" \
-d "group=DEFAULT_GROUP" \
-d "content=token=your_new_token_here"

从 Nacos 读取 Token:

import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;

// 创建 Nacos 配置服务
ConfigService nacos = NacosFactory.createConfigService("localhost:8848");

// 读取 Token
String config = nacos.getConfig("tdengine-credential", "DEFAULT_GROUP", 5000);
if (config == null || config.isEmpty()) {
throw new IllegalStateException("Nacos 配置为空");
}
String token = "";
for (String line : config.split("\n")) {
line = line.trim();
if (line.startsWith("token=")) {
token = line.substring("token=".length()).trim();
break;
}
}
if (token.isEmpty()) {
throw new IllegalStateException("Nacos 配置中未找到 token=");
}

添加监听器,Token 变化时自动轮换连接池:

// Register Nacos listener with graceful rotation
nacos.addListener(DATA_ID, GROUP, poolListener);

查看源码

4.3 数据订阅 Token 轮换

轮换原理

TMQ Consumer 轮换建议按清晰生命周期执行:创建 Consumer → 订阅 → 消费。若订阅失败,直接结束并报错。

进入消费后,在固定间隔(每 10 秒或每条消息后)检查当前时间是否达到 refreshAt80% TTL + 最多 10% TTL 的随机抖动);达到后触发轮换。轮换核心顺序为:先创建并订阅新 Consumer → 对旧 Consumer 做 best-effort commit offset → unsubscribe 并 close 旧 Consumer → 切换到新 Consumer。

轮换流程

TMQ Token 轮换状态机:

实现要点

本状态机中的轮换触发条件:周期检查 now >= refreshAt80% TTL + jitter),或出现认证失败(0x80000357)。

初始订阅失败处理:若初始 subscribe 失败,不进入消费循环,直接结束。

Offset 提交时机:在新 Consumer 就绪后对旧 Consumer 做 best-effort commit,避免认证过期导致“先提交再切换”死锁。

优雅切换:获取新 Token → 创建并订阅新 Consumer → 对旧 Consumer best-effort commit offset → unsubscribe + close 旧 Consumer → 切换引用。

按触发来源区分失败处理:主动轮换失败时保留旧 Consumer 并延后重试;鉴权失败恢复轮换若仍失败则退出。

4.4 实现可靠性与资源回收要求

动态轮换代码的稳定性主要取决于资源生命周期管理,建议把以下规则作为实现基线:

  1. Listener 执行器复用getExecutor() 返回共享线程池,不能在每次回调时新建线程池。
  2. Listener 必须解绑:应用退出或示例步骤结束时调用 removeListener,避免回调对象泄漏。
  3. 连接池恢复双向清理:恢复成功后关闭旧池;恢复失败时关闭新建但未切换的池。
  4. TMQ skip 分支也要 closebuildConsumer 成功但 subscribe 失败(如 topic 不存在)时,必须立即关闭 consumer。
  5. 统一退出清理:scheduler、listener executor、pool、consumer 统一在 finally 中幂等关闭并打印日志。

5. 连接器实现

本节提供各语言连接器的完整实现示例。

5.1 基础连接(Token + SSL)

/** Current Token - updated by config center; volatile ensures visibility across threads */
private static volatile String currentToken =
System.getenv().getOrDefault("TDENGINE_TOKEN", "");

/**
* Scenario 1: basic secure connection.
* - Token authentication (replaces username/password)
* - useSSL=true enables transport encryption
* - Root CA in system trust store, no cert path needed in code
*/
public static void basicConnect() throws Exception {
String url = SecurityUtils.buildJdbcUrl(HOST, PORT, DB, currentToken);

try (Connection conn = DriverManager.getConnection(url);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT SERVER_VERSION()")) {
if (rs.next()) {
System.out.println("Connected. Server version: " + rs.getString(1));
}
}
}

查看源码

关键参数:

  • bearerToken:Bearer Token(WebSocket 连接专用)
  • useSSL:启用 SSL/TLS

5.2 普通连接 Token 轮换

连接池配置maxLifetime 设为 Token TTL 的 1/2,连接到期自然淘汰,新连接使用最新 Token):

/**
* Scenario 2a: create a HikariCP connection pool.
*
* Key parameter: maxLifetime = Token TTL / 2 (recommended range: 1/4 ~ 1/2).
* Connections retire naturally at maxLifetime; new ones use the latest Token.
*/
public static HikariDataSource createPool(long tokenTtlMs) {
return createPool(tokenTtlMs, currentToken);
}

private static HikariDataSource createPool(long tokenTtlMs, String token) {
HikariConfig cfg = new HikariConfig();
cfg.setJdbcUrl(SecurityUtils.buildJdbcUrl(HOST, PORT, DB, token));
cfg.setMaximumPoolSize(10);
cfg.setMinimumIdle(2);
cfg.setMaxLifetime(tokenTtlMs / 2); // half of Token TTL; connections won't outlive the token
cfg.setKeepaliveTime(60_000); // keepalive probe every 60 s
cfg.setConnectionTestQuery("SELECT SERVER_STATUS()");
return new HikariDataSource(cfg);
}

查看源码

Token 轮换(Nacos 监听器收到新 Token 时调用,新建池后关闭旧池):

/**
* Scenario 2b: Token rotation - called by the config-center callback.
*
* Strategy:
* 1. Create a new pool with the new Token immediately (new requests go to new pool).
* 2. Close the old pool - waits for in-flight connections then shuts down gracefully.
* The two-pool overlap window is milliseconds; transparent to the application.
*/
public static HikariDataSource rotatePool(
HikariDataSource oldPool, String newToken, long tokenTtlMs) {
if (newToken == null || newToken.trim().isEmpty()) {
throw new IllegalArgumentException("newToken must not be null or empty");
}
HikariDataSource newPool = createPool(tokenTtlMs, newToken);
currentToken = newToken; // update the global Token after the new pool is ready
if (oldPool != null) {
oldPool.close(); // drain active connections, then close
}
return newPool;
}

查看源码

Nacos 监听器集成

// Register Nacos listener with graceful rotation
nacos.addListener(DATA_ID, GROUP, poolListener);

查看源码

5.3 数据订阅 Token 轮换

Consumer 构建(WebSocket + SSL + Token 参数配置):

/** Current Token - updated by config center */
private static volatile String currentToken =
System.getenv().getOrDefault("TDENGINE_TOKEN", "");
private static volatile ConfigService nacosConfigService;

private static final TmqRotationManager<Map<String, Object>> TMQ_ROTATION_MANAGER =
new TmqRotationManager<Map<String, Object>>(SecurityTmqDemo::buildConsumerWithToken, TOPICS, logger);

/**
* Build a TMQ Consumer with the current Token and SSL (wss scheme).
* Using the same groupId after rotation resumes from the last committed offset.
*/
private static TaosConsumer<Map<String, Object>> buildConsumer() throws SQLException {
return buildConsumerWithToken(currentToken);
}

/**
* Build a TMQ Consumer with a specific Token and SSL (wss scheme).
*/
private static TaosConsumer<Map<String, Object>> buildConsumerWithToken(String token) throws SQLException {
Properties p = new Properties();

p.setProperty("bootstrap.servers", HOST + ":" + PORT);
p.setProperty("td.connect.type", "ws"); // use WebSocket consumer
p.setProperty("useSSL", "true"); // enable SSL for TMQ
p.setProperty("td.connect.token", token); // TMQ token for subscription auth
p.setProperty("auto.offset.reset", "earliest"); // consume from earliest offset for easy testing
p.setProperty("group.id", GROUP);
p.setProperty("enable.auto.commit", "false"); // manual commitSync
p.setProperty("msg.with.table.name", "true");
return new TaosConsumer<Map<String, Object>>(p);
}

查看源码

消费循环(含主动轮换 + 认证失败回退)

/**
* Consume loop with proactive rotation (80% TTL + jitter).
*
* Actual rotation order (implemented by {@link TmqRotationManager#tryRotate}):
* 1. fetchNewToken() - retrieve a fresh token from the config center
* 2. build + subscribe - create and verify a new consumer first
* 3. commitSync() - best-effort commit on old consumer
* 4. unsubscribe + close - release old consumer resources
* 5. switch reference - caller adopts the new consumer
*
* Note:
* - If step 2 fails, keep using the old consumer.
* - If step 3 fails, rotation still continues to avoid auth-expired deadlock.
*/
public static void consumeWithRotation(long tokenTtlMs) throws Exception {
currentToken = fetchNewToken();
if (currentToken == null || currentToken.isEmpty()) {
throw new IllegalStateException(
"No token found from Nacos config (dataId="
+ SecurityUtils.DATA_ID + ", group=" + SecurityUtils.GROUP + ")");
}

long createdAt = System.currentTimeMillis();
long refreshAt = nextRefreshAt(createdAt, tokenTtlMs);

TaosConsumer<Map<String, Object>> consumer =
TMQ_ROTATION_MANAGER.createAndSubscribe(currentToken, "[TMQ] initial");

try {
while (!Thread.currentThread().isInterrupted()) {
try {
ConsumerRecords<Map<String, Object>> records =
consumer.poll(Duration.ofMillis(500));
for (ConsumerRecord<Map<String, Object>> record : records) {
// process business logic here
Map<String, Object> map = record.value();
// processRecord(map);
}
if (!records.isEmpty()) {
consumer.commitSync(); // manual offset commit
}

// Proactive rotation: triggered at 80% of token lifetime (+ jitter)
if (System.currentTimeMillis() >= refreshAt) {
TmqRotationManager.RotationResult<Map<String, Object>> rotation =
rotateConsumer(consumer, "[TMQ] proactive-rotation");
if (!rotation.isSwitched()) {
logger.warn("[TMQ] proactive rotation failed: {}", rotation.getFailureReason());
refreshAt += SecurityUtils.ROTATION_RETRY_DELAY_MS;
continue;
}

consumer = rotation.getConsumer();
createdAt = System.currentTimeMillis();
refreshAt = nextRefreshAt(createdAt, tokenTtlMs);
logger.info("[TMQ] proactive rotation succeeded");
}

} catch (SQLException e) {
if (SecurityUtils.isAuthFailure(e)) {
logger.warn("[TMQ] authentication failure detected, attempting token rotation recovery: {}",
e.getMessage());
TmqRotationManager.RotationResult<Map<String, Object>> rotation =
rotateConsumer(consumer, "[TMQ] auth-failure-rotation");
if (!rotation.isSwitched()) {
logger.error("[TMQ] auth-failure rotation recovery failed: {}",
rotation.getFailureReason());
throw e;
}
consumer = rotation.getConsumer();
createdAt = System.currentTimeMillis();
refreshAt = nextRefreshAt(createdAt, tokenTtlMs);
logger.info("[TMQ] auth-failure rotation recovery succeeded");
continue;
}
if (SecurityUtils.isSecurityConnectError(e)) {
logger.error("[TMQ] security connection error (token/SSL). "
+ "Check token validity and TLS trust chain: {}", e.getMessage());
}
throw e;
}
}
} finally {
try {
consumer.commitSync();
} catch (SQLException e) {
logger.warn("[TMQ] final commit failed: {}", e.getMessage());
}
TMQ_ROTATION_MANAGER.closeQuietly(consumer, true, "[TMQ] final cleanup");
}
}

查看源码


6. 进阶主题

6.1 性能优化

SSL/TLS 优化

证书选择
证书类型密钥长度性能安全性推荐场景
RSA2048 位通用场景
RSA4096 位极高高安全要求
ECC256 位推荐(性能最优)
ECC384 位极高兼容性优先

建议:优先使用 ECC 证书(如 ECDSA P-256),相比 RSA 4096:

  • 握手时间减少 50-70%
  • CPU 占用降低 60%
  • 证书体积更小(传输更快)
TLS Session Resumption

启用会话恢复可减少完整握手次数:

# TDengine 服务端配置(如支持)
sslSessionCache internal
sslSessionCacheSize 10000
sslSessionTimeout 3600

效果:

  • 首次握手:~100ms
  • 会话恢复:~10ms(减少 90%
密码套件优化

禁用弱密码套件,优先使用高性能算法:

# 推荐优先级
1. TLS_AES_128_GCM_SHA256 (TLS 1.3,最快)
2. TLS_CHACHA20_POLY1305_SHA256 (ARM 优化)
3. TLS_AES_256_GCM_SHA384 (兼容性好)

连接池优化

HikariCP 配置建议
参数推荐值说明
maximumPoolSize10-20根据 CPU 核心数和并发量调整
minimumIdle2-5保持最小空闲连接,避免冷启动
maxLifetimeToken TTL / 2关键参数:确保连接不会存活超过 Token
connectionTimeout3000030 秒获取连接超时
keepaliveTime600001 分钟保活,检测死连接
connectionTestQuery不设置不设置时,会用 JDBC 的 isValid 来检测连接有效性

6.2 证书管理最佳实践

1. 通配符证书

推荐使用通配符证书(*.yourdomain.com):优点是一张证书覆盖所有子域名、降低证书管理复杂度、减少证书更新频率;限制是仅支持同级子域名(*.a.example.com 不支持 b.a.example.com)、私钥泄漏影响范围更大。

2. 证书自动化分发

# 1. 使用 CFSSL 或 cert-manager 自动生成证书
cfssl gencert -ca ca.crt -ca-key ca.key \
-config ca-config.json \
-hostname="td1.example.com,td2.example.com" \
-profile=server server-csr.json | cfssljson -bare server

# 2. 自动部署到 TDengine 服务器
ansible-playbook deploy-ssl.yml --extra-vars "host=td1.example.com"

# 3. 自动重启服务
systemctl restart taosd

3. 证书监控

# 定期检查证书过期时间
for host in td1 td2 td3; do
expiry=$(echo | openssl s_client -connect $host.example.com:6041 2>/dev/null \
| openssl x509 -noout -enddate | cut -d= -f2)
echo "$host: $expiry"
done

# 集成到监控系统(Prometheus/Zabbix)
# 提前 30 天告警

7. 安全检查清单

在生产环境部署前,请确认以下安全措施:

传输加密

  • 启用 SSL/TLS:所有连接(JDBC、REST API、TMQ)使用 useSSL=true
  • 证书验证:不能跳过证书验证。
  • 强密码套件:TLS 版本 ≥ 1.2,禁用弱密码套件(如 RC4、DES)

Token 管理

  • 最小权限:Token 仅授予必要的数据库权限
  • 合理 TTL:Token 有效期不超过 7 天,推荐 24 小时
  • 静态加密:Token 不明文存储在代码或配置文件中
  • 密钥管理:使用 KMS、Vault 等密钥管理服务存储 Token
  • 定期轮换:配置动态轮换机制,Token 过期前自动更新

运维安全

  • 审计日志:记录 Token 创建、使用、撤销操作
  • 监控告警:Token 过期、轮换失败、连接异常配置告警
  • 应急响应:Token 泄漏时能够快速撤销
  • 最小暴露:不同应用使用不同的 Token

代码安全

  • 日志脱敏:日志输出中隐藏 Token 敏感信息
  • 连接池隔离:不同租户或业务使用独立连接池
  • 异常处理:Token 失效时优雅降级,不泄漏错误信息

8. 故障排查

8.1 常见错误速查表

错误信息原因解决方案
SSLHandshakeException: PKIX path building failed证书未被信任导入 CA 证书到 truststore:
keytool -import -alias tdengine -file ca.crt -keystore truststore.jks
SSLHandshakeException: hostname in certificate didn't match证书 CN/SAN 与连接地址不匹配1. 重新生成证书,CN/SAN 设为连接 IP/域名
2. 或使用与证书匹配的地址连接
SSLException: Connection reset by peertaosAdapter 未启用 SSL 或配置错误1. 检查 taosadapter.toml[ssl] enable = true
2. 查看日志:journalctl -u taosadapter -n 100
SQLException: Token expiredToken 已过期1. 创建新 Token:
CREATE TOKEN new_token FROM USER root TTL 1
2. 配置自动轮换(见上文)
auth failure: Invalid password length使用了错误的 Token 参数名WebSocket 连接应使用 bearerToken,不是 token

8.2 调试命令

验证 SSL 连接openssl s_client -connect td1.internal.taosdata.com:6041 -showcerts 测试连接,应该看到 Verify return code: 0 (ok)、TLSv1.3 连接成功、证书 CN/SAN 与主机名匹配。

测试 Token 连接curl -v "http://td1.internal.taosdata.com:6041/rest/sql/%22SELECT%20SERVER_VERSION()%22" -H "Authorization: Bearer your_token_here" 测试 REST API Token 认证。

检查 Nacos 配置curl -X GET "http://localhost:8848/nacos/v1/cs/configs?dataId=tdengine-credential&group=DEFAULT_GROUP" 查看 Token 配置,预期返回 token=your_actual_token。


9. 常见问题

1. Token 连接报错 "auth failure: Invalid password length"

原因:使用了错误的参数名。WebSocket 连接应使用 bearerToken,不是 token

解决

// 错误
String url = "jdbc:TAOS-WS://host:6041/db?token=xxx";

// 正确
String url = "jdbc:TAOS-WS://host:6041/db?bearerToken=xxx";

2. SSL 连接报错 "certificate verify failed"

原因:客户端无法验证服务端证书。排查步骤:使用 openssl s_client -connect host:port -showcerts 验证服务端 SSL 配置,检查证书 CN/SAN 是否与连接主机名匹配,确认 CA 证书已导入客户端 truststore。

3. Nacos 监听器不生效

原因:ConfigService 未正确初始化或监听器添加失败。排查:确认 Nacos 服务启动(curl http://localhost:8848/nacos/v1/ns/instance/list),检查 dataId 和 group 是否与服务端一致,确认网络连接和防火墙配置。


10. 参考文档