跨项目传递AntiforgeryToken失败问题求助
项目结构
- Project A:带有扩展功能的Duende Identity Server
- Project B:使用Project A进行身份认证的Web客户端
通用配置
两个项目均默认启用:
AuthorizeFilter:要求请求必须经过授权AutoValidateAntiforgeryTokenAttribute:要求请求必须携带反伪造令牌(AntiforgeryToken)
需求
此前所有交互均正常运行,需要在Project B中创建一个已授权的端点,调用Project A中的接口以生成自定义令牌。
Project A测试端点完整代码
[Route("ProjectAController/RequestToken")] [HttpPost] public async Task<IActionResult> RequestToken() { string antiForgeryInputValue = String.Empty; string antiForgeryCookieValue = String.Empty; string antiForgeryCookieKey = String.Empty; var identityPath = _configuration["ProjectA_Link"]; bool getFromIdentityServer = true; if (getFromIdentityServer) { using (var accountClient = _httpClientFactory.CreateClient("IDPClient2")) { var response = await accountClient.GetAsync($"{identityPath}/Account/Login"); var content = await response.Content.ReadAsStringAsync(); IEnumerable<string> cookies = response.Headers.SingleOrDefault(header => header.Key == "Set-Cookie").Value; var rawAntiForgeryCookie = cookies.FirstOrDefault(c => c.StartsWith(".AspNetCore.Antiforgery")); var match = Regex.Match(content, "name=\"__RequestVerificationToken\" type=\"hidden\" value=\"(.*?)\""); if (match.Success) antiForgeryInputValue = match.Groups[1].Value; var cookieAnti = SetCookieHeaderValue.Parse(rawAntiForgeryCookie); antiForgeryCookieValue = cookieAnti.Value.ToString(); antiForgeryCookieKey = cookieAnti.Name.ToString(); } } else { var antiForgeryHeaderTokenKey = Request.Form.FirstOrDefault(f => f.Key.StartsWith("__RequestVerificationToken")).Key; antiForgeryInputValue = Request.Form[antiForgeryHeaderTokenKey].ToString(); antiForgeryCookieKey = Request.Cookies.Keys.FirstOrDefault(c => c.StartsWith(".AspNetCore.Antiforgery")); antiForgeryCookieValue = Request.Cookies[antiForgeryCookieKey]; } var cookieDommainBaseAddress = new Uri(_configuration["ProjectA_Link"]); var container = HttpContext.RequestServices.GetService<CookieContainer>(); container.Add(cookieDommainBaseAddress, new Cookie("idsrv", Request.Cookies["idsrv"])); container.Add(cookieDommainBaseAddress, new Cookie("idsrv.session", Request.Cookies["idsrv.session"])); container.Add(cookieDommainBaseAddress, new Cookie(antiForgeryCookieKey, antiForgeryCookieValue)); using (var accountClient = _httpClientFactory.CreateClient("IDPClient2")) { var dataToPost = new List<KeyValuePair<string, string>>() { { new KeyValuePair<string, string>("__RequestVerificationToken", antiForgeryInputValue) } }; var content = new FormUrlEncodedContent(dataToPost); var result = await accountClient.PostAsync($"{identityPath}/Authentication/GenerateToken", content); result.EnsureSuccessStatusCode(); if (result.IsSuccessStatusCode) { var contentResult = await result.Content.ReadAsStringAsync(); var tokenGeneration = JsonSerializer.Deserialize<TokenGenerationResult>(contentResult, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); if (tokenGeneration != null) return RedirectToAction("AuthStatus", new { referenceToken = tokenGeneration.ReferenceToken, refreshToken = tokenGeneration.RefreshToken }); } } return RedirectToAction("AuthStatus"); }
两种尝试方案及对应代码
方案1:从Project A登录页获取反伪造Cookie和令牌
if (getFromIdentityServer) { using (var accountClient = _httpClientFactory.CreateClient("IDPClient2")) { var response = await accountClient.GetAsync($"{identityPath}/Account/Login"); var content = await response.Content.ReadAsStringAsync(); IEnumerable<string> cookies = response.Headers.SingleOrDefault(header => header.Key == "Set-Cookie").Value; var rawAntiForgeryCookie = cookies.FirstOrDefault(c => c.StartsWith(".AspNetCore.Antiforgery")); var match = Regex.Match(content, "name=\"__RequestVerificationToken\" type=\"hidden\" value=\"(.*?)\""); if (match.Success) antiForgeryInputValue = match.Groups[1].Value; var cookieAnti = SetCookieHeaderValue.Parse(rawAntiForgeryCookie); antiForgeryCookieValue = cookieAnti.Value.ToString(); antiForgeryCookieKey = cookieAnti.Name.ToString(); } }
方案2:从Project B当前请求中获取反伪造Cookie和令牌
else { var antiForgeryHeaderTokenKey = Request.Form.FirstOrDefault(f => f.Key.StartsWith("__RequestVerificationToken")).Key; antiForgeryInputValue = Request.Form[antiForgeryHeaderTokenKey].ToString(); antiForgeryCookieKey = Request.Cookies.Keys.FirstOrDefault(c => c.StartsWith(".AspNetCore.Antiforgery")); antiForgeryCookieValue = Request.Cookies[antiForgeryCookieKey]; }
报错信息
两种方案均在result.EnsureSuccessStatusCode();处触发异常:
方案1异常信息
[23:55:39 信息] Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.AutoValidateAntiforgeryTokenAuthorizationFilter
反伪造令牌验证失败。提供的反伪造令牌属于另一个基于声明的用户,与当前用户不匹配。
Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException: 提供的反伪造令牌属于另一个基于声明的用户,与当前用户不匹配。
在 Microsoft.AspNetCore.Antiforgery.DefaultAntiforgery.ValidateTokens(HttpContext httpContext, AntiforgeryTokenSet antiforgeryTokenSet)
在 Microsoft.AspNetCore.Antiforgery.DefaultAntiforgery.ValidateRequestAsync(HttpContext httpContext)
在 Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.ValidateAntiforgeryTokenAuthorizationFilter.OnAuthorizationAsync(AuthorizationFilterContext context)
方案2异常信息
[00:33:27 信息] Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.AutoValidateAntiforgeryTokenAuthorizationFilter
反伪造令牌验证失败。无法解密反伪造令牌。
Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException: 无法解密反伪造令牌。
---> System.Security.Cryptography.CryptographicException: 载荷无效。
在 Microsoft.AspNetCore.DataProtection.Cng.CbcAuthenticatedEncryptor.DecryptImpl(Byte* pbCiphertext, UInt32 cbCiphertext, Byte* pbAdditionalAuthenticatedData, UInt32 cbAdditionalAuthenticatedData)
在 Microsoft.AspNetCore.DataProtection.Cng.Internal.CngAuthenticatedEncryptorBase.Decrypt(ArraySegment1 ciphertext, ArraySegment1 additionalAuthenticatedData)
在 Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.UnprotectCore(Byte[] protectedData, Boolean allowOperationsOnRevokedKeys, UnprotectStatus& status)
在 Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.Unprotect(Byte[] protectedData)
在 Microsoft.AspNetCore.Antiforgery.DefaultAntiforgeryTokenSerializer.Deserialize(String serializedToken)
--- 内部异常堆栈跟踪结束 ---
在 Microsoft.AspNetCore.Antiforgery.DefaultAntiforgeryTokenSerializer.Deserialize(String serializedToken)
在 Microsoft.AspNetCore.Antiforgery.DefaultAntiforgery.DeserializeTokens(HttpContext httpContext, AntiforgeryTokenSet antiforgeryTokenSet, AntiforgeryToken& cookieToken, AntiforgeryToken& requestToken)
在 Microsoft.AspNetCore.Antiforgery.DefaultAntiforgery.ValidateTokens(HttpContext httpContext, AntiforgeryTokenSet antiforgeryTokenSet)
在 Microsoft.AspNetCore.Antiforgery.DefaultAntiforgery.ValidateRequestAsync(HttpContext httpContext)
在 Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.ValidateAntiforgeryTokenAuthorizationFilter.OnAuthorizationAsync(AuthorizationFilterContext context)
内容的提问来源于stack exchange,提问作者Erick Asto Oblitas

