Blazor WASM使用gRPC code-first模式时token过期的正确处理方法咨询
解决步骤
1. 实现自定义gRPC客户端拦截器
继承Interceptor类实现自定义拦截器,在拦截逻辑中完成token有效期校验、强制刷新、调用重试以及未授权跳转逻辑:
public class AuthTokenRefreshInterceptor : Interceptor { private readonly IAccessTokenProvider _accessTokenProvider; private readonly NavigationManager _navigationManager; public AuthTokenRefreshInterceptor(IAccessTokenProvider accessTokenProvider, NavigationManager navigationManager) { _accessTokenProvider = accessTokenProvider; _navigationManager = navigationManager; } public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>( TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation) { return new AsyncUnaryCall<TResponse>( HandleResponseAsync(request, context, continuation), ResponseHeadersAsync, GetStatus, GetTrailers, Dispose); async Task<TResponse> HandleResponseAsync(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation) { // 预检查token有效性,提前5分钟触发刷新避免接口调用失败 var tokenResult = await _accessTokenProvider.RequestAccessToken(); if (!tokenResult.TryGetToken(out var accessToken) || accessToken.Expires < DateTimeOffset.Now.AddMinutes(5)) { var refreshResult = await _accessTokenProvider.RequestAccessToken(new AccessTokenRequestOptions { ForceRefresh = true }); if (!refreshResult.TryGetToken(out _)) { _navigationManager.NavigateToLogin(); throw new RpcException(new Status(StatusCode.Unauthenticated, "身份凭证已失效,请重新登录")); } } // 执行原调用 var call = continuation(request, context); try { return await call.ResponseAsync; } catch (RpcException ex) when (ex.StatusCode == StatusCode.Unauthenticated) { // 兜底逻辑:调用返回未授权时再刷新重试一次 var refreshResult = await _accessTokenProvider.RequestAccessToken(new AccessTokenRequestOptions { ForceRefresh = true }); if (refreshResult.TryGetToken(out _)) { var retryCall = continuation(request, context); return await retryCall.ResponseAsync; } _navigationManager.NavigateToLogin(); throw; } } // 基础透传方法,无需修改 private Task<Metadata> ResponseHeadersAsync() => Task.FromResult<Metadata>(null); private Status GetStatus() => Status.DefaultSuccess; private Metadata GetTrailers() => new Metadata(); private void Dispose() { } } }
如果你用到了客户端流、服务端流、双向流类型的gRPC调用,需要额外重写
AsyncClientStreamingCall、AsyncServerStreamingCall、AsyncDuplexStreamingCall方法,添加相同的token校验逻辑。
2. 修改Grpc服务注册配置
将自定义拦截器注册到DI容器,并绑定到GrpcChannel中:
public static WebAssemblyHostBuilder AddGrpc(this WebAssemblyHostBuilder builder) { // 注册自定义拦截器 builder.Services.AddScoped<AuthTokenRefreshInterceptor>(); builder.Services .AddTransient(sp => sp.GetRequiredService<IHttpClientFactory>() .CreateClient(BaseClient)); builder.Services.AddScoped(services => { var baseAddressMessageHandler = services.GetRequiredService<AuthenticationHeaderHandler>(); baseAddressMessageHandler.InnerHandler = new HttpClientHandler(); var grpcWebHandler = new GrpcWebHandler(GrpcWebMode.GrpcWeb, baseAddressMessageHandler); // 从DI容器获取拦截器 var refreshInterceptor = services.GetRequiredService<AuthTokenRefreshInterceptor>(); var channel = GrpcChannel.ForAddress(builder.HostEnvironment.BaseAddress, new GrpcChannelOptions { HttpHandler = grpcWebHandler, // 绑定拦截器到Channel Interceptors = { refreshInterceptor } }); return channel; }); return builder; }
3. 可选调整项
- 可以根据业务需要调整token提前刷新的时间阈值,一般推荐设置为token有效期的1/10
- 如果你的
AuthenticationHeaderHandler已经会自动从IAccessTokenProvider获取最新token,拦截器中不需要手动添加Authorization请求头,仅触发刷新逻辑即可
内容的提问来源于stack exchange,提问作者Мстислав Павлов
相关产品推荐
相关产品推荐

