Авторизация на основе ролей в ASP.NET Core

Когда удостоверение пользователя создается после проверки подлинности, пользователь может принадлежать к одной или нескольким ролям, что отражает различные авторизаций, которые пользователь имеет для получения доступа к данным и выполнения операций. Например, Трейси может принадлежать ролям "Администратор" и "Пользователь" с доступом к административным веб-страницам в приложении, в то время как Скотт может принадлежать только роли "Пользователь" и не иметь доступа к административным данным или операциям. Создание и управление этими ролями зависит от резервного хранилища процесса авторизации. Роли предоставляются разработчику через ClaimsPrincipal.IsInRole. AddRoles необходимо вызвать для добавления служб ролей при настройке системы удостоверений приложения.

Хотя роли являются утверждениями, не все утверждения являются ролями. В зависимости от поставщика удостоверений, роль может представлять собой совокупность пользователей, способных применять утверждения к участникам группы, а также служить фактическим утверждением для удостоверения личности. Однако утверждения должны быть сведениями об отдельном пользователе. Использование ролей для добавления утверждений пользователю может запутать границу между пользователем и их отдельными утверждениями. Эта путаница заключается в том, что шаблоны одностраничных приложений (SPA) не предназначены для ролей. Кроме того, для организаций, мигрирующих с локальной устаревшей системы, увеличение количества ролей в течение многих лет может означать, что утверждение права роли может быть слишком большим, чтобы содержаться в токене, который можно использовать SPA. Для защиты SPA см. Использование Identity для защиты серверной части веб-API для SPA.

В этой статье используются Razor примеры компонентов и рассматриваются Blazor сценарии авторизации. Дополнительные Blazor рекомендации см. в разделе "Дополнительные ресурсы ". Инструкции по Pages и MVC можно найти в следующих ресурсах:

Identity конфигурация изменилась с выпуском .NET 6. Примеры в этой статье демонстрируют подходы, которые настраивают Identity службы в файле приложения Program. Для приложений .NET до выпуска .NET 6 (и до выпуска Blazor Web App с .NET 8) службы настраиваются в Startup.ConfigureServices файла Startup.cs. Синтаксис конфигурации Identity показан в статье авторизации на основе ролей для Razor Pages и в статье авторизации на основе ролей для MVC. Ознакомьтесь с предыдущими ресурсами и настройте селектор версии статьи на версию .NET, на которую нацелено ваше приложение.

Пример приложения

Пример для этой статьи — это приложение ( репозиторий GitHub) (как скачать). В примере приложения используются заполненные учетные записи с предварительно настроенными ролями для демонстрации большинства примеров в этой статье. Дополнительные сведения см. в файле README примера (README.md).

Предостережение

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

Добавьте службы ролей в Identity

Зарегистрируйте службы авторизации на основе ролей в файле Program, вызвав AddRoles с типом роли в конфигурации приложения Identity. Тип роли в следующем примере:IdentityRole

builder.Services.AddDefaultIdentity<IdentityUser>( ... )
    .AddRoles<IdentityRole>()
    ...

Для предыдущего кода требуется пакет NuGet Майкрософт.AspNetCore.Identity.UI и директива using для Майкрософт.AspNetCore.Identity.

В случаях, когда приложение получает детализированный контроль для сборки Identity вручную, вызовите AddRoles на AddIdentityCore.

builder.Services.AddIdentityCore<IdentityUser>()
    .AddRoles<IdentityRole>()
    ...

Зарегистрируйте службы авторизации на основе ролей в Startup.ConfigureServices (Startup.cs) путем вызова AddRoles с типом роли в конфигурации приложения Identity . Тип роли в следующем примере:IdentityRole

services.AddDefaultIdentity<IdentityUser>()
    .AddRoles<IdentityRole>()
    ...

Для предыдущего кода требуется пакет NuGet Майкрософт.AspNetCore.Identity.UI и директива using для Майкрософт.AspNetCore.Identity.

В случаях, когда приложение получает детализированный контроль для сборки Identity вручную, вызовите AddRoles на AddIdentityCore.

