Действие проверки подлинности / авторизации веб-сайта MVC и web-api (Framework 4.6.1) - PullRequest
2 голосов
/ 10 июня 2019

У меня есть веб-приложение MVC, использующее аутентификацию и авторизацию модели идентификации, которая работает очень хорошо. Это же веб-приложение также поддерживает веб-API REST. До сих пор все вызовы API работали неавторизованно (по замыслу). Теперь у меня есть необходимость поддерживать аутентифицированные вызовы API, но я не могу отделить поведение при сбое аутентификации Web API от поведения на веб-сайте. Веб-API не подходит для перенаправления на страницу входа в случае сбоя аутентификации, вместо этого он должен возвращать вызывающему HTTP-ответ 401.

Контроллеры для веб-страниц используют AuthorizeAttribute на маршрутах, которые должны быть авторизованы, и если не авторизованный пользователь пытается получить к ним доступ, они перенаправляются на страницу входа.

Я реализовал собственный IAuthenticationFilter, чтобы я мог пометить вызовы API с помощью атрибута HmacAuthenticationAttribute. (Я почти уверен, что конкретный механизм аутентификации здесь не имеет значения.) При использовании в автономном прототипе web-api он работает отлично, и ответом на запрос клиента является правильный код Http 401 в случае сбоя аутентификации.

Когда точно такой же фильтр добавляется в веб-интерфейс в моем реальном веб-приложении, ошибка аутентификации приводит к перенаправлению на страницу входа в систему, и клиент получает ответ Http 200 OK со страницей входа html в теле ответа. Это явно НЕ желаемое поведение.

Может ли кто-нибудь дать представление о том, как отделить веб-страницу от ответов веб-API при использовании фильтров атрибутов аутентификации?

Если нет, мне нужно будет выполнить рефакторинг, чтобы не использовать атрибут, и вместо этого вызвать мою аутентификацию в качестве метода, но не похоже, что это необходимо.

1 Ответ

0 голосов
/ 10 июня 2019

Ваша проблема в том, что промежуточное ПО аутентификации работает в конвейере запросов для всех маршрутов.Вам нужно настроить разные методы аутентификации для разных запросов.

Посмотрите здесь для получения дополнительной информации о условном промежуточном программном обеспечении.

Давайте предположим, что ваш API аутентифицируется api-key заголовок, и ваше обычное приложение аутентифицируется с помощью файла cookie или токена на предъявителя.

В вашем Startup.cs

// This will authenticate calling applications based on the "api-key" header
app.UseWhen(context => context.Request.Headers.ContainsKey("api-key"), appBuilder =>
{
    appBuilder.UseMiddleware<HmacAuthentication>();
});


// Using the exact opposite, makes all other requests authenticate the normal way
app.UseWhen(context => !context.Request.Headers.ContainsKey("api-key"), appBuilder =>
{
    appBuilder.UseAuthentication();
    appBuilder.UseIdentityServer();
});

Если вы измените свой HmacAttribute и вставите тот же код в некоторое промежуточное ПО, тогда выимеют 2 разных пути для аутентификации и могут использовать атрибуты Authorize или Policies в зависимости от необходимости.

Ваше промежуточное ПО может выглядеть примерно так:

public class HmacAuthentication
{

    private const string ApiKey = "api-key";
    private RequestDelegate _next;

    public ApiKeyMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {

        bool success = false;
        string[] claims = null;

        success = GetHeader(context.Request.Headers, out StringValues headerValue);

        if (success)
        {                
            success = ValidateKeyAndGetClaims(headerValue, out claims);                
        }

        if (success)
        {
            context.User = GetPrincipal(headerValue, claims);
            await _next(context);
        }
        else
        {
            context.Response.StatusCode = 401;
            await context.Response.WriteAsync("Unauthorized");
        }            
    }

    internal bool GetHeader(IHeaderDictionary headers, out StringValues headerValue)
    {
        return headers.TryGetValue(ApiKey, out headerValue);
    }

    internal bool ValidateKeyAndGetClaims(string, headerValue, out string[] claims)
    {
        // Validate the api-key.
        // Claims could depend on the key value or could be hardcoded
        claims = new [] { "IsAuthenticated" };
        return true;
    }        

    internal ClaimsPrincipal GetPrincipal(string apiKey, string[] claims)
    {
        var identity = new ClaimsIdentity();
        identity.AddClaim(new Claim(ClaimTypes.Name, apiKey));
        if (claims != null)
        {
            foreach(var claim in claims)
            {
                identity.AddClaim(new Claim(claim, string.Empty));
            }
        }

        return new ClaimsPrincipal(identity);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...