Лучший способ обработки неаутентифицированного запроса в asp.net Web Api

У меня есть стандартный проект VS2013 MVC5 с Web Api 2. В стандартном проекте атрибуты [Authorize] просто возвращают код состояния 401, если запрос не аутентифицирован, в то время как совершенно отдельный модуль перехватывает любые коды 401, останавливает их и вместо этого отправляет перенаправление 302 на страницу входа, указанную в файл Startup.Auth.cs. Это нормально для контроллеров Mvc, но действительно воняет для контроллеров Web Api, потому что, например, браузеры будут автоматически перенаправлять ajax-запросы на URL-адрес входа, поэтому в конечном итоге вы получите статус 200OK, даже если текст ответа — это просто html страницы входа.

Это затрудняет написание хорошего javascript, который может отличить случай, когда вам просто нужно сказать пользователю войти снова, от других видов ошибок. В идеале мы должны быть в состоянии сказать, основываясь на коде состояния, но javascript никогда не видит статус 401. Каков наилучший способ справиться с этим?

Моей первой мыслью было написать атрибут авторизации, но использовать код состояния 403 вместо 401:

public class ApiAuthorizationAttribute : System.Web.Http.AuthorizeAttribute
{
    public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        if (actionContext.RequestContext.Principal.Identity.IsAuthenticated)
        {
            base.OnAuthorization(actionContext);
        }
        else
        {
            actionContext.Response = actionContext.ControllerContext.Request.CreateErrorResponse(HttpStatusCode.Forbidden, "Not signed in.");
        }
    }
}

Конечно, в спецификациях явно указано, что 403 неверен:

Авторизация не поможет и запрос НЕ ДОЛЖЕН повторяться

Моя другая мысль заключается в том, что, возможно, мне следует вообще отключить модуль перенаправления 401 asp.net и обрабатывать перенаправления в пользовательских атрибутах авторизации, потому что даже для представлений Mvc это паршиво, потому что он не позволяет вам перенаправлять на разные страницы входа в зависимости от того, где в сайт, который пользователь пытается посетить.

Существуют ли другие, лучшие подходы к решению этой проблемы?


person Matthew    schedule 16.12.2015    source источник
comment
Этот процесс выполняет перенаправление, когда видит неавторизованные результаты. Есть ли способ сделать это только для запросов, использующих контроллер MVC. Например. MVC наследуется от Controller, но веб-api — это ApiController. Таким образом, вы можете перенаправить на основе типа контроллера, чтобы материал webapi не перенаправлялся.   -  person Ryan Mann    schedule 16.12.2015


Ответы (1)


Вот что я смог найти, проведя немного больше исследований. Ошибка 401 перехватывается промежуточным программным обеспечением OWIN. Но OWIN поддерживает конфигурации ветвления с использованием метода Map. Итак, в файле Startup.cs у меня есть это:

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.Map(new PathString("/api"), site => ConfigureAuth2(site));
        ConfigureAuth(app);

    }
}

где ConfigureAuth — это метод настройки по умолчанию, который находится в файле Startup.Auth.cs, а ConfigureAuth2 — дубликат этого метода, но с опцией LoginPath, оставленной неопределенной в методе UseCookieAuthentication, который выглядит следующим образом:

app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            Provider = new CookieAuthenticationProvider
            {
                // Enables the application to validate the security stamp when the user logs in.
                // This is a security feature which is used when you change a password or add an external login to your account.  
                OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                    validateInterval: TimeSpan.FromMinutes(30),
                    regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
            }
        });

Согласно документации, когда LoginPath не указан, ответы 401 не будут перехватываться для этой ветки.

Таким образом, при таком подходе я разделяю все запросы на две разные конфигурации: все запросы /api настраиваются так, чтобы не перенаправлять на статусы 401, а все остальные настраиваются на перенаправление на страницу входа.

В этом SO-вопросе немного говорилось о разветвлении конфигурации.

Я все еще не уверен, что это лучший подход.

person Matthew    schedule 16.12.2015