我有以下结构:
两个项目都在使用:
AuthorizeFilter
默认需要授权请求。AutoValidateAntiforgeryTokenAttribute
默认需要 AntiforgeryToken。到目前为止,所有交互都运行良好。
我需要在
Project B
中创建一个虚拟授权端点,该端点调用 Project A
中的端点来创建自定义令牌。
在项目 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");
}
我正在尝试从本节中的
ProjectA/Account/Login
表单获取防伪 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();
}
}
为了测试其他可能性,我从当前请求中获取防伪 Cookie 和防伪令牌
Project B
,在这部分中:
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();
上遇到错误。
如果从Project A login page
获取防伪数据,错误是:
[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 上下文)
如果我尝试从
Project B, current request
获取防伪数据,错误是:
[00:33:27 信息] Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.AutoValidateAntiforgeryTokenAuthorizationFilter 防伪令牌验证失败。防伪令牌无法解密。 Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException:无法解密防伪令牌。 ---> System.Security.Cryptography.CryptographyException:有效负载无效。有关更多信息,请访问 http://aka.ms/dataprotectionwarning 在 Microsoft.AspNetCore.DataProtection.Cng.CbcAuthenticatedEncryptor.DecryptImpl(字节 * pbCiphertext,UInt32 cbCiphertext,字节 * pbAdditionalAuthenticatedData,UInt32 cbAdditionalAuthenticatedData) 在 Microsoft.AspNetCore.DataProtection.Cng.Internal.CngAuthenticatedEncryptorBase.Decrypt(ArraySegment
1 extraAuthenticatedData) 在 Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.UnprotectCore(Byte[] protectedData,布尔值allowOperationsOnRevokedKeys,UnprotectStatus&状态) 在 Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.Unprotect(Byte[] protectedData) 在Microsoft.AspNetCore.Antiforgery.DefaultAntiforgeryTokenSerializer.Deserialize(字符串serializedToken) --- 内部异常堆栈跟踪结束 --- 在Microsoft.AspNetCore.Antiforgery.DefaultAntiforgeryTokenSerializer.Deserialize(字符串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 上下文)1 ciphertext, ArraySegment
一个站点无法解密另一个站点的防伪令牌,因为该令牌是使用数据保护 API 加密的。我在我的博客文章中对此进行了描述: https://nestenius.se/2023/11/22/exploring-what-is-inside-the-asp-net-core-cookies/
如果两个站点具有相同的数据保护 API 加密密钥,则两个站点都能够解密彼此的令牌。
数据保护 API 取决于表单内的令牌和相应的防伪 cookie。两者都必须存在才能发挥作用。
但主要问题是,当您想要进行服务间通信时,为什么还要费心防伪呢?您可以简单地使用一些 API 密钥或其他方式来保护通信。