Blazor Server JWT认证报错:无法发起JavaScript互操作调用
解决方案
原因分析
Blazor Server默认启用预渲染(Prerendering),在该阶段服务器会提前生成HTML并发送给浏览器,但此时浏览器的JavaScript环境尚未初始化,无法执行JS互操作(比如LocalStorage读写)。你的CustomAuthStateProvider在初始化时(GetAuthenticationStateAsync方法)直接调用_localStorage.GetItemAsStringAsync触发了JS互操作,因此抛出异常。
方法一:禁用预渲染(快速解决)
如果不需要预渲染的首屏优化,可直接禁用:
- 修改
_Host.cshtml中的组件渲染模式:
将原<component type="typeof(App)" render-mode="Server" />ServerPrerendered替换为Server。 - 或在
Program.cs中全局禁用:builder.Services.AddServerSideBlazor() .DisableAutoServerPrerendering();
方法二:保留预渲染,延迟JS互操作(推荐)
若要保留预渲染优势,可修改CustomAuthStateProvider先返回匿名身份,待JS环境就绪后再获取token并更新认证状态:
步骤1:修改CustomAuthStateProvider
public class CustomAuthStateProvider : AuthenticationStateProvider { private readonly ILocalStorageService _localStorage; private readonly HttpClient _httpClient; private readonly AuthenticationState _anonymousState = new(new ClaimsPrincipal(new ClaimsIdentity())); public CustomAuthStateProvider(ILocalStorageService localStorage, HttpClient httpClient) { _localStorage = localStorage; _httpClient = httpClient; } public override async Task<AuthenticationState> GetAuthenticationStateAsync() { try { string token = await _localStorage.GetItemAsStringAsync("token"); if (string.IsNullOrEmpty(token)) return _anonymousState; // 解析JWT生成ClaimsPrincipal,需引入System.IdentityModel.Tokens.Jwt包 var jwtHandler = new JwtSecurityTokenHandler(); var jwtToken = jwtHandler.ReadJwtToken(token); var identity = new ClaimsIdentity(jwtToken.Claims, "JWT"); return new AuthenticationState(new ClaimsPrincipal(identity)); } catch (InvalidOperationException) { // 预渲染阶段捕获JS互操作异常,返回匿名状态 return _anonymousState; } } // 供组件在JS环境就绪后调用,更新认证状态 public async Task UpdateAuthenticationState() { string token = await _localStorage.GetItemAsStringAsync("token"); var state = string.IsNullOrEmpty(token) ? _anonymousState : new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(new JwtSecurityTokenHandler().ReadJwtToken(token).Claims, "JWT"))); NotifyAuthenticationStateChanged(Task.FromResult(state)); } }
步骤2:在根组件触发状态更新
修改App.razor,在JS环境就绪后调用状态更新方法:
@inject CustomAuthStateProvider AuthStateProvider <Router AppAssembly="@typeof(App).Assembly"> <!-- 路由内容 --> </Router> @code { protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { // 首次渲染后(JS环境就绪)更新认证状态 await AuthStateProvider.UpdateAuthenticationState(); } } }
步骤3:优化授权组件体验
在<AuthorizeView>中添加加载状态,避免预渲染阶段的内容闪烁:
<AuthorizeView> <Authorizing> <p>加载中...</p> </Authorizing> <Authorized> <!-- 已授权内容 --> </Authorized> <NotAuthorized> <!-- 未授权内容 --> </NotAuthorized> </AuthorizeView>
内容的提问来源于stack exchange,提问作者Wadjey
相关产品推荐
相关产品推荐

