Blazor Server için ProtectedBrowserStorage

12/2021

Genel Bakış: ASP.NET Core Blazor State Yönetimi

Persist state across circuits

Where to persist state

  • Server-side storage
  • URL
  • Browser storage
  • In-memory state container service

Başlarken

Yol Haritası

  • Proje oluştur
    • Kullanıcı sınıfı oluştur
    • Giriş sayfası oluştur
    • SessionStorage/LocalStorage kurulumu
      • SessionStorage
      • LocalStorage
      • Storage Servis Aktivasyonu
      • Blazor App Host modelinin düzenlenmesi
  • Özel Authentication (CustomAuthenticationStateProvider) yapısının kodlanması
    • 1. Adım: Startup.cs / Program.cs değişiklikleri
    • 2. Adım: AuthenticationStateProvider’dan Miras Alma
    • 3. Adım: App.razor değişiklikleri
  • Arayüzün hazırlanması

Proje Oluştur

Şimdi yeni bir ASP.NET Core Web Application - Blazor Server/WebAssembly App - .NET 6 projesi oluşturalım.

Kullanıcı sınıfı oluştur

User modeli içerisinde Username ve Password alanlarını tutalım.

    public class User
    {
        public string Username { get; set; }

        public string Password { get; set; }
    }

Giriş sayfası oluştur

Kullanıcı girişi için Login.razor sayfasını oluşturalım.

    <EditForm Model="@user" OnValidSubmit="DoLogin">
    <div class="mb-3">
            <label for="exampleInputEmail1" class="form-label">Email address</label>
            <input type="email" class="form-control" id="exampleInputEmail1"  @bind="user.Username">
            <div id="emailHelp" class="form-text">We'll never share your email with anyone else.</div>
        </div>
        <div class="mb-3">
            <label for="exampleInputPassword1" class="form-label">Password</label>
            <input type="password" class="form-control" id="exampleInputPassword1" @bind="user.Password">
        </div>
        <button type="submit" class="btn btn-primary">Login</button>
    </EditForm>

    @code {

        private User user;

        protected override Task OnInitializedAsync()
        {
            user = new User();
            return base.OnInitializedAsync();
        }

        private async Task<bool> DoLogin()
        {
            // Call API
            return await Task.FromResult(true);
        }

    }

SessionStorage/LocalStorage kurulumu

SessionStorage

Line-of-business uygulamalarında en yaygın kullanılan kullanıcı oturum yaklaşımı session üzerinde kullanıcı bilgilerini tutmaktır. Bununla ilgili birçok yaklaşım ve çözüm bulunmaktadır. Bu örnekte Blazored imzalı SessionStorage paketiyle, kimlik bilgisi doğrulanan kullanıcının kısıtlı verisini oturum süresi boyunca tutarak kullanacağız.

  • Kurulumu gerçekleştirmek için NuGet Package Manager içerisinde Blazored.SessionStorage yazarak arama yapıp en güncel versiyonu indirebilirsiniz.
  • Veya .NET CLI üzerinden dotnet add package Blazored.SessionStorage komutu ile kurulumu yapabilirsiniz.

LocalStorage

Kullanıcıları sabit, sınırları belirli, intranet, veya vpn ile erişim sağlanabilen bir uygulama geliştiriyorsanız SessionStorage yerine LocalStorage paketini tercih edebilirsiniz.

Makalenin diğer tüm bölümlerinde Session, session, _session anahtar kelimelerini Local, local, _local olarak değiştirmeniz yeterli olacaktır.

Örnek: SessionStorage sessionStorage yerine LocalStorage localStorage olacak.

Storage Servis Aktivasyonu

Şimdi Startup.cs içerisindeki ConfigureServices metoduna veya Program.cs dosyasına gelerek servisimizi projemize dahil edelim.

Startup.cs için:

    services.AddBlazoredSessionStorage();

Program.cs için:

    builder.Services.AddBlazoredSessionStorage();