services.AddIdentityCore<IdentityUser>()
    .AddRoles<IdentityRole>()
    ...

В Blazor Web App (.NET 8 или более поздняя версия) вызов UseAuthorization в файле Program не требуется.

В приложениях Blazor Server вызовите UseAuthorization в файле Program после строки, где вызывается UseAuthentication (если она присутствует):

app.UseAuthentication(); // Only present if not called internally
app.UseAuthorization();

В приложениях Blazor Server (не Blazor Web App), вызовите UseAuthorization в файле Program после строки, которая вызывает UseAuthentication (если присутствует):

app.UseAuthentication(); // Only present if not called internally
app.UseAuthorization();

В приложениях Blazor Server (не в Blazor Web App), вызов UseAuthorization в Startup.Configure (Startup.cs) должен быть после строки, которая вызывает UseAuthentication (если присутствует):

app.UseAuthentication(); // Only present if not called internally
app.UseAuthorization();

Blazor WebAssembly приложения вызывают AddAuthorizationCore в Program файле, чтобы добавить службы авторизации:

builder.Services.AddAuthorizationCore();

Проверки авторизации на основе ролей

Проверки авторизации на основе ролей:

Компонент AuthorizeView (AuthorizeView компонент в Blazor документации) поддерживает авторизациюна основе ролей . В этом разделе рассматриваются основные понятия. Полный охват см. в разделе ASP.NET Core Blazor аутентификации и авторизации.

Для авторизации на основе ролей содержимого в Razor компонентах используйте AuthorizeView.Roles параметр.

В следующем примере :

  • Пользователь должен иметь утверждение роли для любой из ролей Admin или SuperUser, чтобы просмотреть содержимое первого AuthorizeView компонента.
  • Чтобы требовать как Admin, так и SuperUser утверждения ролей, во втором примере вложены компоненты AuthorizeView.

Pages/RoleChecksWithAuthorizeView.razor:

@page "/role-checks-with-authorizeview"

<h3>Role Checks with AuthorizeView</h3>

<AuthorizeView Roles="Admin, SuperUser">
    <p>User: @context.User.Identity?.Name</p>
    <p>You have an 'Admin' or 'SuperUser' role claim.</p>
</AuthorizeView>

<AuthorizeView Roles="Admin">
    <p>User: @context.User.Identity?.Name</p>
    <p>You have the 'Admin' role claim.</p>
    <AuthorizeView Roles="SuperUser" Context="innerContext">
        <p>User: @innerContext.User.Identity?.Name</p>
        <p>You have both 'Admin' and 'SuperUser' role claims.</p>
    </AuthorizeView>
</AuthorizeView>

Предыдущий код устанавливает Context для внутреннего AuthorizeView компонента, чтобы предотвратить конфликт контекста AuthenticationState . Контекст AuthenticationState доступен во внешнем AuthorizeView, используя стандартный подход для доступа к контексту (@context.User). Доступ к контексту осуществляется внутри AuthorizeView с контекстом, который носит имя innerContext (@innerContext.User).

Атрибут [Authorize] поддерживает авторизацию на основе ролей для всех Razor компонентов. Используйте параметр AuthorizeAttribute.Roles. Следующий код ограничивает доступ к компонентам пользователям, которые являются членом Admin роли.

Pages/RequireAdminRoleWithAuthorizeAttribute.razor:

@page "/require-admin-role-with-authorize-attribute"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "Admin")]

<h1>Require 'Admin' role with [Authorize] attribute</h1>

<p>You can only see this if you're in the 'Admin' role.</p>

Несколько ролей можно указать как разделенный запятыми список. В следующем примере доступ ограничен пользователям, которые являются членами роли Admin, или роли SuperUser.

Pages/RequireAdminOrSuperUserRoleWithAuthorizeAttribute.razor:

@page "/require-admin-or-superuser-role-with-authorize-attribute"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "Admin, SuperUser")]

<h1>Require 'Admin' or 'SuperUser' role with [Authorize] attribute</h1>

<p>
    You can only see this if you're in the 'Admin' role or the 'SuperUser' role.
