如何正确配置Blazor 8 Server Interactive以维护认证状态
问题:Blazor 8刷新页面时认证逻辑执行顺序导致错误跳转
我搭建了一个完整的示例项目,目前采用临时方案——每个页面手动调用方法获取认证状态,但存在问题:用户刷新页面时,Authorize会先于OnAfterRenderAsync执行,导致错误跳转至登录页。
我希望使用Blazor 8内置功能解决该问题,但未能找到正确配置方式。
Program.cs 配置代码
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddRazorComponents() .AddInteractiveServerComponents(); //Authentication builder.Services.AddAuthorization(); builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.Cookie.Name = "auth_token"; options.LoginPath = "/login"; options.Cookie.MaxAge = TimeSpan.FromHours(24); options.AccessDeniedPath = "/acessDenied"; }); builder.Services.AddScoped<AuthService>(); builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>(); builder.Services.AddCascadingAuthenticationState(); builder.Services.AddBlazoredSessionStorage(); builder.Services.AddScoped<ICustomSessionService, CustomSessionService>(); var app = builder.Build(); // Configure the HTTP request pipeline. 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.MapRazorComponents<App>() .AddInteractiveServerRenderMode(); app.Run();
AuthService 代码
using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using Microsoft.IdentityModel.Tokens; namespace Blazor8Auth.Services { public class AuthService { const string AuthTokenName = "auth_token"; public event Action<ClaimsPrincipal>? UserChanged; private ClaimsPrincipal? currentUser; private readonly ICustomSessionService _sessionService; private readonly IConfiguration _configuration; public AuthService(ICustomSessionService sessionService, IConfiguration configuration) { _sessionService = sessionService; _configuration = configuration; } public ClaimsPrincipal CurrentUser { get { return currentUser ?? new(); } set { currentUser = value; if (UserChanged is not null) { UserChanged(currentUser); } } } public bool IsLoggedIn => CurrentUser.Identity?.IsAuthenticated ?? false; public async Task LogoutAsync() { CurrentUser = new(); string authToken = await _sessionService.GetItemAsStringAsync(AuthTokenName); if (!string.IsNullOrEmpty(authToken)) { await _sessionService.RemoveItemAsync(AuthTokenName); } } public async Task GetStateFromTokenAsync() { string authToken = await _sessionService.GetItemAsStringAsync(AuthTokenName); var identity = new ClaimsIdentity(); if (!string.IsNullOrEmpty(authToken)) { try { var tokenHandler = new JwtSecurityTokenHandler(); var key = System.Text.Encoding.UTF8.GetBytes(_configuration.GetSection("AppSettings:Token").Value); tokenHandler.ValidateToken(authToken, new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(key), ValidateIssuer = false, ValidateAudience = false, ClockSkew = TimeSpan.Zero }, out SecurityToken validatedToken); var jwtToken = (JwtSecurityToken)validatedToken; identity = new ClaimsIdentity(jwtToken.Claims, "jwt"); } catch { await _sessionService.RemoveItemAsync(AuthTokenName); identity = new ClaimsIdentity(); } } var user = new ClaimsPrincipal(identity); CurrentUser = user; } public async Task Login(ClaimsPrincipal user) { CurrentUser = user; var tokenEncryptionKey = _configuration.GetSection("AppSettings:Token").Value; var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8 .GetBytes(tokenEncryptionKey)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature); var tokenHoursString = _configuration.GetSection("AppSettings:TokenHours").Value; int.TryParse(tokenHoursString, out int tokenHours); var token = new JwtSecurityToken( claims: user.Claims, expires: DateTime.Now.AddHours(tokenHours), signingCredentials: creds); var jwt = new JwtSecurityTokenHandler().WriteToken(token); await _sessionService.SetItemAsStringAsync(AuthTokenName, jwt); } } }
每个页面需添加的代码
@code { [Inject] private AuthService AuthService { get; set; } protected override async Task OnAfterRenderAsync(bool firstRender) { if (!AuthService.IsLoggedIn) { await AuthService.GetStateFromTokenAsync(); } } }
内容的提问来源于stack exchange,提问作者Greg Finzer
相关产品推荐
相关产品推荐