Blazor App Host modelinin düzenlenmesi

Uygulamanızda SessionStorage, LocalStorage gibi JavaScript aracılığı ile uygulama içeriğinde kullanacağınız bileşenleri, değişkenleri ve verileri yöneteceğiniz kütüphaneleri kullanırken uygulamanızın tamamen derlenmiş olması gerekiyor. Daha fazlası için tıklayın.

Projenizde Pages dizini altında yer alan _Host.cshtml sayfasını açarak uygulamanızın render-mode özelliğini Server olarak değiştirmelisiniz.

<component type="typeof(App)" render-mode="Server"/>

Özel Authentication (CustomAuthenticationStateProvider) Oluşturma

ASP.NET Core AuthenticationMiddleware yapısını projemize dahil etmek için yapmamız gerekenleri 3 adımda ele alalım.

1. Adım: Startup.cs / Program.cs değişiklikleri

Authentication Middleware yapısını projemize dahil ederek Authentication yapımızı inşa edebiliriz.

Startup.cs ve Program.cs için:

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

2. Adım: AuthenticationStateProvider’dan Miras Alma

AuthenticationStateProvider sınıfını miras alarak yeni bir StateProvider sınıfı oluşturalım (CustomAuthenticationStateProvider).

    using System.Security.Claims;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Components.Authorization;

    public class CustomAuthenticationStateProvider : AuthenticationStateProvider
    {
       
    }

ISessionStorageService arayüzünü sınıfımıza dahil edelim.

        private ISessionStorageService _sessionStorageService;

        public CustomAuthenticationStateProvider(ISessionStorageService sessionStorageService)
        {
            _sessionStorageService = sessionStorageService;
        }

AuthenticationStateProvider’ın GetAuthenticationStateAsync metodunu özelleştirelim.

        public override async Task<AuthenticationState> GetAuthenticationStateAsync()
        {
            var email = await _sessionStorageService.GetItemAsStringAsync("email");
            ClaimsIdentity identity = email != null
                ? new ClaimsIdentity(new[] {new Claim(ClaimTypes.Name, email)}, "basic_user")
                : new ClaimsIdentity();
            var user = new ClaimsPrincipal(identity);
            return await Task.FromResult(new AuthenticationState(user));
        }

Başarılı giriş yapılması durumunda kimliğinin doğrulanmasını sağlayalım.

        public void MarkAsAuthenticated(string email)
        {
            var identity = new ClaimsIdentity(new[] {new Claim(ClaimTypes.Name, email)}, "basic_user");
            var user = new ClaimsPrincipal(identity);
            NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(user)));
        }

Güvenli çıkış yapılması durumunda kimliğinin silinmesini sağlayalım.

        public void MarkAsLoggedOut()
        {
            _sessionStorageService.RemoveItemAsync("email");
            NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()))));
        }

Servis Aktivasyonu

Şimdi Startup.cs içerisindeki ConfigureServices metoduna veya Program.cs dosyasına gelerek servisimizi projemize dahil edelim.

Startup.cs için:

    services.AddScoped<AuthenticationStateProvider, CustomAuthenticationStateProvider>();

Program.cs için:

    builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthenticationStateProvider>();

3. Adım: App.razor değişiklikleri

Uygulamanın izleyeceği rotayı yöneten RouteView ile kimlik doğrulama sonucu erişim sağlanan AuthorizeView yapılarının birleşimi olan AuthorizeRouteView, uygulamamızın varsayılan görünümünü oluşturmalı.

Task<AuthenticationState> kullanımı için CascadingAuthenticationState‘in App komponentinde yer alması gerekiyor. Daha fazlası için tıklayın.

<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(App).Assembly">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
            <FocusOnNavigate RouteData="@routeData" Selector="h1"/>
        </Found>
        <NotFound>
                <PageTitle>Not found</PageTitle>
                <LayoutView Layout="@typeof(MainLayout)">
                    <p role="alert">Sorry, there's nothing at this address.</p>
                </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState>    

