使用 HttpContext.SignInAsync 不使用身份的 Blazor 服务器身份验证

问题描述 投票:0回答:1

一直在尝试使用我自己的身份检查为 Blazor 服务器端应用程序进行身份验证(我不想使用 SqlServer 或该机制)。一切似乎都很好,接受

HttpContext
始终是
null
。我看到了很多很多关于如何克服这个问题的建议,但没有一个有效:

无需身份服务器的 Blazor 服务器应用程序身份验证

其他一切似乎都有效,除了我无法登录我正在创建的身份(也注销)。我尝试过删除交互式渲染,但无济于事。

这是

Login.razor
代码:

@page "/login"
@rendermode @(new InteractiveServerRenderMode(false))
@inject LoginApiClient LoginApi
@inject UserApiClient UserApi
@inject NavigationManager navigationManager 
@inject IHttpContextAccessor httpContextAccessor
@using MayApp.Web.APIClients;
@using MayApp.Web.Models
@using Blazorise.Components
@using System.Text.RegularExpressions;
@using System.Security.Claims
@using Microsoft.AspNetCore.Authentication.Cookies
@using Microsoft.AspNetCore.Authentication;
@using Microsoft.AspNetCore.Http

<PageTitle>Login - My App</PageTitle>
<h1>Login</h1>

<div>
<Validations @ref="@ValidationsRef" Model="@login" ValidateOnLoad="false">
    <Field ColumnSize="ColumnSize.Is4">
        <FieldLabel RequiredIndicator>User Name</FieldLabel>
        <Validation Validator="ValidationRule.IsNotEmpty">
            <TextEdit @bind-Text="@login.UserName" MaxLength="30">
                <Feedback>
                    <ValidationError>Please provide an User Name</ValidationError>
                </Feedback>
            </TextEdit>
        </Validation>
    </Field>
    @if (@error)
    {
        <Field ColumnSize="ColumnSize.Is4" > 
           <Text TextColor="TextColor.Danger">@errorMessage</Text>
        </Field>
    }
    <Field ColumnSize="ColumnSize.Is4">
        <FieldLabel RequiredIndicator>Password</FieldLabel>
        <Validation Validator="@ValidatePassword">
            <TextEdit @bind-Text="@login.Password" Placeholder="Enter a Password" MaxLength="50" Role="TextRole.Password">
                <Feedback>
                    <ValidationError>Invalid password. Passwords must be between 8 and 30 characters long, one number, one lower and one upper case letter, and one special character.</ValidationError>
                </Feedback>
            </TextEdit>
        </Validation>
    </Field>
    <Field>
        <Button Color="Color.Success" Clicked="@Authenticate">Login</Button>
    </Field>

</Validations>
</div>



@code {
    [CascadingParameter]
    public HttpContext httpcontext { get; set; } = default!;

    private MyApp.Web.Models.Login login = new();
    const string passwordPattern = @"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\da-zA-Z]).{8,30}$";
    string errorMessage = string.Empty;
    bool error = false;

    Validations ValidationsRef { get; set; }

    void ValidatePassword(ValidatorEventArgs e)
    {
        bool isMatch = Regex.IsMatch(Convert.ToString(e.Value), passwordPattern);
        e.Status = isMatch ? ValidationStatus.Success : ValidationStatus.Error;
    }

    async void Authenticate()
    {
        var result = await MyApp.Login(login);
        if (result.Success)
        {
            if (result.PasswordResetNeeded)
            {
                // Provide a link to reset password.
                error = true;
                errorMessage = result.Message;
                await InvokeAsync(StateHasChanged);
            } 
            else if (result.Users != null && result.Users.Count > 0)
            {
                User user = result.Users[0];
                if (!string.IsNullOrEmpty(user.UserName) && !string.IsNullOrEmpty(user.Role))
                {
                    errorMessage = string.Empty;
                    error = false;
                    var claims = new List<Claim>
                    {
                        new Claim(ClaimTypes.Name, user.UserName),
                        new Claim(ClaimTypes.Role, user.Role)
                    };
                    var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
                    var principal = new ClaimsPrincipal(identity);
                    await httpcontext.SignInAsync(principal);
                    navigationManager.NavigateTo("/"); // Should be home

                }
                else
                {
                    error = true;
                    errorMessage = "Invalid UserName or Password.";
                    await InvokeAsync(StateHasChanged);
                }
            }
            else
            {
                error = true;
                errorMessage = "Invalid UserName or Password.";
                await InvokeAsync(StateHasChanged);
            }
        }
        else
        {
            error = true;
            errorMessage = "Invalid UserName or Password.";
            await InvokeAsync(StateHasChanged);
        }

    }

    protected override void OnInitialized()
    {
    }

}

失败总是因为这一行的上下文为空:

   await httpcontext.SignInAsync(principal);

我的program.cs看起来像这样:

using Blazored.Toast;
using Blazorise;
using Blazorise.Bootstrap5;
using Blazorise.Icons.FontAwesome;
using CollectXScore.Web.APIClients;
using CollectXScore.Web.Components;
using CollectXScore.Web.Utilities;
using Microsoft.AspNetCore.Authentication.Cookies;

// This is a Blazor Server Application (NOT a Webassembly App)
var builder = WebApplication.CreateBuilder(args);

// Add service defaults & Aspire components.
builder.AddServiceDefaults();

// Add services to the container.
builder.Services.AddRazorComponents().AddInteractiveServerComponents();
builder.Services.AddOutputCache();
builder.Services.AddBlazoredToast();
// Authentication and Authorization services
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).
    AddCookie(options => 
    {
        options.Cookie.Name = "auth_cookie";
        options.LoginPath = "/login";
        options.Cookie.MaxAge = TimeSpan.FromMinutes(90);
        options.AccessDeniedPath = "/accessdenied";
});
builder.Services.AddAuthorization();
builder.Services.AddCascadingAuthenticationState();

builder.Services
    .AddBlazorise(options =>
    {
        options.Immediate = true;
    })
    .AddBootstrap5Providers()
    .AddFontAwesomeIcons();
builder.Services.ConfigureApplicationCookie(ops =>
{
    ops.ExpireTimeSpan = TimeSpan.FromMinutes(30);
    ops.SlidingExpiration = true;
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAntiforgery();
app.UseAuthentication();
app.UseAuthorization();
app.UseOutputCache();
app.MapDefaultEndpoints();
app.Run();

有没有办法在启动时存储

HttpContext
然后在代码中或其他地方引用它?或者某些机制没有记录?

c# authentication authorization blazor-server-side
1个回答
0
投票

HttpContext 在交互式渲染模式下为 null。仅在 SSR 中可用。 https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-context?view=aspnetcore-8.0#ihttpcontextaccessorhttpcontext-in-razor-components-blazor

如果你的项目在整个项目中都使用SSR,你可以像MVC一样使用

HttpContext.SigninAsycn

当rendermode为InteractiveServer(prerender:false)时,项目使用websocket而不是Http。所以HttpContext不可用。 Blazor 实际上有另一个身份验证系统,它从

AuthenticationStateProvider
而不是
HttpContext
获取用户身份。

© www.soinside.com 2019 - 2024. All rights reserved.