</p>

При применении нескольких атрибутов пользователь должен быть членом всех указанных ролей. В следующем примере требуются обеAdmin ролииSuperUser роли.

Pages/RequireAdminAndSuperUserRolesWithAuthorizeAttributes.razor:

@page "/require-admin-and-superuser-roles-with-authorize-attributes"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "Admin")]
@attribute [Authorize(Roles = "SuperUser")]

<h1>Require 'Admin' and 'SuperUser' roles with [Authorize] attributes</h1>

<p>
    You can only see this if you're in both the 'Admin' role 
    and the 'SuperUser' role.
</p>

Сопоставление ролей обычно чувствительно к регистру, так как имена ролей хранятся и сравниваются с использованием строковых сравнений .NET. Например, Admin(верхний регистр) не рассматривается как та же роль, что и A(строчная admin). Дополнительные сведения см. в разделе Авторизация на основе заявок в ASP.NET Core.

Проверки авторизации на основе политик

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

В следующем примере :

  • Политика RequireAdminRole указывает, что пользователи должны находиться в Admin роли.
  • Политика RequireSuperUserRole указывает, что пользователи должны находиться в SuperUser роли.
builder.Services.AddAuthorizationBuilder()
    .AddPolicy("RequireAdminRole",
         policy => policy.RequireRole("Admin"))
    .AddPolicy("RequireSuperUserRole",
         policy => policy.RequireRole("SuperUser"));
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("RequireAdminRole",
        policy => policy.RequireRole("Admin"));
    options.AddPolicy("RequireSuperUserRole",
        policy => policy.RequireRole("SuperUser"));
});
services.AddAuthorization(options =>
{
    options.AddPolicy("RequireAdminRole",
        policy => policy.RequireRole("Admin"));
    options.AddPolicy("RequireSuperUserRole",
        policy => policy.RequireRole("SuperUser"));
});

Для авторизации на основе политик с помощью AuthorizeView компонента используйте AuthorizeView.Policy параметр с одним именем политики.

Pages/PassRequireAdminRolePolicy.razor:

@page "/pass-requireadminrole-policy-with-authorizeview"

<h1>Pass 'RequireAdminRole' policy with AuthorizeView</h1>

<AuthorizeView Policy="RequireAdminRole">
    <p>You satisfy the 'RequireAdminRole' policy.</p>
</AuthorizeView>

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

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

  • Создайте политику для AuthorizeView, которая подтверждает, что пользователь удовлетворяет нескольким другим политикам.

  • Вложите политики в несколько AuthorizeView компонентов.

    Pages/PassRequireAdminRoleAndRequireSuperUserRolePoliciesWithAuthorizeViews.razor:

    @page "/pass-requireadminrole-and-requiresuperuserrole-policies-with-authorizeviews"
    
    <h1>
        Pass 'RequireAdminRole' and 'RequireSuperUserRole' policies with AuthorizeViews
    </h1>
    
    <AuthorizeView Policy="RequireAdminRole">
        <AuthorizeView Policy="RequireSuperUserRole" Context="innerContext">
            <p>
                You satisfy the 'RequireAdminRole' and 
                'RequireSuperUserRole' policies.
            </p>
        </AuthorizeView>
    </AuthorizeView>
    

Если заданы и Roles, и Policy, авторизация выполняется только в случае выполнения обоих условий. То есть пользователь должен принадлежать по крайней мере одной из указанных ролей и соответствовать требованиям, определенным политикой.

Если ни Roles, ни Policy не указаны, AuthorizeView используется стандартная политика:

  • Пользователи, прошедшие проверку подлинности, авторизованы.
  • Неавторизованные (вышедшие из системы) пользователи не имеют разрешения.

В отличие от сопоставления ролей, которое обычно учитывает регистр, в ASP.NET Core поиск названия политики обычно не учитывает регистр, поэтому RequireAdminRole и requireadminrole ссылаются на ту же политику.

Политики применяются ко всему Razor компоненту с использованием свойства Policy на атрибуте [Authorize].

