Заголовок Set-Cookie исчезает через некоторое время при использовании httpclient с делегирующим ичингом в ядре ASP.NET CC#

Место общения программистов C#
Ответить
Anonymous
 Заголовок Set-Cookie исчезает через некоторое время при использовании httpclient с делегирующим ичингом в ядре ASP.NET C

Сообщение Anonymous »

У меня есть основной проект .NET с двумя слоями: слой API и слой пользовательского интерфейса, оба в одном решении. Уровень пользовательского интерфейса построен с помощью ASP.NET Core MVC, где контроллеры вызовут службы, а службы используют httpclient , сгенерированные NSWAG Studio для вызова конечных точек API. Он вручную читает файлы cookie из заголовков ответов и хранит их в словаре. Логика состоит в том, что если токен доступа JWT истекает, и я получаю несанкционированный ответ 401 , обработчик извлекает токен обновления из файлов cookie и отправляет его в API обновление . Это отлично работает около двух минут, но после этого сервер перестает отправлять заголовки Set-cookie в ответах API, и их значения становятся нулевыми.

Код: Выделить всё

public class ManualCookieHttpClient : DelegatingHandler
{
private readonly Dictionary _cookies;
private readonly ILocalStorageService _localStorage;
private readonly IConfiguration _configuration;
private readonly IHttpClientFactory _httpClientFactory;

public ManualCookieHttpClient(ILocalStorageService localStorage, IConfiguration configuration, IHttpClientFactory httpClientFactory)
{
_cookies = new Dictionary(StringComparer.OrdinalIgnoreCase);
_localStorage = localStorage;
_configuration = configuration;
_httpClientFactory = httpClientFactory;
}

protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
AddBearerToken(request);
AddCookiesToRequest(request);

var response = await base.SendAsync(request, cancellationToken);
ProcessResponseCookies(response);

if (response.StatusCode == HttpStatusCode.Unauthorized)
{
return await HandleTokenRefresh(request, cancellationToken);
}

return response;
}

private void AddBearerToken(HttpRequestMessage request)
{
//Add Bearer Token...
}

private void AddCookiesToRequest(HttpRequestMessage request)
{
if (_cookies.Count == 0) return;

var cookieHeader = string.Join("; ", _cookies.Select(c => $"{c.Key}={c.Value}"));
request.Headers.Add("Cookie", cookieHeader);
}

private void ProcessResponseCookies(HttpResponseMessage response)
{
try
{
var requestUrl = response.RequestMessage?.RequestUri?.ToString();
Debug.WriteLine($"=== ProcessResponseCookies for: {requestUrl} ===");
Debug.WriteLine($"Response Status: {response.StatusCode}");

if (response.Headers.TryGetValues("Set-Cookie", out var setCookieHeaders))
{
foreach(var cookie in setCookieHeaders)
{
Console.WriteLine(cookie);
}

foreach (var setCookieHeader in setCookieHeaders)
{
var cookieParts = setCookieHeader.Split(';')[0].Split('=');
if (cookieParts.Length == 2)
{
var name = cookieParts[0].Trim();
var value = cookieParts[1].Trim();

if (string.IsNullOrEmpty(value))
continue;

_cookies[name] = value;
}
}
}

if (_cookies.ContainsKey("RefreshToken"))
{
var currentToken = _cookies["RefreshToken"];
}
else
{
Console.WriteLine("there is no refreshToken in headers cookies");
}
}
catch (Exception e)
{
Debug.WriteLine($"Exception in ProcessResponseCookies: {e.Message}");
}
}

