.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会覆盖原有令牌,导致验证时解密失败。
解决方案
移除中间件中手动设置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); }验证DataProtection密钥一致性
- 确保
./keys目录下的密钥文件未损坏,且应用进程拥有该目录的读写权限 - 若为多实例部署,所有实例必须共享同一个密钥目录
- 确认
SetApplicationName("BudgetTracker")在所有环境中保持一致,该值会影响密钥的隔离性
- 确保
检查服务注册顺序
确保AddDataProtection在AddAntiforgery之前注册,Antiforgery依赖DataProtection进行令牌加密,顺序错误会导致使用默认的临时密钥而非自定义的持久化密钥。清理客户端缓存
清除浏览器的Cookie和缓存,避免使用旧的、用不同密钥加密的令牌。
内容的提问来源于stack exchange,提问作者Eugenio Danilchenko