Arayüzün hazırlanması

AuthorizeView Komponenti

Authentication yapısında arayüzlerimizde kullanabileceğimiz AuthorizeView komponenti bulunuyor. Bu komponent içerisine 3 farklı etiket alabiliyor.

<AuthorizeView>
    <Authorized>
        Kullanıcı kimliği doğrulandığında gösterilecek alan.
    </Authorized>
    <NotAuthorized>
        Kullanıcı kimliği doğrulanmadığında gösterilecek alan.
    </NotAuthorized>
    <Authorizing>
        Asenkron olarak kimlik doğrulanırken gösterilecek alan.
    </Authorizing>
</AuthorizeView>

Herkesin görüntüleyebileceği fakat sadece giriş yapmış kullanıcıların özel olarak görüntüleyebileceği bir içerik varsa, bunu sayfa içerisinde AuthorizeView ve alt etiketlerini kullanarak sağlayabiliriz.

<AuthorizeView>
    <Authorized>
        <h1>Merhaba, @context.User.Identity.Name!</h1>
        <p>Bu içeriği sadece giriş yaptıysanız görüntüleyebilirsiniz.</p>
        <button @onclick="SecureMethod">Sadece Yetkili İşlem Butonu</button>
    </Authorized>
    <NotAuthorized>
        <h1>Yetki Hatası!</h1>
        <p>Giriş yapmadınız.</p>
    </NotAuthorized>
</AuthorizeView>

@code {
    private void SecureMethod() { ... }
}

Authorize Attribute

[Authorize] attribute’ü eklenen sayfalar, sadece kimliği doğrulanan kişilerin görüntüleyebileceği sayfalardır.

Sadece giriş yapan kullanıcıların görüntülemesi

@page "/"
@attribute [Authorize]

Bu sayfayı sadece giriş yaptıysanız görüntüleyebilirsiniz.

Rol özelinde erişimi kısıtlı sayfaların görüntülenmesi

@page "/"
@attribute [Authorize(Roles = "admin, superuser")]

Bu sayfayı sadece admin veya superuser rolüne sahipseniz görüntüleyebilirsiniz.

İlke özelinde erişimi kısıtlı sayfaların görüntülenmesi

@page "/"
@attribute [Authorize(Policy = "content-editor")]

Bu sayfayı sadece content-editor ilkesine sahipseniz görüntüleyebilirsiniz.

Yetkisizlerin görüntüleyeceği içeriğin Router komponentinde düzenlenmesi

<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(Program).Assembly">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="@routeData" 
                DefaultLayout="@typeof(MainLayout)">
                <NotAuthorized>
                    <h1>Üzgünüm</h1>
                    <p>Bu sayfaya erişmeye yetkiniz yoktur.</p>
                    <p>Farklı bir kullanıcı olarak oturum açmanız gerekebilir.</p>
                </NotAuthorized>
                <Authorizing>
                    <h1>Yetkilendirme devam ediyor.</h1>
                    <p>Bu içerik sadece yetkilendirme devam ederken görüntülenir.</p>
                </Authorizing>
            </AuthorizeRouteView>
        </Found>
        <NotFound>
            <LayoutView Layout="@typeof(MainLayout)">
                <h1>Üzgünüm</h1>
                <p>Bu adreste bir içerik bulunamadı.</p>
            </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState>

ABDULLAH

I am a Software Craftsman at the Company, a Developer MVP at Embarcadero,
and a Jedi Daddy for Zeybie.

SAYFA

Bu sayfa programlama, insan faktörü ve ara sıra deneme yazıları içerir. Ancak yazılar bu sırayla yer almaz.

Abdullah Ilgaz © MMXXI by Abdullah Ilgaz.
İçerik, aksi belirtilmedikçe Creative Commons (BY-NC-SA) kapsamındadır.