admin管理员组文章数量:1356452
I am working on an internal tool that displays data from a database. The application uses authentication, managed by a CustomAuthStateProvider, which allows users to log in and out. Certain actions are restricted to authenticated users, and this functionality has been working correctly.
However, I encountered an issue when trying to navigate to a specific page. The navigation is triggered using the following line:
NavigationManager.NavigateTo($"/myPage/dateFrom={System.Uri.EscapeUriString(fromStr)}/dateTo={System.Uri.EscapeUriString(toStr)}", forceLoad: true);
The important part here is forceLoad: true
. The error only occurs when forceLoad: true
is used. When this navigation occurs, the application crashes with the following error message:
InvalidOperationException: Unable to find the required 'IAuthenticationService' service. Please add all the required services by calling 'IServiceCollection.AddAuthentication' in the application startup code.
Additional Finding
If I manually enter some URL of the app into the browser's address bar and press Enter, the same error occurs.
Further attempts
I tried refactoring some parts of the authentication logic and then encountered a new error:
The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action configureOptions).
In response to this, I attempted to use the solution suggested in this StackOverflow answer, but it did not resolve the issue.
Next, I tried to follow the approach outlined in this answer, but I ran into additional errors during implementation.
CustomAuthStateProvider.cs
using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;
using System.Threading.Tasks;
public class CustomAuthStateProvider : AuthenticationStateProvider
{
public CustomAuthStateProvider()
{
this.CurrentUser = this.GetAnonymous();
}
private ClaimsPrincipal CurrentUser { get; set; }
private ClaimsPrincipal GetUser(string userName, uint id, string role)
{
var identity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes. Sid, id.ToString(new System.Globalization.CultureInfo("de-DE"))),
new Claim(ClaimTypes.Name, userName),
new Claim(ClaimTypes.Role, role)
}, "Authentication type");
return new ClaimsPrincipal(identity);
}
private ClaimsPrincipal GetAnonymous()
{
var identity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Sid, "0"),
new Claim(ClaimTypes.Name, "Anonymous"),
new Claim(ClaimTypes.Role, "Anonymous")
}, null);
return new ClaimsPrincipal(identity);
}
public override Task<AuthenticationState> GetAuthenticationStateAsync()
{
var task = Task.FromResult(new AuthenticationState(this.CurrentUser));
return task;
}
public Task<AuthenticationState> ChangeUser(string username, uint id, string role)
{
this.CurrentUser = this.GetUser(username, id, role);
var task = this.GetAuthenticationStateAsync();
this.NotifyAuthenticationStateChanged(task);
return task;
}
public Task<AuthenticationState> Logout()
{
this.CurrentUser = this.GetAnonymous();
var task = this.GetAuthenticationStateAsync();
this.NotifyAuthenticationStateChanged(task);
return task;
}
}
Program.cs
var builder = WebApplication.CreateBuilder(args);
// Add MudBlazor services
builder.Services.AddMudServices();
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddSingleton<InitializationStateService>(); // Logging
builder.Services.AddScoped<CustomAuthStateProvider>();
builder.Services.AddScoped<AuthenticationStateProvider>(s => s.GetRequiredService<CustomAuthStateProvider>());
builder.Services.AddAuthorizationCore();
builder.Services.AddServerSideBlazor().AddCircuitOptions(options =>
{
options.DetailedErrors = true;
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(0);
options.DisconnectedCircuitMaxRetained = 0;
});
builder.Services.AddHealthChecks();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
}
app.UseStaticFiles();
app.UseAntifery();
app.UseHealthChecks("/health");
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();
I am working on an internal tool that displays data from a database. The application uses authentication, managed by a CustomAuthStateProvider, which allows users to log in and out. Certain actions are restricted to authenticated users, and this functionality has been working correctly.
However, I encountered an issue when trying to navigate to a specific page. The navigation is triggered using the following line:
NavigationManager.NavigateTo($"/myPage/dateFrom={System.Uri.EscapeUriString(fromStr)}/dateTo={System.Uri.EscapeUriString(toStr)}", forceLoad: true);
The important part here is forceLoad: true
. The error only occurs when forceLoad: true
is used. When this navigation occurs, the application crashes with the following error message:
InvalidOperationException: Unable to find the required 'IAuthenticationService' service. Please add all the required services by calling 'IServiceCollection.AddAuthentication' in the application startup code.
Additional Finding
If I manually enter some URL of the app into the browser's address bar and press Enter, the same error occurs.
Further attempts
I tried refactoring some parts of the authentication logic and then encountered a new error:
The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action configureOptions).
In response to this, I attempted to use the solution suggested in this StackOverflow answer, but it did not resolve the issue.
Next, I tried to follow the approach outlined in this answer, but I ran into additional errors during implementation.
CustomAuthStateProvider.cs
using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;
using System.Threading.Tasks;
public class CustomAuthStateProvider : AuthenticationStateProvider
{
public CustomAuthStateProvider()
{
this.CurrentUser = this.GetAnonymous();
}
private ClaimsPrincipal CurrentUser { get; set; }
private ClaimsPrincipal GetUser(string userName, uint id, string role)
{
var identity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes. Sid, id.ToString(new System.Globalization.CultureInfo("de-DE"))),
new Claim(ClaimTypes.Name, userName),
new Claim(ClaimTypes.Role, role)
}, "Authentication type");
return new ClaimsPrincipal(identity);
}
private ClaimsPrincipal GetAnonymous()
{
var identity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Sid, "0"),
new Claim(ClaimTypes.Name, "Anonymous"),
new Claim(ClaimTypes.Role, "Anonymous")
}, null);
return new ClaimsPrincipal(identity);
}
public override Task<AuthenticationState> GetAuthenticationStateAsync()
{
var task = Task.FromResult(new AuthenticationState(this.CurrentUser));
return task;
}
public Task<AuthenticationState> ChangeUser(string username, uint id, string role)
{
this.CurrentUser = this.GetUser(username, id, role);
var task = this.GetAuthenticationStateAsync();
this.NotifyAuthenticationStateChanged(task);
return task;
}
public Task<AuthenticationState> Logout()
{
this.CurrentUser = this.GetAnonymous();
var task = this.GetAuthenticationStateAsync();
this.NotifyAuthenticationStateChanged(task);
return task;
}
}
Program.cs
var builder = WebApplication.CreateBuilder(args);
// Add MudBlazor services
builder.Services.AddMudServices();
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddSingleton<InitializationStateService>(); // Logging
builder.Services.AddScoped<CustomAuthStateProvider>();
builder.Services.AddScoped<AuthenticationStateProvider>(s => s.GetRequiredService<CustomAuthStateProvider>());
builder.Services.AddAuthorizationCore();
builder.Services.AddServerSideBlazor().AddCircuitOptions(options =>
{
options.DetailedErrors = true;
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(0);
options.DisconnectedCircuitMaxRetained = 0;
});
builder.Services.AddHealthChecks();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
}
app.UseStaticFiles();
app.UseAntifery();
app.UseHealthChecks("/health");
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();
Share
Improve this question
edited Mar 31 at 8:45
Qiang Fu
9,3671 gold badge6 silver badges16 bronze badges
asked Mar 31 at 8:30
DanielDaniel
5522 silver badges16 bronze badges
1 Answer
Reset to default 1You are using InteractiveServer rendermode which uses SSR(Http) 1st and websocket(No http) later.
When you reload the page, it will use SSR 1st, which treated @attribute [Authorize]
like MVC/WebApi project, requiring you to add authentication scheme to check the identity. After that, it will proceed to AuthenticationStateProvider check.
A simple solution is using disable SSR rendermode
@rendermode @(new InteractiveServerRenderMode(false))
.
Another solution is bypass the scheme check by implement IAuthorizationMiddlewareResultHandler
public class BlazorAuthorizationMiddlewareResultHandler : IAuthorizationMiddlewareResultHandler
{
public Task HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult)
{
return next(context);
}
}
Then add it to program.cs
builder.Services.AddSingleton<IAuthorizationMiddlewareResultHandler, BlazorAuthorizationMiddlewareResultHandler>();
Just note that to for @attribute [Authorize]
to work will need to use AuthorizeRouteView
instead of RouteView
in Routes.razor.
本文标签:
版权声明:本文标题:c# - Blazor Server: forceLoad: true causes "IAuthenticationService" error on page navigation - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1743959729a2568798.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论