Авторизация по разным схемам по областям с использованием соглашений в Razor Pages - PullRequest
1 голос
/ 31 мая 2019

Приложение My Razor Pages имеет следующую структуру:

  • Pages
    • Index.cshtml
  • Области
    • Администратор
      • Страницы
        • Index.cshtml
    • API
      • Страницы
        • Указатель.cshtml

Я хочу разрешить анонимный доступ к любой странице, не связанной с областью (что-либо в / Pages /).Я хочу использовать проверку подлинности Windows для всех страниц в области администрирования и авторизацию через токен-носитель для всех страниц в API.

Я могу сделать это, используя атрибуты Authorize непосредственно в PageModels и указав схему.

//Non-area example
[AllowAnonymous]
public class IndexModel : PageModel
//Admin example
[Authorize(AuthenticationSchemes = "Windows")]
public class IndexModel : PageModel
//API example
[Authorize(AuthenticationSchemes = "ApiKey")]
public class IndexModel : PageModel

Затем я могу создать базовую PageModel для каждой из 3 областей и наследовать все PageModel для каждой области из соответствующей базовой PageModel.

Есть ли способвыполнить то же самое, используя соглашения?

services.AddMvc()
    .AddRazorPagesOptions(options =>
    {
        options.Conventions.???
    })

1 Ответ

1 голос
/ 31 мая 2019

Я решил это.Хитрость заключается в том, что AuthorizeFilter может включать схемы с перегрузкой конструктора.

var authorizeFilter = new AuthorizeFilter(new List<IAuthorizeData> {
    new AuthorizeAttribute()
    {
        AuthenticationSchemes = authenticationSchemes
    }
});

Затем мне нужно было написать собственное IPageApplicationModelConvention, которое будет применяться на уровне области.Методы по умолчанию работают на уровне папок и страниц.Я использовал исходный код Microsoft.AspNetCore.Mvc.RazorPages в качестве руководства.

public class AreaModelConvention : IPageApplicationModelConvention
{
    private readonly string _areaName;
    private readonly Action<PageApplicationModel> _action;

    public AreaModelConvention(string areaName, Action<PageApplicationModel> action)
    {
        _areaName = areaName;
        _action = action;
    }

    public void Apply(PageApplicationModel model)
    {
        if(string.Equals(_areaName, model.AreaName, StringComparison.OrdinalIgnoreCase))
        {
            _action(model);
        }
    }
}

Я написал несколько PageConventionCollectionExtensions, как это все делается в Microsoft.AspNetCore.Mvc.RazorPages .

public static class PageConventionCollectionExtensions
{
    public static PageConventionCollection RequireAuthenticationSchemesForArea(this PageConventionCollection conventions, string areaName, string authenticationSchemes)
    {
        if (conventions == null)
        {
            throw new ArgumentNullException(nameof(conventions));
        }

        if (string.IsNullOrEmpty(areaName))
        {
            throw new ArgumentException(nameof(areaName));
        }

        var authorizeFilter = new AuthorizeFilter(new List<IAuthorizeData> {
            new AuthorizeAttribute()
            {
                AuthenticationSchemes = authenticationSchemes
            }
        });

        conventions.AddAreaModelConvention(areaName, model => model.Filters.Add(authorizeFilter));
        return conventions;
    }

    public static IPageApplicationModelConvention AddAreaModelConvention(this ICollection<IPageConvention> pageConventions, string areaName, Action<PageApplicationModel> action)
    {
        if (action == null)
        {
            throw new ArgumentNullException(nameof(action));
        }

        var convention = new AreaModelConvention(areaName, action);

        pageConventions.Add(convention);

        return convention;
    }
}

Наконец, я могу зарегистрировать все это:

services.AddMvc()
    .AddRazorPagesOptions(options =>
    {
        options.Conventions.AllowAnonymousToNonareas();
        options.Conventions.RequireAuthenticationSchemesForArea("Admin", "Windows");
        options.Conventions.RequireAuthenticationSchemesForArea("Api", "ApiKey");
    })

Примечание. Код для AllowAnonymousToNonareas здесь не определен, но он очень похож.Я создал NonareaModelConvention с помощью этого метода Apply:

public void Apply(PageApplicationModel model)
{
    if (model.AreaName == null)
    {
        _action(model);
    }
}

и написал похожие методы расширения, чтобы связать его вместе.

Не забудьте включить для приложения как анонимную аутентификацию, так и аутентификацию Windows.

...