Авторизация MVC внутри MapWhen () применяется ко всем контроллерам

В ASP.Net Core 3.0 Preview 7 я попытался написать следующий код:

public void Configure(IApplicationBuilder app) {
    app.MapWhen(context =>
        context.Request.Path.StartsWithSegments(
              new PathString("/UnsecureLog")),
        a => {
            a.UseRouting();
            a.UseEndpoints(endpoints => {
                endpoints.MapControllers();
            });
        }
    );
    
    app.UseAuthentication();
    app.UseAuthorization();

    app.MapWhen(context =>
        context.Request.Path.StartsWithSegments(
           new PathString("/SecureLog")),
        a => {
            a.UseRouting();
            a.UseEndpoints(endpoints => {
                endpoints.MapControllers()
                    .RequireAuthorization("MustBeReader");
            });
        }
    );
}

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

Вместо этого я вижу эту ошибку при попадании в конечную точку /UnsecureLog:

System.InvalidOperationException: Endpoint ... contains authorization metadata,
but a middleware was not found that supports authorization.
Configure your application startup by adding app.UseAuthorization()
inside the call to Configure(..) in the application startup code.

Перевод: Как насчет того, чтобы реализовать функции безопасности для той конечной точки, которую вы не хотели защищать.

Мой вывод состоит в том, что любой вызов RequireAuthorization("MustBeReader") в любой логике контроллера обработки блока MapWhen () будет фактически применяться ко всем маршрутам контроллера MVC.

Мой текущий обходной путь - удалить вызов .RequireAuthorization("MustBeReader") во втором блоке кода MapWhen () и повторно применить его как атрибут ([RequireAuthorization("MustBeReader")]) к тем конечным точкам, которые я хочу защитить. Это работает без ошибок и обеспечивает желаемое поведение.

Но цель была не в этом, не так ли?

Я бы предпочел управлять целыми группами контроллеров с аналогичными политиками, избавляя других от безопасности вообще, и обрабатывать все это из Configure (). Вместо этого я должен применять желаемые требования авторизации к каждому контроллеру по частям.

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

Мысли, кто-нибудь?


person Wellspring    schedule 07.08.2019    source источник
comment
Я также с тех пор обнаружил, что верно и обратное ... Если вы вызовете MapWhen () дважды и сделаете это в ОДНОМ из них .WithMetadata(new AllowAnonymousAttribute()) - угадайте, что? Ни к одному из них не применяется безопасность. Даже если у другого было множество политик, применяемых повсюду.   -  person Wellspring    schedule 11.03.2020


Ответы (4)


Переместите приведенный ниже код выше в своем классе Startup (выше app.MapWhen).

 app.UseAuthentication();
 app.UseAuthorization();
person Oyebode    schedule 08.10.2019
comment
Если бы я это сделал, все маршруты были бы защищены. Достижение противоположного заявленной цели. - person Wellspring; 09.10.2019
comment
Спасибо. Это была моя проблема docs.microsoft.com/en-us/aspnet/core/migration/ - person FindOutIslamNow; 09.10.2019
comment
Мне кажется немного забавным, что ответ, который (насколько мне известно) не отвечает на мой вопрос, должен продолжать накапливать очки. Или я что-то пропустил, и шутка уже написана? По моему мнению, опубликованное решение устраняет сообщение об ошибке ... и тем же ходом также удаляет желаемую функциональность, которую я стремился достичь. Цвет меня смутил. - person Wellspring; 21.01.2020
comment
@Wellspring Я полагаю, что он решил ряд проблем пользователей, хотя и не ваших. - person Collin Klopfenstein; 19.02.2020
comment
Спасибо! Это устранило мою проблему. - person McCrockett; 20.02.2020

Важно отметить, что app.UseAuthorization должен находиться между app.UseRouting() и app.UseEndpoints(...);

Итак, это должно выглядеть примерно так:

        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller}/{action=Index}/{id?}");
        });