Pages/PassRequireAdminRolePolicyWithAuthorizeAttribute.razor:

@page "/pass-requireadminrole-policy-with-authorize-attribute"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Policy = "RequireAdminRole")]

<h1>Pass RequireAdminRole policy with [Authorize] attribute</h1>

<p>You can only see this if the 'RequireAdminRole' policy is satisfied.</p>

Чтобы указать несколько разрешенных ролей в требовании, укажите роли в качестве параметров метода RequireRole . В следующем примере пользователи авторизованы, если они принадлежат к ролям AdminилиSuperUser.

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("ElevatedRights", policy =>
        policy.RequireRole("Admin", "SuperUser"));
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("ElevatedRights", policy =>
        policy.RequireRole("Admin", "SuperUser"));
});
services.AddAuthorization(options =>
{
    options.AddPolicy("ElevatedRights", policy =>
        policy.RequireRole("Admin", "SuperUser"));
});

Если вы хотите, чтобы политика требовала всех предыдущих ролей, свяжите роли с построителем политик или укажите их в построителе политик отдельно в лямбда-выражении.

Привязаны к построителю политик:

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("ElevatedRights", policy => 
        policy
            .RequireRole("Admin")
            .RequireRole("SuperUser"));

Или используйте лямбда-выражение:

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("ElevatedRights",
        policy =>
        {
            policy.RequireRole("Admin");
            policy.RequireRole("SuperUser");
        });
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("ElevatedRights", policy => 
        policy
            .RequireRole("Admin")
            .RequireRole("SuperUser"));
});

Или используйте лямбда-выражение:

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("ElevatedRights",
        policy =>
        {
            policy.RequireRole("Admin");
            policy.RequireRole("SuperUser");
        });
});
services.AddAuthorization(options =>
{
    options.AddPolicy("ElevatedRights", policy => 
        policy
            .RequireRole("Admin")
            .RequireRole("SuperUser"));
});

Или используйте лямбда-выражение:

services.AddAuthorization(options =>
{
    options.AddPolicy("ElevatedRights",
        policy =>
        {
            policy.RequireRole("Admin");
            policy.RequireRole("SuperUser");
        });
});

Группы безопасности Windows-аутентификации в качестве ролей приложения

После настройки приложения для проверки подлинности Windows (Blazor-специфические рекомендации), когда клиентские и серверные машины являются частью одного и того же домена Windows, группы безопасности пользователей автоматически включаются в качестве клеймов в ClaimsPrincipal пользователя.

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

User.Identity обычно является WindowsIdentity при использовании аутентификации Windows, и вы можете получить данные о группах SID или проверить, находится ли пользователь в роли, используя следующий код, где заполнитель {DOMAIN} — это домен, а {SID GROUP NAME} — это имя группы SID.

if (User.Identity is WindowsIdentity windowsIdentity)
{
    var groups = windowsIdentity.Groups;

    // If needed, obtain a list of the SID groups
    var securityGroups = 
        groups.Select(g => g.Translate(typeof(NTAccount)).ToString()).ToList();

    // If needed, obtain the user's Windows identity name
    var windowsIdentityName = windowsIdentity.Name;

    // Check if the user is in a specific SID group
    if (User.IsInRole(@"{DOMAIN}\{SID GROUP NAME}"))
    {
        // User is in the specified group
    }
    else
    {
        // User isn't in the specified group
    }
}
else
{
    // The user isn't authenticated with Windows Authentication
}

Демонстрацию связанного кода, который преобразует утверждения SID группы в удобочитаемые значения в приложении Blazor, см. компонент UserClaims в Secure ASP.NET Core Blazor Web App с проверкой подлинности Windows. Такой подход к получению утверждений групп SID можно объединить с добавлением утверждений для создания настраиваемых утверждений роли IClaimsTransformation при проверке подлинности пользователя.

Подход, аналогичный предыдущему примеру для получения утверждений групп SID, можно объединить с добавлением утверждений с IClaimsTransformation для создания настраиваемых утверждений роли при аутентификации пользователя.

Дополнительные ресурсы