一直在尝试使用我自己的身份检查为 Blazor 服务器端应用程序进行身份验证(我不想使用 SqlServer 或该机制)。一切似乎都很好,接受
HttpContext
始终是null
。我看到了很多很多关于如何克服这个问题的建议,但没有一个有效:
其他一切似乎都有效,除了我无法登录我正在创建的身份(也注销)。我尝试过删除交互式渲染,但无济于事。
这是
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
然后在代码中或其他地方引用它?或者某些机制没有记录?
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
获取用户身份。