person mwilson    schedule 11.02.2020
comment
Могут ли UseAuthorization и UseAuthorization быть помещены в отдельные вызовы MapWhen ()? Интересно, пробовал ли я это уже, но попробую, когда у меня будет шанс. Если это сработает, то ваше решение, которое я искал. - person Wellspring; 11.02.2020

Я отдаю себе должное за полный ответ, потратив сегодня некоторое время на это и разместив код. Спасибо mwilson за то, что подтолкнул меня попробовать еще раз.

До моего нового подхода при каждом запуске кода отображалось это предупреждение ...

Startup.cs(189,13): warning ASP0001: The call to UseAuthorization
should appear between app.UseRouting() and app.UseEndpoints(..)
for authorization to be correctly evaluated.

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

Вот что я сегодня выяснил. Я разместил вызовы UseAuthentication и UseAuthorization в двух разных местах. Это работает. В некотором роде.

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

FWIW, код ниже:

        public void Configure(...)
        {
            ...
            ...
            app.UseStaticFiles();
            app.UseRouting();

            app.MapWhen(
                context => (context.Request.Path
                    .StartsWithSegments(new PathString("/api"))
                ),
                a =>
                {
                    a.UseRouting();
                    a.UseAuthentication();
                    a.UseAuthorization();
                    a.UseEndpoints(endpoints =>
                    {
                        endpoints
                            .MapControllers()
                            .RequireAuthorization(...);
                    });
                }
            );

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseWebSockets();
            app.UseGraphQLWebSockets<...>(...);
            app.UseGraphQL<...>(...);
        }

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


    [Route("vapi/[controller]")]
    //[AllowAnonymous]
    public class VersionController : Controller
    { ...

вместе с этим дополнительным кодом Startup.cs ...

            app.MapWhen(
                context => (context.Request.Path
                    .StartsWithSegments(new PathString("/vapi"))
                ),
                a =>
                {
                    a.UseRouting();
                    // a.UseAuthentication(); <-- look, Ma, no authentication!
                    // a.UseAuthorization(); <-- look, Ma, no authorization!
                    a.UseEndpoints(endpoints =>
                    {
                        endpoints
                            .MapControllers()
                            .RequireAuthorization(...);
                    });
                }
            );

Я также все еще получаю ошибку, указанную в моем OP. (Это все возвращается ко мне, как в старый кошмар ...)

Endpoint ....Controllers.VersionController.Version (...) contains authorization
metadata, but a middleware was not found that supports authorization.
Configure your application startup by adding app.UseAuthorization() ...

Итак, я оставляю дело в следующем: я все еще не могу делать то, что хотел, а именно защищать только определенные пути к контроллерам. Что я могу сделать (без добавления кода поддержки пути "vapi" в автозагрузку):

    [Route("api/[controller]")]
    [AllowAnonymous]
    public class VersionController : Controller
    { ...

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

person Wellspring    schedule 11.02.2020

Я использую атрибут Authorize для классов / контроллеры, чтобы заставить систему требовать аутентифицированного пользователя. Если вы укажете параметр для этого атрибута, это потребует от пользователя наличия этого утверждения. Для любого контроллера или метода, которые должны быть доступны без аутентификации, мы устанавливаем AllowAnonymous -атрибут контроллера или конкретного метода. Это должно отменять любые требования к авторизации.

person Mark Elissen    schedule 26.08.2019
comment
Я подтвердил, что [AllowAnonymous] ничего не делает для предотвращения этой ошибки. Но даже если бы это было так, моя цель заключалась в том, чтобы иметь возможность в конвейере промежуточного программного обеспечения по-разному обрабатывать целые группы контроллеров, а не добавлять атрибуты к контроллерам по частям. Представьте, например, группу контроллеров в маршруте с префиксом / secured и другую группу с префиксом / unsecured. - person Wellspring; 30.08.2019
comment
Сдаваться. Либо я безнадежно запутался, либо я слишком много прошу от текущего набора функций pipleline. Я нашел интересные переулки для прогулки, в том числе это и это однако я думаю, что я свистну в темноте мимо этого отвлечения и продолжу жить без того, на что я здесь надеялся. - person Wellspring; 30.08.2019