. NET Core Web API: как правильно обрабатывать ошибку истечения срока действия токена и избегать DuplicateKeyException в заголовке - PullRequest
0 голосов
/ 27 марта 2020

Я работаю над проектом с клиентом Angular и несколькими веб-API, где я случайно наткнулся на проблему. И на данный момент, я не знаю, КАК решить эту проблему.

Сначала, что работает:

  1. пользователь входит в систему, получает токен и обновляет sh токен
  2. пользователь вызывает защищенный маршрут веб-API от клиента, например, [Authorize (Roles = "Admin")]
  3. , если срок действия токена истек, пользователь получает новый токен и refre * 1076. * token
  4. повторный вызов от клиента

Работает как задумано, и поэтому я еще не видел проблему.

Что не не работает:

  1. пользователь входит в систему, получает токен и обновляет sh токен
  2. пользователь вызывает незащищенный маршрут Web API, который возвращает открытый заголовок из функции разбивки на страницы
  3. , если токен истек тем временем

, здесь начинается проблема.

Вот как токены обрабатываются в API :

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8
                .GetBytes(Configuration.GetSection("AppSettings:Token").Value)),
            ValidateIssuer = false,
            ValidateAudience = false,
            ValidateLifetime = true, 
            ClockSkew = TimeSpan.Zero,
        };

        options.Events = new JwtBearerEvents
        {
            OnAuthenticationFailed = context =>
            {
                if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                {
                    context.Response.Headers.Add("Token-Expired", "true");
                    context.Response.Headers.Add("Access-Control-Expose-Headers", "Token-Expired");                                
                }
                return Task.CompletedTask;
            }                        
        };
    });

В случае токена с истекшим сроком действия на защищенном маршруте API 401 с открытым заголовком возвращается нед. Клиент Angular реагирует на эту ошибку через перехватчик ошибок, запрашивает новый токен в моем пользовательском API и повторяет вызов исходного API.

Проблема возникает, если вызывается незащищенный маршрут (с токен с истекшим сроком действия), который также имеет открытый заголовок в результате. В моем случае это возвращает нумерованный список и информация о нумерации добавляется в заголовок:

public static void AddPagination(this HttpResponse response, int currentPage, int itemsPerPage, int totalItems, int totalPages)
{
    var paginationHeader = new PaginationHeader(currentPage, itemsPerPage, totalItems, totalPages);

    var camelCaseFormatter = new JsonSerializerSettings();
    camelCaseFormatter.ContractResolver = new CamelCasePropertyNamesContractResolver();

    response.Headers.Add("Pagination", JsonConvert.SerializeObject(paginationHeader, camelCaseFormatter));
    response.Headers.Add("Access-Control-Expose-Headers", "Pagination");
}

В этом случае поток выглядит так:

  1. событие OnAuthenticationFailed вызывается и Token -Expired добавляется и отображается в заголовке ответа.
  2. Нет 401 возвращается (я полагаю, потому что маршрут не защищен) клиенту
  3. он продолжается до изначально называется незащищенным маршрутом и добавляет заголовок пагинации и пытается также разоблачить это
  4. , и это приводит к ошибке 500
2020-03-27 01:32:54.8663|1|ERROR|Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware|An unhandled exception has occurred while executing the request. System.ArgumentException: An item with the same key has already been added.
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ThrowDuplicateKeyException()
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.System.Collections.Generic.IDictionary<System.String,Microsoft.Extensions.Primitives.StringValues>.Add(String key, StringValues value)

На данный момент я понятия не имею, как чтобы обойти это, потому что

  • Почему я вообще получаю событие OnAuthenticationFailed, если не вызывается защищенный маршрут? Да, токен истек, но для незащищенного маршрута аутентификация не требуется.
  • Как в этом случае избежать DuplicateKeyException?

Спасибо за помощь.

РЕДАКТИРОВАТЬ:

На данный момент я решил проблему с помощью того, что я проверяю уже существующий ключ в заголовках и удаляю его, но я не знаю, если это правильный путь:

public static void AddPagination(this HttpResponse response, int currentPage, int itemsPerPage, int totalItems, int totalPages)
{
    var paginationHeader = new PaginationHeader(currentPage, itemsPerPage, totalItems, totalPages);

    var camelCaseFormatter = new JsonSerializerSettings();
    camelCaseFormatter.ContractResolver = new CamelCasePropertyNamesContractResolver();

    StringValues locationHeaderValue = string.Empty;
    if (response.Headers.TryGetValue("Access-Control-Expose-Headers", out locationHeaderValue))
    {
        response.Headers.Remove("Access-Control-Expose-Headers");
    }

    response.Headers.Add("Pagination", JsonConvert.SerializeObject(paginationHeader, camelCaseFormatter));
    response.Headers.Add("Access-Control-Expose-Headers", "Pagination");
}
...