You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Forms与ASP.NET Core Redis Session跨站点共享方案咨询

ASP.NET Forms与ASP.NET Core Redis Session共享解决方案

针对你的问题,核心矛盾在于传统ASP.NET(Forms)和ASP.NET Core的Session加密/序列化机制不兼容:前者用MachineKey加密Cookie、BinaryFormatter序列化Session数据;后者默认用DataProtection系统、JSON序列化。下面分两种场景给出解决方案:


一、不修改ASP.NET Forms主站点(当前3个月冻结期)

这种场景需要让ASP.NET Core主动兼容传统ASP.NET的机制,步骤如下:

1. 安装必要NuGet包

Install-Package Microsoft.AspNetCore.DataProtection.SystemWeb
Install-Package StackExchange.Redis

2. 配置DataProtection兼容MachineKey

传统ASP.NET的ASP.NET_SessionId Cookie是用WebConfig中的machineKey加密的,Core需要用相同的密钥来解密。在Startup.cs的ConfigureServices中添加:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    // 从Forms站点的WebConfig复制machineKey的decryptionKey和validationKey
    var machineKeySettings = new MachineKeySettings
    {
        DecryptionKey = "你的Forms站点decryptionKey",
        ValidationKey = "你的Forms站点validationKey",
        Decryption = "AES",
        Validation = "HMACSHA256"
    };

    // 配置DataProtection使用传统MachineKey解密Cookie
    var dataProtectionProvider = DataProtectionProvider.Create(config =>
    {
        config.SetApplicationName("myApp"); // 与Forms站点的applicationName一致
        config.UseMachineKey(machineKeySettings);
    });

    services.AddDataProtection().UseProvider(dataProtectionProvider);

3. 自定义Redis Session存储,兼容BinaryFormatter序列化

传统ASP.NET的RedisSessionProvider用BinaryFormatter序列化Session数据,Core默认用JSON,所以需要自定义SessionStore来匹配:

// 配置Redis连接
    string redisConn = Configuration.GetValue<string>("Redis:ConnectionString");
    var redis = ConnectionMultiplexer.Connect(redisConn);
    services.AddSingleton<IConnectionMultiplexer>(redis);

    // 添加自定义Session存储
    services.AddSingleton<ISessionStore, BinaryFormatterRedisSessionStore>();

    // 配置Session参数与Forms站点一致
    services.AddSession(options =>
    {
        options.Cookie.Name = "ASP.NET_SessionId";
        options.Cookie.Path = "/"; // 与Forms站点Cookie路径一致
        options.IdleTimeout = TimeSpan.FromMinutes(20); // 与Forms站点Session超时一致
        options.Cookie.HttpOnly = true;
    });
}

自定义SessionStore实现代码

public class BinaryFormatterRedisSessionStore : ISessionStore
{
    private readonly IConnectionMultiplexer _redis;
    private readonly string _appName = "myApp";

    public BinaryFormatterRedisSessionStore(IConnectionMultiplexer redis)
    {
        _redis = redis;
    }

    public ISession Create(string sessionId, TimeSpan idleTimeout, TimeSpan ioTimeout, Func<bool> tryEstablishSession, bool isNewSession)
    {
        return new BinaryFormatterRedisSession(sessionId, idleTimeout, _redis.GetDatabase(), _appName);
    }
}

public class BinaryFormatterRedisSession : ISession
{
    private readonly string _sessionId;
    private readonly TimeSpan _idleTimeout;
    private readonly IDatabase _redisDb;
    private readonly string _appName;
    private Dictionary<string, byte[]> _sessionData;
    private bool _isModified;

    public BinaryFormatterRedisSession(string sessionId, TimeSpan idleTimeout, IDatabase redisDb, string appName)
    {
        _sessionId = sessionId;
        _idleTimeout = idleTimeout;
        _redisDb = redisDb;
        _appName = appName;
        _sessionData = new Dictionary<string, byte[]>();
    }

    public string Id => _sessionId;
    public bool IsAvailable => true;
    public IEnumerable<string> Keys => _sessionData.Keys;

    public Task LoadAsync(CancellationToken cancellationToken = default)
    {
        var redisKey = $"{_appName}:{_sessionId}";
        var data = _redisDb.StringGet(redisKey);
        if (!data.IsNull)
        {
            using var ms = new MemoryStream(data);
            var formatter = new BinaryFormatter();
            _sessionData = (Dictionary<string, byte[]>)formatter.Deserialize(ms);
            _redisDb.KeyExpire(redisKey, _idleTimeout);
        }
        return Task.CompletedTask;
    }

    public Task CommitAsync(CancellationToken cancellationToken = default)
    {
        if (_isModified)
        {
            var redisKey = $"{_appName}:{_sessionId}";
            using var ms = new MemoryStream();
            var formatter = new BinaryFormatter();
            formatter.Serialize(ms, _sessionData);
            _redisDb.StringSet(redisKey, ms.ToArray(), _idleTimeout);
            _isModified = false;
        }
        return Task.CompletedTask;
    }

    public bool TryGetValue(string key, out byte[] value) => _sessionData.TryGetValue(key, out value);
    public void Set(string key, byte[] value) { _sessionData[key] = value; _isModified = true; }
    public void Remove(string key) { _sessionData.Remove(key); _isModified = true; }
    public void Clear() { _sessionData.Clear(); _isModified = true; }
}

4. 确保中间件顺序正确

在Configure方法中,UseSession必须在UseMvc之前:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseHttpsRedirection();
    app.UseRouting();

    app.UseSession(); // 必须在UseMvc之前
    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}

二、未来可修改ASP.NET Forms主站点时的优雅方案

这种场景下,我们统一两边的加密和序列化机制,让Forms站点使用ASP.NET Core的DataProtection系统,步骤如下:

1. 在Forms站点中配置DataProtection

  • 安装NuGet包:Install-Package Microsoft.AspNetCore.DataProtection.SystemWeb
  • 在Global.asax的Application_Start中添加:
protected void Application_Start()
{
    // 配置DataProtection与Core站点共享密钥存储(Redis)
    var redisConn = ConfigurationManager.ConnectionStrings["RedisConnection"].ConnectionString;
    var redis = ConnectionMultiplexer.Connect(redisConn);
    var dataProtectionProvider = DataProtectionProvider.Create(config =>
    {
        config.SetApplicationName("myApp");
        config.PersistKeysToRedis(redis, "DataProtection-Keys"); // 与Core站点的密钥存储路径一致
    });

    // 替换传统MachineKey为DataProtection
    DataProtectionSetup.SetupMachineKey(dataProtectionProvider);
}

2. 统一Session序列化方式

如果需要更灵活的序列化(比如JSON),可以自定义Forms站点的RedisSessionProvider序列化器,或者保持BinaryFormatter(Core站点也用对应的自定义SessionStore)。

3. 同步两边配置

确保Forms和Core站点的:

  • Session超时时间一致
  • Cookie名称均为ASP.NET_SessionId
  • Redis连接字符串一致
  • ApplicationName一致

这样修改后,两边的Session加密和序列化机制完全统一,Session共享会更稳定可靠。


内容的提问来源于stack exchange,提问作者Hendel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:30:32