基于Redis实现两个C#微服务间Session共享的问题排查与解决
问题原因分析
- Redis缓存键前缀不统一:AuthMicroservice使用
AuthMicroservice:作为Redis缓存的实例名称,所有Session数据会以这个前缀存储;而GameMicroservice使用GameMicroservice:作为前缀,导致它只能查找自身前缀下的Session键,无法读取Auth服务写入的数据。 - DataProtection未统一应用隔离标识:ASP.NET Core的DataProtection默认会以应用程序名称作为加密隔离边界,若两个微服务未指定相同的应用名称,即使共享DataProtectionKeys,也无法解密对方生成的Session Cookie,进而无法正确关联Redis中的Session数据。
正确实现Session共享的配置方案
要实现跨微服务的Session共享,需要保证三个核心配置统一:DataProtection应用名称、Redis缓存实例名称、Session Cookie配置。
步骤1:统一DataProtection配置
在两个微服务的AddDataProtection中添加SetApplicationName,指定相同的全局标识,确保加密解密逻辑一致:
builder.Services.AddDataProtection() .SetApplicationName("SharedSession_App") // 统一应用名称,必须完全一致 .PersistKeysToStackExchangeRedis(redis, "DataProtectionKeys");
步骤2:统一Redis缓存实例名称
将两个服务的AddStackExchangeRedisCache中的InstanceName修改为相同的前缀,比如SharedSession:,这样Session数据会存储在同一组键下:
builder.Services.AddStackExchangeRedisCache(option => { option.Configuration = redisConnectionString; option.InstanceName = "SharedSession:"; // 统一前缀,替换原来的服务专属前缀 });
步骤3:确保Session Cookie配置一致
当前两个服务的Cookie配置已经统一(名称、过期时间等),如果微服务部署在不同子域名下,还需要添加Cookie的Domain配置,确保Cookie能跨子域名共享:
builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromDays(15); options.Cookie.MaxAge = TimeSpan.FromDays(15); options.Cookie.Name = "Session"; options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; options.Cookie.Domain = ".yourdomain.com"; // 若跨子域名部署,需设置统一根域名 });
完整配置示例
AuthMicroservice修改后的配置
var redisConnectionString = builder.Configuration.GetConnectionString("RedisConnection"); var redis = redisConnectionString != null ? ConnectionMultiplexer.Connect(redisConnectionString) : null; // 统一DataProtection配置 builder.Services.AddDataProtection() .SetApplicationName("SharedSession_App") .PersistKeysToStackExchangeRedis(redis, "DataProtectionKeys"); // 统一Redis缓存前缀 builder.Services.AddStackExchangeRedisCache(option => { option.Configuration = redisConnectionString; option.InstanceName = "SharedSession:"; }); // Session配置(保持一致,跨域需添加Domain) builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromDays(15); options.Cookie.MaxAge = TimeSpan.FromDays(15); options.Cookie.Name = "Session"; options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; // options.Cookie.Domain = ".yourdomain.com"; // 按需启用 });
GameMicroservice修改后的配置
var redisConnectionString = builder.Configuration.GetConnectionString("RedisConnection"); var redis = redisConnectionString != null ? ConnectionMultiplexer.Connect(redisConnectionString) : null; // 统一DataProtection配置 builder.Services.AddDataProtection() .SetApplicationName("SharedSession_App") .PersistKeysToStackExchangeRedis(redis, "DataProtectionKeys"); // 统一Redis缓存前缀 builder.Services.AddStackExchangeRedisCache(option => { option.Configuration = redisConnectionString; option.InstanceName = "SharedSession:"; }); // Session配置(保持一致,跨域需添加Domain) builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromDays(15); options.Cookie.MaxAge = TimeSpan.FromDays(15); options.Cookie.Name = "Session"; options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; // options.Cookie.Domain = ".yourdomain.com"; // 按需启用 });
验证方式
修改配置后重启两个服务,在Auth服务中写入Session数据后,查看Redis中的键会以SharedSession:开头;在Game服务中请求时,HttpContext.Session.GetString("UserId")就能正确读取到对应值。
内容的提问来源于stack exchange,提问作者Grg
相关产品推荐
相关产品推荐

