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

.NET+Angular应用Antiforgery令牌解密失败问题求助

.NET + Angular Antiforgery 令牌解密失败问题

问题概述

配置.NET+Angular应用的Antiforgery机制时,已确认请求头X-XSRF-TOKEN与Cookie中的XSRF-TOKEN值完全一致,但服务器仍返回令牌无法解密的错误,多次调整配置未解决。

当前配置详情

1. Session配置

builder.Services.AddSession(options => {
  options.IdleTimeout = TimeSpan.FromMinutes(20);
  options.Cookie.HttpOnly = true;
  options.Cookie.IsEssential = true;
  options.Cookie.SameSite = SameSiteMode.None;
  options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});

2. DataProtection配置

builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"./keys"))
    .UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration {
      EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
      ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
    })
    .SetApplicationName("BudgetTracker")
    .SetDefaultKeyLifetime(TimeSpan.FromDays(14));

3. 控制器特性配置

builder.Services.AddControllersWithViews(options => {
  options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});

4. Antiforgery设置

builder.Services.AddAntiforgery(options => {
  options.HeaderName = "X-XSRF-TOKEN";
  options.Cookie.Name = "XSRF-TOKEN";
  options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
  options.Cookie.HttpOnly = false;
  options.Cookie.SameSite = SameSiteMode.None;
  options.Cookie.Expiration = TimeSpan.FromDays(14);
  options.SuppressXFrameOptionsHeader = false;
  options.FormFieldName = "AntiforgeryField";
});

5. 自定义Antiforgery中间件注册

app.UseMiddleware<AntiForgeryMiddleware>();

中间件代码

public class AntiForgeryMiddleware {
  private readonly IAntiforgery _antiforgery;
  private readonly RequestDelegate _next;
  private readonly ILogger<TokenValidationMiddleware> _logger;

  public AntiForgeryMiddleware(RequestDelegate next, IAntiforgery antiforgery, ILogger<TokenValidationMiddleware> logger) {
    _next = next;
    _antiforgery = antiforgery;
    _logger = logger;
  }

  public async Task Invoke(HttpContext context) {
    try {
      if (string.Equals(context.Request.Path.Value, "/session/csrf", StringComparison.OrdinalIgnoreCase)) {
        var token = _antiforgery.GetAndStoreTokens(context).RequestToken;
        if (!string.IsNullOrEmpty(token)) {
          context.Response.Cookies.Append("XSRF-TOKEN", token, new CookieOptions {
            HttpOnly = false,
            Secure = true,
            SameSite = SameSiteMode.None,
            Expires = DateTimeOffset.UtcNow.AddDays(14)
          });
        }
        _logger.LogInformation($"Generated XSRF-TOKEN: {token}");
      }

    } catch (Exception ex) {
      _logger.LogError(new EventId(), ex, ex.Message);
      throw;
    }

    await _next(context);
  }
}

Angular拦截器代码

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let token = this.tokenExtractor.getToken();
let permitted = this.findByActionName(request.method, this.actions);
let forbidden = this.findByActionName(request.method, this.forbiddenActions);;

    if (permitted !== undefined && forbidden === undefined && token !== null) {   
      request = request.clone({ setHeaders: { "X-XSRF-TOKEN": token }, withCredentials: true });
    }
    return next.handle(request);

}

private findByActionName(name: string, actions: string[]): string | undefined {
  return actions.find(action => action.toLocaleLowerCase() === name.toLocaleLowerCase());
  }
}

日志信息

服务器日志显示请求头与Cookie中的令牌值一致:

info: webapi.Middlewares.AntiForgeryTokenLoggingMiddleware[0]
      Request XSRF-TOKEN: cece034d-b5a2-4042-a884-c4aa9399436b
info: webapi.Middlewares.AntiForgeryTokenLoggingMiddleware[0]
      Request X-XSRF-TOKEN: cece034d-b5a2-4042-a884-c4aa9399436b

错误信息

[19:02:46 INF] Antiforgery token validation failed. The antiforgery token could not be decrypted.
Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException: The antiforgery token could not be decrypted.
---> System.Security.Cryptography.CryptographicException: The provided payload cannot be decrypted because it was not protected with this protection provider.

问题原因及解决方案

核心原因

错误提示表明令牌的加密与解密使用了不同的DataProtection保护提供者,或令牌本身被篡改/重复设置导致不匹配。其中最可能的问题是自定义中间件中手动重复设置了XSRF-TOKEN Cookie:IAntiforgery.GetAndStoreTokens(context)方法已经会自动将生成的令牌存储到Antiforgery配置指定的Cookie中,手动调用context.Response.Cookies.Append会覆盖原有令牌,导致验证时解密失败。

解决方案

  1. 移除中间件中手动设置Cookie的代码
    修改中间件的Invoke方法,删除手动追加Cookie的逻辑,保留GetAndStoreTokens调用即可:

    public async Task Invoke(HttpContext context) {
      try {
        if (string.Equals(context.Request.Path.Value, "/session/csrf", StringComparison.OrdinalIgnoreCase)) {
          var tokens = _antiforgery.GetAndStoreTokens(context);
          _logger.LogInformation($"Generated XSRF-TOKEN: {tokens.RequestToken}");
        }
      } catch (Exception ex) {
        _logger.LogError(new EventId(), ex, ex.Message);
        throw;
      }
    
      await _next(context);
    }
    
  2. 验证DataProtection密钥一致性

    • 确保./keys目录下的密钥文件未损坏,且应用进程拥有该目录的读写权限
    • 若为多实例部署,所有实例必须共享同一个密钥目录
    • 确认SetApplicationName("BudgetTracker")在所有环境中保持一致,该值会影响密钥的隔离性
  3. 检查服务注册顺序
    确保AddDataProtection在AddAntiforgery之前注册,Antiforgery依赖DataProtection进行令牌加密,顺序错误会导致使用默认的临时密钥而非自定义的持久化密钥。

  4. 清理客户端缓存
    清除浏览器的Cookie和缓存,避免使用旧的、用不同密钥加密的令牌。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 16:28:10