Blazor Server Authorization

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.

Привіт!

Сьогодні подивимось, як додати авторизацію в Blazor Server. Для початку створимо новий проект, в якому включемо автентифікацію.

В проекті з автентифікацією присутні декілька додаткових файлів, які відповідають за автентифікацію. Також в конфігураційному файлі буде додано connection string до бази даних.

  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-BlazorAuth-BD4B8646-B1A3-438C-8FFC-8C3A29438C24;Trusted_Connection=True;MultipleActiveResultSets=true"
  },

В package management консолі потрібно запустити команду Update-Database для того, щоб створити базу даних та потрібні таблиці.

Тепер можна запустити аплікацію і спробувати залогуватись.

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

Autorization/h2>

Підтримку ролей потрібно добавити в Startup.cs в методі ConfigureService.

            services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
                .AddRoles<IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>();

Generate Roles

Додам новий razor-компонент в папку Pages. При переході на цю сторінку будуть створюватись базові ролі, вона буде доступна за шляхом /addroles. Також потрібно за допомогою DI взяти декілька об’єктів.

@page "/addroles"
@using Microsoft.AspNetCore.Identity

@inject RoleManager<IdentityRole> RoleManager
@inject UserManager<IdentityUser> UserManager

Створювати ролі буду за допомогою RoleManager. Для початку створю список, в якому будуть всі потрібні ролі. Далі іду циклом по усіх ролях, якщо роль існує, то нічого не відбудеться. Якщо ж ні, то нова роль буде створена.

    private async Task GenerateRoles()
    {
        const string adminRole = "Administrator";
        string[] roles = { AdminRole };

        foreach (var role in roles)
        {
            var roleExist = await RoleManager.RoleExistsAsync(role);

            if (roleExist == false)
            {
                await RoleManager.CreateAsync(new IdentityRole(role));
            }
        }
    }

Create admin

Наступним кроком потрібно додати користувачів до ролей. В appsettings.json я додам параметр, який буде містити email дефолтного адміна.

"AdminUser": "777rip777@gmail.com"

Повернусь назад до addroles сторінки і створю новий метод. Перевіряю, чи користувач з емейлом з конфігураційного файлу існує, якщо так, то додаю його в групу Administrator. Для того, щоб можна було прочитати дані з конфігураційного файлу, потрібо отримати об’єкт типу IConfiguration. Вверху сторінки добавити @using Microsoft.Extensions.Configuration i @inject IConfiguration config.

    private async Task AddAdmin()
    {
        var user = await UserManager.FindByEmailAsync(config.GetValue<string>("AdminUser"));

        if (user != null)
        {
            await UserManager.AddToRoleAsync(user, "Administrator");
        }
    }

Для того, щоб ці методи викликались при відкритті сторінки, потрібно викликати їх в методі OnParametersSetAsync.

Повний код addroles сторінки

@page "/addroles"
@using Microsoft.AspNetCore.Identity
@using Microsoft.Extensions.Configuration

@inject RoleManager<IdentityRole> RoleManager
@inject UserManager<IdentityUser> UserManager
@inject IConfiguration config

@code {

    protected override async Task OnParametersSetAsync()
    {
        await GenerateRoles();
        await AddAdmin();
    }

    private async Task GenerateRoles()
    {
        const string adminRole = "Administrator";
        string[] roles = { adminRole };

        foreach (var role in roles)
        {
            var roleExist = await RoleManager.RoleExistsAsync(role);

            if (roleExist == false)
            {
                await RoleManager.CreateAsync(new IdentityRole(role));
            }
        }
    }

    private async Task AddAdmin()
    {
        var user = await UserManager.FindByEmailAsync(config.GetValue<string>("AdminUser"));

        if (user != null)
        {
            await UserManager.AddToRoleAsync(user, "Administrator");
        }
    }
}

Тепер, відкривши в браузері сторінку /addroles, роль буде створена та користувач (якщо існує) буде доданий в неї.

Restrict access to pages

Тепер коли в нас є ролі, можна обмежити доступ до сторінок. Для сторінки FetchData я додам @attribute [Authorize]. Це дасть доступ до сторінки тільки авторизованим користувачам. А для сторінки Counter я вкажу @attribute [Authorize(Roles = "Administrator")] це дасть доступ до сторінки тільки користувачам з ролю адміністратор.

Замінити Not Autorized помилку можна в App.razor в NotAutorized секції.

<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(Program).Assembly">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
                <NotAuthorized>
                    <h1>You don't have access</h1>
                    <p>Ask your administrator to give you permission</p>
                </NotAuthorized>
            </AuthorizeRouteView> />
        </Found>
        <NotFound>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState>

Також можна повністю сховати елемент сайту для незареєстрованих користувачів. В NavMenu.razor, я додам елементи меню в <AuthorizeView></AuthorizeView>.

<div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">BlazorAuth</a>
    <button class="navbar-toggler" @onclick="ToggleNavMenu">
        <span class="navbar-toggler-icon"></span>
    </button>
</div>

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </li>
        <AuthorizeView Roles="Administrator">
            <li class="nav-item px-3">
                <NavLink class="nav-link" href="counter">
                    <span class="oi oi-plus" aria-hidden="true"></span> Counter
                </NavLink>
            </li>
        </AuthorizeView>
        <AuthorizeView>
            <li class="nav-item px-3">
                <NavLink class="nav-link" href="fetchdata">
                    <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
                </NavLink>
            </li>
        </AuthorizeView>
    </ul>
</div>

@code {
    private bool collapseNavMenu = true;

    private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;

    private void ToggleNavMenu()
    {
        collapseNavMenu = !collapseNavMenu;
    }
}

Тепер counter в меню будуть бачити тільки користувачі з ролю Administrator, а fetchdata буде доступна зареєстрованим користувачам.

В index сторінку додам повідомлення користувачам, щоб залогувались.

@page "/"

<AuthorizeView>
    <Authorized>
        <h1>Thank you for logging in</h1>
    </Authorized>
    <NotAuthorized>
        <h3 class="text-danger">Log in please</h3>
    </NotAuthorized>
</AuthorizeView>

<AuthorizeView Roles="Administrator">
    <Authorized>
        Welcome back Admin
    </Authorized>
</AuthorizeView>

Якщо користувача не залоговний, буде повідомлення Log in please. В інакшому випадку Thank you for logging in. А якщо користувач admin, то додатково буде повідомлення Welcome back Admin.

👍НравитсяПонравилось0
В избранноеВ избранном1
LinkedIn
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Коли відправляєш статтю в редакцію через email то редактори зберігають картинки на сервері DOU і вставляють через img

Або коли самостійно створюєш тему то є підказка:

Для додавання картинок використовуйте imgur.com. Приклад коду для вставки картинки: <img src="https://i.imgur.com/tgK21CF.png">

Жаль майки не успели сделать компилятор AoT для клиент версии blazor, из за этого плохая производительность. Но есть серверная реализация, у которой узкое горлышко это сеть и задержки ответа(если проект локальный, то это наверное не проблема)

Подписаться на комментарии