Entity Framework พร้อมหน้าเข้าสู่ระบบ Blazor
เมื่อพูดถึงการใช้ Identity Framework กับ Blazor Server คำแถลงอย่างเป็นทางการของ Microsoft คือไม่รองรับและให้ใช้หน้าเข้าสู่ระบบ Razor แทน ปัญหาคือ Blazor เขียนคุกกี้ไม่ได้
สิ่งนี้มีข้อบกพร่องเล็กน้อย:
- ไม่สามารถใช้ไฟล์เลย์เอาต์ HTML ซ้ำได้จำเป็นต้องสร้างไฟล์เลย์เอาต์ที่ซ้ำกันสำหรับ Razor
- ไม่สามารถฝังปุ่มล็อกอินบนหน้า Blazor ได้
- ประสบการณ์ผู้ใช้ไม่ดีหากจำเป็นต้องลงชื่อเข้าใช้เป็นส่วนหนึ่งของประสบการณ์การชำระเงิน
ผู้ชายคนนี้คิดหาวิธีทำให้หน้าล็อกอินของ Blazor ทำงานร่วมกับ Blazor WebAssembly ...ไม่แน่ใจว่าเขาแก้ไขปัญหาอย่างไรหรือว่าวิธีการที่คล้ายกันนี้สามารถใช้ได้กับ Blazor Server หรือไม่
ฉันกำลังคิดหาทางเลือกอื่น ปัญหาคือการจัดเก็บคุกกี้ พื้นที่จัดเก็บในตัวเครื่องสามารถใช้ได้ใน Blazorแต่ที่เก็บข้อมูลในเครื่องไม่ปลอดภัยสำหรับโทเค็นความปลอดภัย อย่างไรก็ตามสามารถตั้งค่าคุกกี้ผ่านการทำงานร่วมกันของ JavaScript ได้ในลักษณะเดียวกัน
วิธีนี้จะใช้ได้ผลหรือไม่โดยตั้งค่าคุกกี้การเข้าสู่ระบบผ่านการทำงานร่วมกันของ JavaScript หลังจากเข้าสู่ระบบจากนั้นคุกกี้จะถูกส่งไปในการโหลดหน้าใด ๆ เพิ่มเติม มีใครทำบ้าง
สำหรับเนื้อหาผู้ดูแลระบบหน้าเดียวฉันพบวิธีที่ง่ายกว่าในการสร้างคอมโพเนนต์ GatedContent ที่แสดงฟอร์มการเข้าสู่ระบบจากนั้นแสดง ChildContent หลังจากล็อกอิน แน่นอนว่านี่จะไม่เก็บรักษาเซสชันในการรีเฟรชหน้า แต่ใช้ได้กับบางกรณี
คำตอบ
นี่เป็นวิธีแก้ปัญหาสำหรับเนื้อหาผู้ดูแลระบบหน้าเดียว: GatedContent
จะแสดงแบบฟอร์มการเข้าสู่ระบบและหลังจากเข้าสู่ระบบสำเร็จให้แสดงเนื้อหาที่มีรั้วรอบขอบชิด
SpinnerButton ถูกกำหนดไว้ที่นี่
@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;
}
}
}
ใช้แบบนี้
<GatedContent>
This is an admin page.
</GatedContent>
แน่นอนว่าโซลูชันนี้ไม่ได้รักษาสถานะหลังจากการโหลดหน้าซ้ำ แต่ใช้ได้กับหน้าผู้ดูแลระบบทั่วไป
วิธีแก้ปัญหาของฉันเป็นเช่นนี้ฉันได้สร้าง SignInController ไว้เช่นนี้
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("/");
}
}
และฉัน login.razor แบบนี้:
@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>
ปัญหานี้เกิดจาก signInManager ซึ่งใช้งานได้ก็ต่อเมื่อคุณใช้คำขอ HTTP / S มิฉะนั้นจะทำให้เกิดข้อยกเว้น นอกจากนี้ยังมีรายงานบางส่วนเกี่ยวกับเรื่องนี้ใน dotnet / aspcore.net Repo บน GitHub
ในขณะนี้นี่เป็นวิธีเดียวที่ (ฉันรู้) ที่จะใช้กับเซิร์ฟเวอร์ blazor