Entity Framework mit Blazor-Anmeldeseite
Wenn Sie Identity Framework mit Blazor Server verwenden, lautet die offizielle Erklärung von Microsoft, dass es nicht unterstützt wird, und verwenden Sie stattdessen eine Razor-Anmeldeseite. Das Problem ist, dass Blazor keine Cookies schreiben kann.
Dies hat einige Nachteile:
- Die HTML-Layoutdateien können nicht wiederverwendet werden. Es müssen doppelte Layoutdateien für Razor neu erstellt werden.
- Anmeldeschaltflächen können nicht auf der Blazor-Seite eingebettet werden
- Schlechte Benutzererfahrung, wenn Sie sich optional im Rahmen einer Checkout-Erfahrung anmelden müssen
Dieser Typ hat herausgefunden, wie eine Blazor-Anmeldeseite mit Blazor WebAssembly funktioniert. Er ist sich nicht sicher, wie er das Problem umgangen hat und ob ein ähnlicher Ansatz für Blazor Server funktionieren kann.
Ich dachte an alternative Lösungen. Das Problem besteht darin, das Cookie zu speichern. Lokaler Speicher kann in Blazor verwendet werden , lokaler Speicher ist jedoch für Sicherheitstoken nicht sicher. Cookies können jedoch in ähnlicher Weise auch über JavaScript Interop gesetzt werden.
Würde dieser Ansatz funktionieren, indem das Anmelde-Cookie nach der Anmeldung über JavaScript Interop gesetzt wird und das Cookie dann beim weiteren Laden der Seite gesendet wird? Hat es jemand getan?
Für einseitige Administratorinhalte habe ich einen einfacheren Ansatz gefunden, eine GatedContent-Komponente zu erstellen, die ein Anmeldeformular anzeigt und nach der Anmeldung ChildContent anzeigt. Dadurch wird die Sitzung bei der Seitenaktualisierung natürlich nicht beibehalten, dies funktioniert jedoch in einigen Fällen.
Antworten
Hier ist eine Lösung für einseitige Administratorinhalte: GatedContent
Es wird ein Anmeldeformular angezeigt und nach erfolgreicher Anmeldung wird der gated Inhalt angezeigt.
SpinnerButton wird hier definiert.
@using Microsoft.AspNetCore.Identity
@using System.ComponentModel.DataAnnotations
@inject NavigationManager navManager
@inject SignInManager<ApplicationUser> signInManager
@inject UserManager<ApplicationUser> userManager;
@if (!LoggedIn)
{
<EditForm Context="formContext" class="form-signin" OnValidSubmit="@SubmitAsync" Model="this">
<DataAnnotationsValidator />
<div style="margin-bottom: 1rem; margin-top: 1rem;" class="row">
<Field For="@(() => Username)" Width="4" Required="true">
<RadzenTextBox @bind-Value="@Username" class="form-control" Style="margin-bottom: 0px;" />
</Field>
</div>
<div style="margin-bottom: 1rem" class="row">
<Field For="@(() => Password)" Width="4" Required="true">
<RadzenPassword @bind-Value="@Password" class="form-control" />
</Field>
</div>
<SpinnerButton @ref="ButtonRef" style="width:150px" Text="Login" ButtonType="@((Radzen.ButtonType)ButtonType.Submit)" ButtonStyle="@((Radzen.ButtonStyle)ButtonStyle.Primary)" OnSubmit="LogInAsync" />
@if (Error.HasValue())
{
<div class="red">@Error</div>
}
</EditForm>
}
else
{
@ChildContent
}
@code {
public SpinnerButton? ButtonRef { get; set; }
public async Task SubmitAsync() => await ButtonRef!.FormSubmitAsync().ConfigureAwait(false);
[Required]
public string Username { get; set; } = string.Empty;
[Required]
public string Password { get; set; } = string.Empty;
string? Error { get; set; }
bool LoggedIn { get; set; }
[Parameter]
public RenderFragment? ChildContent { get; set; }
public async Task LogInAsync()
{
Error = null;
try
{
var user = await userManager.FindByNameAsync(Username).ConfigureAwait(false);
if (user != null)
{
var isAdmin = await userManager.IsInRoleAsync(user, ApplicationRole.Admin).ConfigureAwait(false);
if (isAdmin)
{
var singInResult = await signInManager.CheckPasswordSignInAsync(user, Password, true).ConfigureAwait(false);
if (singInResult.Succeeded)
{
LoggedIn = true;
}
else
{
Error = "Invalid password";
}
}
else
{
Error = "User is not Admin";
}
}
else
{
Error = "Username not found";
}
}
catch (Exception ex)
{
Error = ex.Message;
}
}
}
Verwenden Sie so
<GatedContent>
This is an admin page.
</GatedContent>
Natürlich behält diese Lösung den Status nach dem erneuten Laden der Seite nicht bei, funktioniert jedoch für einfache Administrationsseiten.
Meine Problemumgehung sieht folgendermaßen aus: Ich habe einen SignInController wie diesen erstellt
public class SignInController : ControllerBase
{
private readonly SignInManager<IdentityUser> _signInManager;
public SignInController(SignInManager<IdentityUser> signInManager)
{
_signInManager = signInManager;
}
[HttpPost("/signin")]
public IActionResult Index([FromForm]string username, [FromForm]string password, [FromForm]string rememberMe)
{
bool.TryParse(rememberMe, out bool res);
var signInResult = _signInManager.PasswordSignInAsync(username, password, res, false);
if (signInResult.Result.Succeeded)
{
return Redirect("/");
}
return Redirect("/login/"+ signInResult.Result.Succeeded);
}
[HttpPost("/signout")]
public async Task<IActionResult> Logout()
{
if (_signInManager.IsSignedIn(User))
{
await _signInManager.SignOutAsync();
}
return Redirect("/");
}
}
Und ich habe einen login.razor wie diesen:
@page "/login"
<form action="/signin" method="post">
<div class="form-group">
<p> Username </p>
<input id="username" Name="username" />
</div>
<div class="form-group">
<p>password<p/>
<input type="password" id="password" />
</div>
<div class="form-group">
<p> remember me? <p/>
<input type="checkbox" id="rememberMe" />
</div>
<button type="Submit" Text="login" />
</form>
Dieses Problem wird durch den signInManager verursacht, der nur funktioniert, wenn Sie HTTP / S-Anforderungen verwenden. Andernfalls werden Ausnahmen ausgelöst. Es gibt auch einige Berichte darüber im dotnet / aspcore.net Repo auf GitHub.
Im Moment ist dies die einzige Möglichkeit (ich weiß), es mit Blazor Server zu verwenden.