IIS部署的Blazor Server SignalR应用证书绕过及403错误排查
问题:Blazor Server部署IIS后SignalR Hub连接403错误
我在Windows Server 2019的IIS上部署了基于.NET Core的Blazor Server应用,采用智能卡证书认证登录。IIS仅启用匿名认证,其余认证方式均禁用,应用登录功能正常,但加载初始化SignalR Hub连接的页面时直接崩溃,事件查看器显示403(Forbidden)错误。本地Visual Studio 2022的IIS Express环境下,应用及SignalR Hub连接均正常。我已安装IIS的WebSocket协议,并尝试修改HomeComponent.razor中的HubConnection配置(指定WebSocket传输、绕过证书校验等),但问题仍未解决。
相关代码
Program.cs
var builder = WebApplication.CreateBuilder(args); string connString = builder.Configuration.GetConnectionString("DefaultConnection"); // Add services to the container. builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); builder.Services.AddHttpContextAccessor(); builder.Services.AddSession(options => { options.Cookie.IsEssential = true; }); builder.Services.AddDistributedMemoryCache(); builder.Services.AddDbContextFactory<ApplicationDbContext>(options => options.UseSqlServer(connString)); builder.Services.AddServerSideBlazor().AddCircuitOptions(options => { options.DetailedErrors = true; }); builder.Services.AddSignalRCore(); builder.Services.AddScoped<ICertificateValidationService, CertificateValidationService>(); builder.Services.AddScoped<IUserManagement, UserManagement>(); builder.Services.AddAuthentication( CertificateAuthenticationDefaults.AuthenticationScheme) .AddCertificate(options => { options.AllowedCertificateTypes = CertificateTypes.All; options.Events = new CertificateAuthenticationEvents { OnCertificateValidated = context => { var validationService = context.HttpContext.RequestServices .GetRequiredService<ICertificateValidationService>(); if (validationService.ValidateCertificate(context.ClientCertificate)) { var claims = new[] { new Claim( ClaimTypes.NameIdentifier, context.ClientCertificate.Subject, ClaimValueTypes.String, context.Options.ClaimsIssuer), new Claim( ClaimTypes.Name, context.ClientCertificate.Subject, ClaimValueTypes.String, context.Options.ClaimsIssuer) }; context.Principal = new ClaimsPrincipal( new ClaimsIdentity(claims, context.Scheme.Name)); context.Success(); } return Task.CompletedTask; } }; }); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } else { app.UseDeveloperExceptionPage(); } app.UseAuthentication(); app.UseAuthorization(); app.UseSession(); app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.MapBlazorHub(); app.UseEndpoints(endpoints => { endpoints.MapHub<EditingNotificationHub>("/editingHub"); }); app.MapFallbackToPage("/_Host"); app.Run();
HomeComponent.razor修改后代码
protected override async Task OnInitializedAsync() { hubConnection = new HubConnectionBuilder().WithUrl(_navigationManager.ToAbsoluteUri("/editingHub"), options => { options.UseDefaultCredentials = true; options.Transports = HttpTransportType.WebSockets; options.HttpMessageHandlerFactory = (msg) => { if (message is HttpClientHandler clientHandler) { clientHandler.ServerCertificateCustomValidationCallback += (sender, certificate, chain, sslPolicyErrors) => { return true; }; return message; } return message; }; options.WebSocketConfiguration = wsc => wsc.RemoteCertificateValidationCallback = (sender, certificate, chain, policyErrors) => true; }).WithAutomaticReconnect().Build(); hubConnection.On<int, bool, int>("ReceiveMessage", (senderID, editingStatus, prid) => { notification = new EditingNotification() { UserID = senderID, Editing = editingStatus, PRID = prid }; if(prid == PRNum) { if (editingStatus) { userEditing = editingStatus; userEditingMessage = "is editing."; } else { userEditingMessage = "has finished editing, please reload the page."; } } InvokeAsync(StateHasChanged); }); await hubConnection.StartAsync(); }
事件查看器错误日志
Category: Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost EventId: 111 SpanId: 083e8e84ca56fc4b TraceId: dfa84b4238d669b811b5aca6e1cc31c7 ParentId: 0000000000000000 RequestId: 80002230-0001-c600-b63f-84710c7967bb RequestPath: /_blazor TransportConnectionId: z1Bby_rgcRzSoz45wN1wIg Unhandled exception in circuit 'NCCj9qdxaYD99Y9ZlZswPHWLazRLu_r5xk_4BA_RBP0'. Exception: System.Net.Http.HttpRequestException: Response status code does not indicate success: 403 (Forbidden). at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode() at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.NegotiateAsync(Uri url, HttpClient httpClient, ILogger logger, CancellationToken cancellationToken) at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.GetNegotiationResponseAsync(Uri uri, CancellationToken cancellationToken) at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.SelectAndStartTransport(TransferFormat transferFormat, CancellationToken cancellationToken) at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.StartAsyncCore(TransferFormat transferFormat, CancellationToken cancellationToken) at System.Threading.Tasks.ForceAsyncAwaiter.GetResult() at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.StartAsync(TransferFormat transferFormat, CancellationToken cancellationToken) at Microsoft.AspNetCore.Http.Connections.Client.HttpConnectionFactory.ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken) at Microsoft.AspNetCore.Http.Connections.Client.HttpConnectionFactory.ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken) at Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsyncCore(CancellationToken cancellationToken) at Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsyncInner(CancellationToken cancellationToken) at System.Threading.Tasks.ForceAsyncAwaiter.GetResult() at Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsync(CancellationToken cancellationToken) at WORKS.Pages.PurchaseRequestDetailsView.PurchaseRequestDetailsView.OnInitializedAsync() in C:\Users\613163\Desktop\WORKS\WORKS\Pages\PurchaseRequestDetailsView\PurchaseRequestDetailsView.razor:line 320 at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync() at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
目前不清楚遗漏了哪些配置或代码,寻求解决思路。
内容的提问来源于stack exchange,提问作者Syed Akbar
相关产品推荐
相关产品推荐

