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

如何解密ASP.NET Core 8跨服务器身份验证Cookie并获取用户名?

如何解密ASP.NET Core 8跨服务器身份验证Cookie并获取用户名?

问题背景

某ASP.NET Core 8 MVC应用在Startup.cs中配置了Cookie身份验证,核心代码如下:

services.AddDataProtection().PersistKeysToFileSystem(new DirectoryInfo(@"Voti"));

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
        {  
            options.DataProtectionProvider = DataProtectionProvider.Create(new DirectoryInfo(@"Voti"));
            options.Cookie = new CookieBuilder()
                {
                    Name = ".EevaAuth",
                    IsEssential = true,
                    SameSite = SameSiteMode.Strict
                };
            options.LoginPath = new PathString("/Account/LogOn");
            options.ReturnUrlParameter = "returnUrl";
            options.SlidingExpiration = true;
            options.ExpireTimeSpan = TimeSpan.FromDays(30);
       });

应用部署在不同DNS服务器上,密钥文件保持一致,但解密跨服务器Cookie时遇到两个问题:

  1. 参考Stack Overflow编写的手动解密代码抛出异常:

System.Security.Cryptography.CryptographicException: The payload was invalid.
...
at MyApp.Controllers.Erp.AboutController.DecryptCookie()

  1. 使用TicketDataFormat解密时,同服务器Cookie可正常获取用户名,但其他服务器Cookie返回null。

核心问题分析

  • 手动解密报错:DataProtection的上下文不匹配,包括未统一应用名称、手动创建的DataProtectionProvider与全局配置不一致,导致保护目的字符串校验失败。
  • 跨服务器返回null:Cookie域名配置不统一,且TicketDataFormat依赖的加密上下文未与生成Cookie的服务器对齐。

解决方案

步骤1:统一跨服务器的DataProtection与Cookie配置

修改Startup.cs中的配置,确保所有服务器共享相同的加密上下文和Cookie规则:

services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"Voti"))
    .SetApplicationName("EevaAuthApp"); // 所有服务器必须设置相同的应用名称

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        // 复用全局配置的DataProtectionProvider,避免手动创建导致上下文不一致
        options.DataProtectionProvider = services.BuildServiceProvider().GetRequiredService<IDataProtectionProvider>();
        options.Cookie = new CookieBuilder()
        {
            Name = ".EevaAuth",
            IsEssential = true,
            SameSite = SameSiteMode.Strict,
            Domain = ".yourdomain.com" // 设置根域名,让所有子服务器共享Cookie
        };
        options.LoginPath = new PathString("/Account/LogOn");
        options.ReturnUrlParameter = "returnUrl";
        options.SlidingExpiration = true;
        options.ExpireTimeSpan = TimeSpan.FromDays(30);
    });

步骤2:正确的跨服务器Cookie解密代码

使用框架自带的TicketDataFormat解密,确保与认证中间件的配置完全对齐:

private readonly IOptionsMonitor<CookieAuthenticationOptions> _cookieOptionsMonitor;

// 通过构造函数注入依赖
public YourController(IOptionsMonitor<CookieAuthenticationOptions> cookieOptionsMonitor)
{
    _cookieOptionsMonitor = cookieOptionsMonitor;
}

public string DecryptCrossServerCookie(string cookieValue)
{
    var authScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    var options = _cookieOptionsMonitor.Get(authScheme);
    
    // 确保Cookie域名与生成时一致
    options.Cookie.Domain = ".yourdomain.com";
    
    // 使用框架原生的TicketDataFormat解密,自动处理DataProtection细节
    AuthenticationTicket ticket = options.TicketDataFormat.Unprotect(cookieValue);
    
    if (ticket == null)
    {
        throw new InvalidOperationException("无法解密Cookie,可能是密钥不匹配或Cookie已失效");
    }
    
    return ticket.Principal.Identity.Name;
}

步骤3:关键注意事项

  • 共享密钥存储:所有服务器必须能访问同一个Voti目录(如网络共享文件夹),确保密钥文件完全一致,且应用池账户拥有读写权限。
  • 统一应用名称:SetApplicationName的值必须在所有服务器上完全相同,DataProtection会通过该名称隔离不同应用的加密上下文。
  • Cookie域名配置:将Cookie.Domain设为根域名(如.yourdomain.com),确保子域名服务器都能接收并处理该Cookie。
  • 避免手动创建DataProtectionProvider:手动创建的Provider若未设置应用名称,会导致加密上下文不匹配,引发解密失败。

原错误代码问题说明

  1. 手动创建DataProtectionProvider时未设置应用名称,与全局配置的加密上下文不一致,即使保护目的字符串参数正确也无法解密。
  2. 跨服务器解密时临时修改Cookie.Domain的方式错误,应在全局配置中统一设置,否则TicketDataFormat的内部规则不匹配。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 08:30:57