private async Task  HandleTokenRefresh(HttpRequestMessage originalRequest, CancellationToken cancellationToken)
{
try
{
var userName = GetUserNameFromToken();

if (string.IsNullOrEmpty(userName))
{
throw new ApiException(
message: "User is not authenticated",
statusCode: 401,
response: "User not authenticated",
headers: null,
innerException: null
);
}

var refreshToken = _cookies.ContainsKey("refreshToken") ? _cookies["refreshToken"] : null;

if (string.IsNullOrEmpty(refreshToken))
{
throw new ApiException(
message: "Refresh Token does not exist",
statusCode: 401,
response: "Refresh token not found",
headers: null,
innerException: null
);
}

var apiAddress = _configuration["ApiAddress"];

var client = _httpClientFactory.CreateClient("RefreshClient");
client.BaseAddress = new Uri(apiAddress!);

var refreshRequest = new HttpRequestMessage(HttpMethod.Post, "api/Account/refreshToken");
AddCookiesToRequest(refreshRequest);

var refreshTokenRequest = new RefreshTokenRequest { UserName = userName, RefreshToken = refreshToken };
refreshRequest.Content = JsonContent.Create(refreshTokenRequest);

var refreshResponse = await client.SendAsync(refreshRequest, cancellationToken);

if (refreshResponse.IsSuccessStatusCode)
{
string newRefreshToken = null!;

if (refreshResponse.Headers.TryGetValues("Set-Cookie", out var setCookieHeaders))
{
foreach (var setCookieHeader in setCookieHeaders)
{
if (setCookieHeader.StartsWith("refreshToken="))
{
var cookieParts = setCookieHeader.Split(';')[0].Split('=');

if (cookieParts.Length == 2)
{
newRefreshToken = cookieParts[1].Trim();
_cookies["refreshToken"] = newRefreshToken;
}
}
}
}

ProcessResponseCookies(refreshResponse);

var authResponse = await refreshResponse.Content.ReadFromJsonAsync();

if (!string.IsNullOrWhiteSpace(authResponse?.Token))
{
_localStorage.SetStorageValue("token", authResponse.Token);

AddCookiesToRequest(originalRequest);

originalRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authResponse.Token);

return await base.SendAsync(originalRequest, cancellationToken);
}
}
else
{
throw new ApiException(
message: "Refreshing Token was not successful, Please login again.",
statusCode: 401,
response: await refreshResponse.Content.ReadAsStringAsync(),
headers: refreshResponse.Headers.ToDictionary(h => h.Key, h =>  h.Value.AsEnumerable()),
innerException: null
);
}
}
catch (ApiException)
{
throw;
}
catch (Exception ex)
{
throw new ApiException(
message: "Refresh Token Error",
statusCode: 401,
response: ex.Message,
headers: null,
innerException: ex
);
}

return await base.SendAsync(originalRequest, cancellationToken);
}

private string? GetUserNameFromToken()
{
//Get UserName From Token...
}
}
my refreshtoken api, который устанавливает обновление cookie после его восстановления:

Код: Выделить всё

[HttpPost("refreshToken")]
public async Task RefreshToken([FromBody] RefreshTokenRequest request)
{
var authenticationResponse = await _authenticationService.RefreshTokenAsync(request.RefreshToken, request.UserName);

if (authenticationResponse == null)
return Unauthorized();

Response.Cookies.Append("RefreshToken", authenticationResponse.RefreshToken!, new CookieOptions
{
HttpOnly = true,
Secure = false,
SameSite = SameSiteMode.Lax,
Path = "/",
Expires = authenticationResponse.RefreshTokenExpirationDate,
});

return Ok(authenticationResponse);
}
< /code>
DelegatingHandler
Настройки в программе.

Код: Выделить всё

services.AddTransient();

services.AddHttpClient(c =>
{
c.BaseAddress = new Uri(configuration.GetSection("ApiAddress").Value!);
})
.AddHttpMessageHandler()
.ConfigurePrimaryHttpMessageHandler(sp =>
{
return new HttpClientHandler
{
UseCookies = false,
AllowAutoRedirect = false
};
});
< /code>
I tried using SameSite = SameSiteMode.None
, но это не решило проблему. Из -за этого я сейчас вручную обрабатываю файлы cookie в моем делегировании и код .

Подробнее здесь: https://stackoverflow.com/questions/797 ... h-delegati
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»