Вход в серверное приложение Blazor не работает - PullRequest
1 голос
/ 15 октября 2019

Я создаю пример бритвенного компонента для входа в приложение на стороне сервера Blazor для ядра Asp.net 3.0. Всякий раз, когда код достигает метода SignInAsyc, он просто зависает или блокируется, поскольку код прекращает дальнейшее выполнение. Я также попытался переключить логику, используя метод PasswordSignInAsync, который дал мне точно такой же результат. Весь код будет выполняться перед этим методом, но затем остановится при выполнении этого оператора. Что мне здесь не хватает?

Страница компонента бритвы:

<div class="text-center">
    <Login FieldsetAttr="fieldsetAttr" UsernameAttr="usernameAttr" PasswordAttr="passwordInput"
           ButtonAttr="buttonAttr" ButtonText="Sign In" InvalidAttr="invalidAttr" />

</div>

@code {
    Dictionary<string, object> fieldsetAttr =
        new Dictionary<string, object>()
        {
            {"class", "form-group" }
        };

    Dictionary<string, object> usernameAttr =
        new Dictionary<string, object>()
        {
            {"class", "form-control" },
            {"type", "text" },
            {"placeholder", "Enter your user name here." }
        };

    Dictionary<string, object> passwordInput =
        new Dictionary<string, object>()
        {
            {"class", "form-control" },
            {"type", "password" }
        };

    Dictionary<string, object> buttonAttr =
        new Dictionary<string, object>()
        {
            {"type", "button" }
        };

    Dictionary<string, object> invalidAttr =
        new Dictionary<string, object>()
        {
            {"class", "" },
            {"style", "color: red;" }
        };

    Dictionary<string, object> validAttr =
        new Dictionary<string, object>()
        {
            {"class", "" },
            {"style", "color: green;" }
        };

}

Компонент бритвы:

@inject SignInManager<IdentityUser> signInManager
@inject UserManager<IdentityUser> userManager

<div @attributes="FormParentAttr">
    <form @attributes="LoginFormAttr">
        <fieldset @attributes="FieldsetAttr">
            <legend>Login</legend>
            <label for="usernameId">Username</label><br />
            <input @attributes="UsernameAttr" id="usernameId" @bind="UserName" /><br />
            <label for="upasswordId">Password</label><br />
            <input @attributes="PasswordAttr" id="passwordId" @bind="Password" /><br />
            <button @attributes="ButtonAttr" @onclick="@(async e => await LoginUser())">@ButtonText</button>
            @if (errorMessage != null && errorMessage.Length > 0)
            {
                <div @attributes="InvalidAttr">
                    @errorMessage
                </div>
            }
            else if(successMessage != null && successMessage.Length > 0)
            {
                <div @attributes="ValidAttr">
                    @successMessage
                </div>
            }
        </fieldset>
    </form>
</div>

@code {

    string successMessage = "";

    private async Task LoginUser()
    {
        if(!String.IsNullOrEmpty(UserName))
        {
            var user = await userManager.FindByNameAsync(UserName);
            var loginResult =
                await signInManager.CheckPasswordSignInAsync(user, Password, false);



            if(loginResult.Succeeded)
            {
                await signInManager.SignInAsync(user, true);
                successMessage = $"{UserName}, signed in.";
                errorMessage = "";
            }
            else
            {
                successMessage = "";
                errorMessage = "Username or password is incorrect.";
            }
        }
        else
        {
            successMessage = "";
            errorMessage = "Provide a username.";
        }
    }

    [Parameter]
    public Dictionary<string, object> FormParentAttr { get; set; }

    [Parameter]
    public Dictionary<string, object> LoginFormAttr { get; set; }

    [Parameter]
    public Dictionary<string, object> FieldsetAttr { get; set; }

    [Parameter]
    public Dictionary<string, object> UsernameAttr { get; set; }

    [Parameter]
    public Dictionary<string, object> PasswordAttr { get; set; }

    [Parameter]
    public Dictionary<string,object> ButtonAttr { get; set; }

    [Parameter]
    public Dictionary<string, object> InvalidAttr { get; set; }

    private string UserName { get; set; }
    private string Password { get; set; }

    [Parameter]
    public string ButtonText { get; set; }

    [Parameter]
    public Dictionary<string, object> ValidAttr { get;set; }

    public string errorMessage { get; set; }

}

1 Ответ

1 голос
/ 15 октября 2019

По сути, это происходит потому, что SigninManger::SignInAsync() на самом деле попытается отправить cookie через HTTP, чтобы указать, что этот пользователь уже вошел в систему. Но при работе с серверной стороной Blazor в данный момент, есть нет данных HTTP Ответ вообще, есть только соединение WebSocket (SignalR).

Как исправить

В двух словах, Войтисохранить учетные данные пользователя / куки / ..., чтобы WebApp знал, кто является клиентом. Поскольку вы используете серверную часть Blazor, ваш клиент общается с сервером в рамках соединения WebSocket . Там нет необходимости отправлять куки более HTTP. Потому что ваше WebApp уже знает, кто является текущим пользователем.

Чтобы решить эту проблему, сначала зарегистрируйте службу IHostEnvironmentAuthenticationStateProvider:

services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<IdentityUser>>();
services.AddScoped<IHostEnvironmentAuthenticationStateProvider>(sp => {
    // this is safe because 
    //     the `RevalidatingIdentityAuthenticationStateProvider` extends the `ServerAuthenticationStateProvider`
    var provider = (ServerAuthenticationStateProvider) sp.GetRequiredService<AuthenticationStateProvider>();
    return provider;
});

А затем создайте принципал и замените старый.

<b>@inject AuthenticationStateProvider AuthenticationStateProvider
@inject IHostEnvironmentAuthenticationStateProvider HostAuthentication</b>
...

var user = await userManager.FindByNameAsync(UserName);
<b>var valid= await signInManager.UserManager.CheckPasswordAsync(user, Password);</b>

if (valid)
{
    <b>var principal = await signInManager.CreateUserPrincipalAsync(user);

    var identity = new ClaimsIdentity(
        principal.Claims,
        Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationDefaults.AuthenticationScheme
    );
    principal = new System.Security.Claims.ClaimsPrincipal(identity);
    signInManager.Context.User = principal;
    HostAuthentication.SetAuthenticationState(Task.FromResult(new AuthenticationState(principal)));</b>

    // now the authState is updated
    var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();

    successMessage = $"{UserName}, signed in.";
    errorMessage = "";

}
else
{
    successMessage = "";
    errorMessage = "Username or password is incorrect.";
}

Демо

enter image description here

И отметьте authState:

enter image description here

...