可以做到。这是原理:
1.创建一个 Login.razor 组件并注入 SignInManager 和 NavigationManager 。使用 SignInManager 使用 CheckPasswordSignInAsync() 方法来验证密码。不要调用 PasswordSignInAsync() ,因为它会抛出前面提到的异常。相反,将凭据传递给自定义中间件中的凭据缓存(参见下一段)。然后调用 NavigationManager.NavigateTo(/login?key=, true) 执行完整的 postback,这是必需的以设置 cookie。
2.创建一个 Middleware 类(我称其为 BlazorCookieLoginMiddleware ):在其中使用静态字典来缓存来自 Blazor 登录组件的登录信息。此外,拦截 "/login?key=" 请求,然后使用 SignInManager 执行实际的登录。这是有效的,因为当 cookie 可以被设置时,中间件在管道中较早执行。可以从静态字典缓存中检索凭据,并应立即从字典中删除它们。如果身份验证成功,只需将用户重定向到应用程序根目录“/”或任何其他位置即可。
我测试过了,它可以很好地工作。我还成功添加了双因素认证,但那对于本文来说太多了。
以下是一些代码(请注意:为简单起见,未正确处理极端情况和错误):
Login.razor:
@page "/login"
@attribute [AllowAnonymous]
@inject SignInManager<ApplicationUser> SignInMgr
@inject UserManager<ApplicationUser> UserMgr
@inject NavigationManager NavMgr
<h3>Login</h3>
<label for="email">Email:</label>
<input type="email" @bind="Email" name="email" />
<label for="password">Password:</label>
<input type="password" @bind="password" name="password" />
@if (!string.IsNullOrEmpty(error))
<button @onclick="LoginClicked">Login</button>
@code
private string password;
private string error;
private async Task LoginClicked()
if (await SignInMgr.CanSignInAsync(usr))
;
NavMgr.NavigateTo($"/login?key=", true);
}
else
}
else
}
}
BlazorCookieLoginMiddleware.cs:
public class LoginInfo
{
public string Email { get; set; }
public string Password { get; set; }
}
public class BlazorCookieLoginMiddleware
{
public static IDictionary<Guid, LoginInfo> Logins { get; private set; }
= new ConcurrentDictionary<Guid, LoginInfo>();
private readonly RequestDelegate _next;
public BlazorCookieLoginMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context, SignInManager<ApplicationUser> signInMgr)
{
if (context.Request.Path == "/login" && context.Request.Query.ContainsKey("key"))
{
var key = Guid.Parse(context.Request.Query["key"]);
var info = Logins[key];
var result = await signInMgr.PasswordSignInAsync(info.Email, info.Password, false, lockoutOnFailure: true);
info.Password = null;
if (result.Succeeded)
{
Logins.Remove(key);
context.Response.Redirect("/");
return;
}
else if (result.RequiresTwoFactor)
{
context.Response.Redirect("/loginwith2fa/" + key);
return;
}
else
{
context.Response.Redirect("/loginfailed");
return;
}
}
else
{
await _next.Invoke(context);
}
}
}
不要忘记将新的中间件添加到 Startup.cs 中:
public void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<BlazorCookieLoginMiddleware>();
}