gpt4 book ai didi

没有身份脚手架的 .NET Blazor 登录

转载 作者:行者123 更新时间:2023-12-04 12:41:18 24 4
gpt4 key购买 nike

我正在尝试将身份验证/登录添加到我的 Blazor 服务器应用程序。
我不想使用 Identity 的东西,它设置页面和数据库。

当我使用 Razor 页面执行此操作时,我可以使用如下代码登录一个页面:

    var claims = new List<Claim>{
new Claim(ClaimTypes.Name, Input.Name),
};

var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme, principal);

这将使用 cookie 登录,我可以阻止访问页面,或者根据声明或您是否登录来显示/隐藏内容。

现在,如果我使用 Identity,它会为登录和用户管理设置 razor 页面,但我正在尝试从 razor 组件执行登录功能,我真的找不到解决这个问题的方法。
我可以尝试注入(inject)一个 HttpContextAccessor 并使用它:
    HttpContext httpContext = HttpContextAccessor.HttpContext;
await httpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme, principal);

但它抛出一个异常:
未处理的异常呈现组件: header 是只读的,响应已经开始。
System.InvalidOperationException: header 是只读的,响应已经开始。

我能找到的大部分内容似乎都是采用 Razor 页面的方法。

是否无法使用 razor 组件登录?

最佳答案

可以办到。这是原理:

  • 创建 Login.razor 组件并注入(inject) SignInManager 和 NavigationManager。使用 SignInManager 使用 CheckPasswordSignInAsync() 方法验证密码。不要调用 PasswordSignInAsync() 因为它会抛出前面提到的异常。相反,将凭据传递到自定义中间件中的凭据缓存(请参阅下一段)。然后调用 NavigationManager.NagigateTo(/login?key=, true ) 执行完整回发,这是设置 cookie 所必需的。
  • 创建一个中间件类(我称之为 BlazorCookieLoginMiddleware):在那里你使用一个静态字典来缓存来自 Blazor 登录组件的登录信息。此外,您拦截对“/login?key=”的请求,然后使用 SignInManager 执行实际登录。这是有效的,因为中间件在管道中较早执行,此时仍然可以设置 cookie。凭据可以从静态字典缓存中检索,并应立即从字典中删除。如果身份验证成功,您只需将用户重定向到应用程序根目录“/”或您想要的任何位置。

  • 我测试了这个,它就像一个魅力。我还成功添加了 2FA,但这对于这篇文章来说太多了。
    这是一些代码(请注意:为简单起见,未正确处理边缘情况和错误;只是一个 PoC):
    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))
    {
    <div class="alert-danger">
    <p>@error</p>
    </div>
    }
    <button @onclick="LoginClicked">Login</button>

    @code {
    public string Email { get; set; }

    private string password;
    private string error;

    private async Task LoginClicked()
    {
    error = null;
    var usr = await UserMgr.FindByEmailAsync(Email);
    if (usr == null)
    {
    error = "User not found";
    return;
    }


    if (await SignInMgr.CanSignInAsync(usr))
    {
    var result = await SignInMgr.CheckPasswordSignInAsync(usr, password, true);
    if (result == Microsoft.AspNetCore.Identity.SignInResult.Success)
    {
    Guid key = Guid.NewGuid();
    BlazorCookieLoginMiddleware.Logins[key] = new LoginInfo { Email = Email, Password = password };
    NavMgr.NavigateTo($"/login?key={key}", true);
    }
    else
    {
    error = "Login failed. Check your password.";
    }
    }
    else
    {
    error = "Your account is blocked";
    }
    }
    }
    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)
    {
    //TODO: redirect to 2FA razor component
    context.Response.Redirect("/loginwith2fa/" + key);
    return;
    }
    else
    {
    //TODO: Proper error handling
    context.Response.Redirect("/loginfailed");
    return;
    }
    }
    else
    {
    await _next.Invoke(context);
    }
    }
    }
    并且不要忘记向 添加新的中间件启动.cs:
            public void Configure(IApplicationBuilder app)
    {
    //.....
    app.UseAuthentication();
    app.UseAuthorization();

    app.UseMiddleware<BlazorCookieLoginMiddleware>();
    //.....
    }

    关于没有身份脚手架的 .NET Blazor 登录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58940504/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com