カスタムユーザーとの


イントロ


今回、私はBlazor(サーバー)アプリケーションから署名してみます.

  • 環境

  • .ネットコアver .5.0.102
  • サンプル

  • ApproveWorkflowSample
  • ブレザーサーバに関する私のポスト




  • サイン


    signinmanagerでの署名(失敗)


    BlazorがDIを使うことができるので、私は最初にsigninmanagerに署名しようとしました.

    ApplicationUserService。cs


    ...
        public async Task<bool> SignInAsync(string email, string password)
        {
            var target = await applicationUsers.GetByEmailAsync(email);
            if (target == null)
            {
                return false;
            }
            var result = await signInManager.PasswordSignInAsync(target, password, false, false);
            return result.Succeeded;
        }
    ...
    

    signin剃刀


    @page "/Pages/SignIn"
    <div id="background">
        <div id="sign_in_frame">
            <h1>Sign In</h1>
            <div class="sign_in_input_container">
                <input type="text" @bind="Email" class="sign_in_input @AdditionalClassName">
            </div>
            <div class="sign_in_input_container">
                <input type="password" @bind="Password" class="sign_in_input @AdditionalClassName">
            </div>
            <div id="sign_in_controller_container">
                <button @onclick="StartSigningIn">Sign In</button>
            </div>
        </div>
    </div>
    

    signinかみそり。cs


    using System;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Components;
    using Microsoft.JSInterop;
    using ApprovementWorkflowSample.Applications;
    
    namespace ApprovementWorkflowSample.Views
    {
        public partial class SignIn
        {
            [Inject]
            public IJSRuntime? JSRuntime { get; init; }
            [Inject]
            public NavigationManager? Navigation { get; init; }
            [Inject]
            public IApplicationUserService? ApplicationUsers{get; init; }
    
            [Parameter]
            public string Email { get; set; } = "";
            [Parameter]
            public string Password { get; set; } = "";
            [Parameter]
            public string AdditionalClassName { get; set; } = "";
    
            public async Task StartSigningIn()
            {
                if(string.IsNullOrEmpty(Email) ||
                    string.IsNullOrEmpty(Password))
                {
                    await HandleSigningInFailedAsync("Email and Password are required");
                    return;
                }
                var result = await ApplicationUsers!.SignInAsync(Email, Password);
                if(result)
                {
                    Console.WriteLine("Navi");
                    Navigation!.NavigateTo("/Pages/Edit");
                    return;
                }
                AdditionalClassName = "login_failed";
                await JSRuntime!.InvokeAsync<object>("Page.showAlert","Email or Password are not match");
            }
            private async Task HandleSigningInFailedAsync(string errorMessage)
            {
                AdditionalClassName = "login_failed";
                await JSRuntime!.InvokeAsync<object>("Page.showAlert", errorMessage);   
            }
        }
    }
    
    私は"signinasync "を呼び出して例外を得ました.
    Unhandled exception rendering component: Headers are read-only, response has already started. System.InvalidOperationException: Headers are read-only, 
    response has already started.
       at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ThrowHeadersReadOnlyException()
    ...
    

    コントローラからの署名(失敗)


    それで、私はJavaScriptコードからサインインと同じようにコントローラからサインをしてみました.

    ユーザーコントローラ。cs


    ...
            [HttpPost]
            [Route("Users/SignIn")]
            public async ValueTask<bool> SignIn([FromBody]SignInValue value)
            {
                if(string.IsNullOrEmpty(value.Email) ||
                    string.IsNullOrEmpty(value.Password))
                {
                    return false;
                }
                return await users.SignInAsync(value.Email, value.Password);
            }
    ...
    

    signinvalue。cs


    namespace ApprovementWorkflowSample.Applications.Dto
    {
        public record SignInValue(string Email, string Password);
    }
    

    signinかみそり。cs


    using System.IO;
    using System.Text;
    using System.Net.Http;
    using System.Threading.Tasks;
    using ApprovementWorkflowSample.Applications.Dto;
    using Microsoft.AspNetCore.Components;
    using Microsoft.JSInterop;
    using Microsoft.Extensions.Configuration;
    using ApprovementWorkflowSample.Applications;
    using Newtonsoft.Json;
    
    namespace ApprovementWorkflowSample.Views
    {
        public partial class SignIn
        {
            [Inject]
            public IJSRuntime? JSRuntime { get; init; }
            [Inject]
            public IHttpClientFactory? HttpClients { get; init; }
            [Inject]
            public IConfiguration? Configuration { get; init; }
            [Inject]
            public NavigationManager? Navigation { get; init; }     
    ...
            public async Task StartSigningIn()
            {
    ...
                var httpClient = HttpClients.CreateClient();
                var signInValue = new SignInValue(Email, Password);
                var context = new StringContent(JsonConvert.SerializeObject(signInValue), Encoding.UTF8, "application/json");
                var response = await httpClient.PostAsync(Path.Combine(Configuration!["BaseUrl"], "Users/SignIn"), context);
                if(response.IsSuccessStatusCode == false)
                {
                    await HandleSigningInFailedAsync("Failed access");
                    return;
                }
                string resultText = await response.Content.ReadAsStringAsync();
                bool.TryParse(resultText, out var result);
                if(result)
                {
                    Navigation!.NavigateTo("/Pages/Edit");
                    return;
                }
                AdditionalClassName = "login_failed";
                await HandleSigningInFailedAsync("Email or Password are not match");
            }
    ...
    
    私はどんな例外も得ませんでした、そして、私は結果として「真実」を得ることができました.
    しかし、ステータスは“認証”として扱われませんでした.

    エディットワークフロー.剃刀


    @page "/Pages/Edit"
    @attribute [Authorize]
    
    <CascadingAuthenticationState>
        <AuthorizeView>
            <Authorized>
                <h1>Hello, @context.User.Identity!.Name!</h1>
                <p>You can only see this content if you're authorized.</p>
            </Authorized>
            <NotAuthorized>
                <h1>Authentication Failure!</h1>
                <p>You're not signed in.</p>
            </NotAuthorized>
        </AuthorizeView>
    </CascadingAuthenticationState>
    
    認証された後に、このページは「noauthorizedされた」要素をまだ示しました.
    HTTP接続を通して認証されたので.しかし、サーバーサーバアプリケーションはsignalrを使用します.
    それで、認証に関する情報を得ることができませんでした.
  • c# - SignIn for Blazor Server-Side app not working - Stack Overflow
  • Regulsprincity , Requestentity , AuthenticationStatus ( OK )を使用する


    スタックオーバーフローの記事によると、"iHostEnvironmentThenticationStateProvider "を追加し、"signin . razor . cs "を変更しました.

    起動。cs


    ...
            public void ConfigureServices(IServiceCollection services)
            {
    ...
                services.AddScoped<IHostEnvironmentAuthenticationStateProvider>(sp =>
                    (ServerAuthenticationStateProvider) sp.GetRequiredService<AuthenticationStateProvider>()
                );
    ...
            }
    ...
    

    signinかみそり。cs


    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Components;
    using Microsoft.JSInterop;
    using ApprovementWorkflowSample.Applications;
    using Microsoft.AspNetCore.Identity;
    using System.Security.Claims;
    using Microsoft.AspNetCore.Components.Authorization;
    using Microsoft.AspNetCore.Authentication.Cookies;
    
    namespace ApprovementWorkflowSample.Views
    {
        public partial class SignIn
        {
            [Inject]
            public IJSRuntime? JSRuntime { get; init; }
            [Inject]
            public NavigationManager? Navigation { get; init; }
            [Inject]
            public IApplicationUserService? ApplicationUsers{get; init; }
            [Inject]
            public SignInManager<ApplicationUser>? SignInManager { get; init; }
            [Inject]
            public IHostEnvironmentAuthenticationStateProvider? HostAuthentication { get; init; }
            [Inject]
            public AuthenticationStateProvider? AuthenticationStateProvider{get; init; }
    ...
            public async Task StartSigningIn()
            {
    ...
                ApplicationUser? user = await ApplicationUsers!.GetUserByEmailAsync(Email);
                if(user == null)
                {
                    await HandleSigningInFailedAsync("Email or Password are not match");
                    return;
                }
                SignInResult loginResult = await SignInManager!.CheckPasswordSignInAsync(user, Password, false);
                if(loginResult.Succeeded == false)
                {
                    await HandleSigningInFailedAsync("Email or Password are not match");
                    return;
                }
                if(loginResult.Succeeded)
                {
                    ClaimsPrincipal principal = await SignInManager.CreateUserPrincipalAsync(user);
                    SignInManager.Context.User = principal;
                    HostAuthentication!.SetAuthenticationState(
                        Task.FromResult(new AuthenticationState(principal)));
    
                    // If you don't need doing anything without moving to next page, you can remove this.
                    AuthenticationState authState = await AuthenticationStateProvider!.GetAuthenticationStateAsync();
    
                    Navigation!.NavigateTo("/Pages/Edit");
                }
            }
    ...
    
    最後に、私はサインすることができました、そして、ステータスは「認証された」として扱われました.

    非認証ユーザーの自動リダイレクト


    ASP .NETコアMVCは、ユーザーが認証されていないときに自動的にリダイレクトできます.
    ブレザーはどう?
    今回はこのポストに続きました.
  • Blazor - Redirect non-authenticated user to login page
  • アプリ。剃刀


    @using Shared
    <CascadingAuthenticationState>
    <Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
                <NotAuthorized>
                    <RedirectToSignIn></RedirectToSignIn>
                </NotAuthorized>
            </AuthorizeRouteView>
        </Found>
        <NotFound>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
    </CascadingAuthenticationState>
    

    redirecttosignin。かみそり。cs


    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Components;
    using Microsoft.AspNetCore.Components.Authorization;
    
    namespace ApprovementWorkflowSample.Views
    {
        public partial class RedirectToSignIn
        {
            [CascadingParameter]
            private Task<AuthenticationState>? AuthenticationStateTask { get; init; }
            [Inject]
            public NavigationManager? Navigation { get; init; }
            protected override async Task OnInitializedAsync()
            {
                var authenticationState = await AuthenticationStateTask!;
    
                if (authenticationState?.User?.Identity is null || !authenticationState.User.Identity.IsAuthenticated)
                {
                    var returnUrl = Navigation!.ToBaseRelativePath(Navigation.Uri);
                    if (string.IsNullOrWhiteSpace(returnUrl))
                    {
                        Navigation.NavigateTo("Pages/SignIn", true);
                    }
                    else
                    {
                        Navigation.NavigateTo($"Pages/SignIn?returnUrl={returnUrl}", true);
                    }
                }
            }
        }
    }
    

    資源

  • ASP.NET Core Blazor authentication and authorization | Microsoft Docs
  • c# - SignIn for Blazor Server-Side app not working - Stack Overflow
  • Blazor - Redirect non-authenticated user to login page