华为云 MRS Redis 安全集群 Kerberos 认证接入实战(Spring Boot + Jedis)
摘要 :华为云 MRS(FusionInsight)大数据集群默认开启 Kerberos 强安全机制,集群内 Redis 不再支持传统密码认证,仅可通过 Kerberos+GSSAPI SASL 方式完成鉴权,满足等保三级合规要求。大量开发者对接时高频踩坑:管理员 keytab 与 Redis 客户端 keytab 混用、未加载集群 krb5 配置、遗漏 SASL 握手参数、标准 Jedis 包冲突等。本文基于 MRS 3.5.1-LTS 生产环境,从零完整落地 Spring Boot 接入 MRS 安全 Redis 集群,提供完整可编译 POM、认证核心代码、RedisTemplate 零侵入改造、配置文件、启动校验与高频报错解决方案,适配所有国产化 MRS 安全集群场景。
1. 背景与动机
1.1 开源 Redis 认证短板
原生开源 Redis 仅支持 requirepass 静态密码认证,认证逻辑简单、明文可被劫持、无身份细粒度管控,仅适用于测试、内网非安全场景,无法满足政企等保、信创项目安全合规要求。同时密码认证无票据时效、无主体隔离、无审计能力,完全不适合企业级大数据生产集群。
1.2 MRS 安全集群认证体系
华为云 MRS 安全模式集群,所有组件(Redis、Kafka、HDFS、Hive)统一接入 Kerberos 可信第三方认证体系 ,Redis 关闭传统密码登录,强制使用 GSSAPI SASL 双向握手认证,依托 KDC 完成身份确权、票据分发、权限校验,具备时效性、不可伪造、主体隔离、操作审计等安全能力,完全适配等保合规要求。
1.3 MRS 两套用户体系核心区别(重中之重)
MRS 集群存在两套完全隔离的 Kerberos 主体体系,密钥文件绝对不可混用,是 90% 接入失败的根源:
-
Manager 管理员用户 :用于登录 FusionInsight 管理页面、集群运维、参数配置,对应
user.keytab,无 Redis 读写权限,无法用于业务连接 -
Redis 专属客户端用户 redisCli :MRS Redis 内置业务访问主体,仅用于集群缓存读写、SASL 认证鉴权,对应 redisCli.keytab,是唯一合法的客户端凭证
硬核结论 :绝对不能使用 Manager 后台下载的 user.keytab 连接 Redis!必须使用 MRS 服务器 Redis 安装目录下原生的 redisCli.keytab,主体必须为 redisCli@<MRS_REALM>。
2. 生产环境信息(脱敏占位)
本次实战基于生产标准 MRS 集群环境,所有真实地址统一脱敏,可直接替换复用:
| 配置项 | 环境参数 |
|---|---|
| MRS 版本 | 3.5.1-LTS |
| Redis 版本 | 6.2.7(MRS 定制安全版) |
| 集群节点实例 | <NODE1_IP>:22400/22401、<NODE2_IP>:22400/22401、<NODE3_IP>:22400/22401 |
| KDC 认证地址 | <KDC_IP1>:21732、<KDC_IP2>:21732(双活冗余) |
| 集群 Realm | <MRS_REALM> |
| SSL 状态 | false(集群底层未开启 SSL 加密) |
| 合法认证主体 | redisCli(禁止使用 admin/自定义运维用户) |
节点说明 :MRS Redis 集群单节点默认双实例部署,分别监听22400、22401 端口,接入时必须写入全量节点端口,否则集群路由异常、读写失败。
3. Kerberos 完整认证原理速览
MRS Redis 不支持标准 Redis 认证协议,基于华为定制扩展 AUTHEXT 命令完成 Kerberos 鉴权,整体链路为「配置加载→票据申请→SASL握手→扩展认证」,完整时序如下:
应用启动完整认证流程图
-
Spring Boot 启动,开启 JAAS 认证开关,启用 Kerberos 安全上下文
-
JVM 加载本地
krb5.conf,解析集群 Realm、KDC 地址、协议端口、超时策略 -
读取本地
redisCli.keytab,解密获取redisCli主体加密密钥 -
客户端携带主体信息+密钥,向 KDC-AS 服务发起认证,申请 TGT 全局票据
-
KDC 校验身份合法,返回长效 TGT 票据(有效期默认 24h)
-
客户端携带 TGT 向 KDC-TGS 服务申请 Redis 专属 Service Ticket 服务票据
-
获取短时服务票据后,通过 GSSAPI SASL 协议与 Redis 节点完成双向握手
-
触发 MRS 扩展命令 AUTHEXT,Redis 校验票据合法性、主体权限
-
鉴权通过,建立长连接,完成集群路由初始化,可正常读写数据
核心知识点 :标准开源 Redis 无 AUTHEXT 扩展认证命令,这是 MRS 安全 Redis 专属鉴权逻辑,也是标准 Jedis 无法适配、必须使用华为定制 Jedis 的根本原因。
4. 项目依赖准备(完整可编译 POM)
MRS Redis Kerberos 接入严禁使用 Maven 公共仓库的标准 Jedis,会出现方法缺失、认证不触发、SASL 握手失败等问题。必须排除原生 Jedis、Lettuce,引入 MRS 集群配套定制依赖,同时补齐被排除的传递依赖。
4.1 核心依赖说明
-
Spring Boot 版本固定 2.7.x,兼容性最稳定,避免高版本自动配置冲突
-
排除
spring-boot-starter-data-redis自带的 Lettuce、标准 Jedis -
引入 MRS 定制三件套:定制 Jedis、Hadoop 安全内核、XML 解析依赖
-
手动补齐
commons-pool2、commons-lang3,解决依赖丢失报错 -
打包插件开启 ZIP 布局、允许 system 依赖打入 Jar
4.2 完整 pom.xml 依赖片段
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
<relativePath/>
</parent>
<dependencies>
<!-- Spring Boot Web 基础依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Redis 核心依赖(排除原生冲突包) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
<exclusion>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 手动补齐连接池依赖(排除Jedis后丢失) -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- MRS 定制 Jedis(Kerberos认证核心) -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.6.3-h0.cbu.mrs.351.r22</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/jedis-3.6.3-h0.cbu.mrs.351.r22.jar</systemPath>
</dependency>
<!-- Hadoop 安全内核依赖(Kerberos底层支撑) -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>3.3.1-h0.cbu.mrs.351.r22</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/hadoop-common-3.3.1-h0.cbu.mrs.351.r22.jar</systemPath>
</dependency>
<!-- Hadoop 依赖缺失的XML解析包 -->
<dependency>
<groupId>com.fasterxml.woodstox</groupId>
<artifactId>woodstox-core</artifactId>
<version>5.4.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/woodstox-core-5.4.0.jar</systemPath>
</dependency>
<!-- 工具类 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.22</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.18</version>
<configuration>
<!-- 开启ZIP布局,支持外部依赖加载 -->
<layout>ZIP</layout>
<!-- 打入system scope定制依赖 -->
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
</plugins>
</build>
5. Kerberos 认证文件获取与校验
接入 MRS 安全 Redis 必须依赖两个核心文件,缺一不可,且来源必须合规:
5.1 文件来源
-
krb5.conf :集群 Kerberos 全局配置,可从 FusionInsight Manager 集群配置页面下载,或从 MRS 服务器
/etc/krb5.conf拷贝 -
redisCli.keytab :Redis 专属客户端凭证,仅可从 MRS 服务器 Redis 安装目录获取,禁止使用任何 Manager 下载的用户凭证
5.2 本地存放路径
在项目资源目录新建文件夹,统一存放认证文件,路径固定:src/main/resources/kerberos/
最终目录结构:
bash
src/main/resources/kerberos/
├─ krb5.conf
└─ redisCli.keytab
5.3 keytab 合法性校验(必做)
上传本地前,在服务器执行校验命令,确认主体合法:
bash
klist -kt redisCli.keytab
合法输出 :票据主体为 redisCli@<MRS_REALM>,无 admin、manager 等运维用户
非法提示:主体非 redisCli,直接废弃该 keytab,重新获取
6. 核心认证代码(完整可运行)
以下为 MRS Redis Kerberos 认证核心代码,严格分为 7 个关键步骤,每一步均为必填,遗漏第六步直接报 NOAUTH 权限拒绝。
6.1 分步核心逻辑注释说明
java
// Step1: 开启JAAS Kerberos认证开关(关闭则不走SASL握手)
System.setProperty("redis.authentication.jaas", "true");
// Step2: 加载集群krb5配置文件(告知JVM KDC地址、Realm、协议规则)
System.setProperty("java.security.krb5.conf", krb5FilePath);
// Step3: 全局注册集群Realm域名
System.setProperty("SERVER_REALM", realm);
// Step4: 初始化认证配置,绑定凭证与主体
AuthConfiguration authConfig = new AuthConfiguration(krb5Path, keytabPath, principal);
authConfig.setServerRealm(realm);
authConfig.setLocalRealm(realm);
// Step5: 全局生效认证配置
GlobalConfig.setAuthConfiguration(authConfig);
// Step6: ★最关键步骤!将认证配置传入Jedis Builder
// 不传递则底层不触发AUTHEXT扩展认证,直接连接失败
builder.authConfiguration(authConfig);
// Step7: 初始化集群连接,完成Kerberos全链路鉴权
JedisCluster cluster = new JedisCluster(nodes, builder.build(), maxAttempts, poolConfig);
6.2 完整集群连接配置类
java
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.auth.AuthConfiguration;
import redis.clients.jedis.global.GlobalConfig;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
/**
* MRS Redis Kerberos 集群认证核心配置
* 适配华为MRS 3.5.1-LTS安全集群
*/
@Configuration
public class RedisClusterConnectionProvider {
@Value("${mrs.redis.nodes}")
private String redisNodes;
@Value("${mrs.redis.kerberos.keytab-path}")
private String keytabPath;
@Value("${mrs.redis.kerberos.krb5-path}")
private String krb5Path;
@Value("${mrs.redis.kerberos.principal}")
private String principal;
@Value("${mrs.redis.kerberos.enabled}")
private Boolean kerberosEnabled;
private final ResourceLoader resourceLoader;
public RedisClusterConnectionProvider(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
public JedisCluster getJedisCluster() throws IOException {
// 解析集群节点列表
Set<HostAndPort> nodes = new HashSet<>();
String[] nodeArr = redisNodes.split(",");
for (String node : nodeArr) {
String[] hostPort = node.split(":");
nodes.add(new HostAndPort(hostPort[0].trim(), Integer.parseInt(hostPort[1].trim())));
}
// 初始化连接池配置
GenericObjectPoolConfig<Object> poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(20);
poolConfig.setMaxIdle(10);
poolConfig.setMinIdle(5);
// 初始化Kerberos认证
if (kerberosEnabled) {
Resource keytabResource = resourceLoader.getResource("classpath:" + keytabPath);
Resource krb5Resource = resourceLoader.getResource("classpath:" + krb5Path);
// 1. 开启JAAS认证
System.setProperty("redis.authentication.jaas", "true");
// 2. 加载krb5配置
System.setProperty("java.security.krb5.conf", krb5Resource.getFile().getAbsolutePath());
// 3. 设置集群Realm
String realm = principal.split("@")[1];
System.setProperty("SERVER_REALM", realm);
// 4. 构建认证实例
AuthConfiguration authConfig = new AuthConfiguration(
krb5Resource.getFile().getAbsolutePath(),
keytabResource.getFile().getAbsolutePath(),
principal
);
authConfig.setServerRealm(realm);
authConfig.setLocalRealm(realm);
// 5. 全局注册
GlobalConfig.setAuthConfiguration(authConfig);
// 6. 绑定到连接构建器
redis.clients.jedis.JedisClientConfig.Builder builder = redis.clients.jedis.JedisClientConfig.builder();
builder.authConfiguration(authConfig);
builder.ssl(false);
// 7. 创建集群实例
return new JedisCluster(nodes, builder.build(), 5, poolConfig);
}
return new JedisCluster(nodes, 5, poolConfig);
}
}
7. RedisTemplate 零侵入桥接配置
为保证业务代码完全零改动 ,通过自定义连接工厂,将已完成 Kerberos 认证的 JedisCluster 注入原生 RedisTemplate,完美兼容原有 Redis 工具类、业务读写逻辑。
java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisCluster;
import javax.annotation.Resource;
import java.io.IOException;
/**
* RedisTemplate 桥接配置
* 业务代码零改动,无缝适配Kerberos认证集群
*/
@Configuration
public class RedisConfig {
@Resource
private RedisClusterConnectionProvider clusterConnectionProvider;
@Bean
public RedisConnectionFactory redisConnectionFactory() throws IOException {
JedisCluster jedisCluster = clusterConnectionProvider.getJedisCluster();
RedisClusterConnection clusterConnection = jedisCluster.getConnection();
return new JedisConnectionFactory() {
@Override
public RedisClusterConnection getClusterConnection() {
return clusterConnection;
}
};
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// Key序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
// Value序列化
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
业务优势 :项目原有@Autowired RedisTemplate、自定义 RedisUtil 工具类无需修改一行代码,无感切换至 MRS 安全 Redis 集群。
8. application.yml 完整配置
yaml
server:
port: 9093
mrs:
redis:
# MRS全量集群节点(双实例端口必须全部写入)
nodes: <NODE1_IP>:22400,<NODE1_IP>:22401,<NODE2_IP>:22400,<NODE2_IP>:22401,<NODE3_IP>:22400,<NODE3_IP>:22401
ssl:
enabled: false
kerberos:
enabled: true
# 资源文件相对路径
keytab-path: kerberos/redisCli.keytab
krb5-path: kerberos/krb5.conf
# 固定redisCli主体+集群Realm
principal: redisCli@<MRS_REALM>
# 连接池参数
pool:
max-total: 20
max-idle: 10
min-idle: 5
配置项详解
-
nodes:必须填写所有节点双实例端口,缺失会导致集群路由分片异常
-
kerberos.enabled:认证总开关,生产环境固定 true
-
principal :格式严格为
redisCli@Realm,禁止拆分、禁止后缀重复 -
ssl.enabled:跟随集群配置,集群关闭则必须 false,强制开启会握手失败
9. 项目启动与连通性验证
9.1 正常启动日志特征
项目正常启动,控制台会依次输出核心认证日志,可用于排查链路是否完整:
-
认证 JAAS=true 认证开关已开启
-
认证 krb5.conf 集群配置加载成功
-
认证 redisCli.keytab 凭证解析成功
-
连接 AuthConfiguration 已绑定Jedis Builder
-
连接 JedisCluster 集群初始化完成,节点数量:6
9.2 健康测试接口
java
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
@RestController
public class RedisHealthController {
@Resource
private RedisTemplate<String, Object> redisTemplate;
@GetMapping("/redis/health")
public Map<String, Object> health() {
Map<String, Object> result = new HashMap<>();
try {
// 测试读写
redisTemplate.opsForValue().set("mrs:kerberos:test", "ok");
String value = (String) redisTemplate.opsForValue().get("mrs:kerberos:test");
result.put("mode", "redisTemplate");
result.put("success", true);
result.put("testValue", value);
} catch (Exception e) {
result.put("success", false);
result.put("msg", e.getMessage());
}
return result;
}
}
9.3 访问验证
请求接口 http://localhost:9093/redis/health,返回如下 JSON 即代表接入成功:
json
{"mode":"redisTemplate","success":true,"testValue":"ok"}
10. 高频报错速查表(生产5大核心问题)
| 报错信息 | 根因分析 | 解决方案 |
|---|---|---|
| NOAUTH Authentication required | 1. 未触发SASL AUTHEXT认证;2. KDC UDP超时;3. 认证配置未传入Builder | 1. krb5.conf新增 udp_preference_limit=1 强制TCP;2. 必须执行 builder.authConfiguration();3. 核对keytab主体为redisCli |
| SocketTimeoutException KDC UDP | 办公网/VPN拦截UDP协议,KDC票据请求超时 | 修改krb5.conf,添加 udp_preference_limit=1,强制使用TCP协议通信 |
| NoSuchMethodException: authConfiguration() | 项目存在标准Jedis依赖冲突,覆盖MRS定制Jedis | 彻底排除所有 redis.clients:jedis 公共依赖,仅保留systemScope定制包 |
| NoClassDefFoundError: commons-pool2 | 排除原生Jedis后,连接池传递依赖丢失 | 手动引入 commons-pool2、commons-lang3 依赖 |
| JedisNoReachableClusterNodeException | keytab主体错误、SSL配置不匹配、节点端口缺失 | 1. 替换为redisCli.keytab;2. 集群SSL关闭则代码关闭ssl;3. 补全所有双实例节点端口 |
11. 总结
华为云 MRS 安全 Redis 的 Kerberos 接入,核心难点不在于代码复杂,而在于集群机制理解、依赖隔离、凭证规范、参数完整性。本文基于生产落地经验,总结三条核心铁律:
-
凭证隔离:严格区分管理员 user.keytab 与业务 redisCli.keytab,绝不混用
-
依赖纯净:彻底排除标准 Jedis,仅使用 MRS 定制依赖,避免方法缺失、认证不触发
-
链路完整:必须开启JAAS、加载krb5配置、绑定Builder认证参数、适配TCP协议,缺一不可
整套方案实现了业务代码零侵入、全链路Kerberos安全认证、生产级稳定可用,可直接复用至所有 MRS 3.x 安全集群国产化项目。