Blazor WASM使用Navigation Manager下载文件时用户Claims获取问题
核心问题
使用NavigationManager.NavigateTo触发后端导出接口时,请求未携带授权令牌,导致后端无法通过User.Claims获取companyId进行数据过滤。
解决方案
方案1:将JWT令牌附加到URL查询参数(适合JWT认证场景)
通过Blazor WASM的IAccessTokenProvider获取当前访问令牌,将其安全附加到导出接口的URL中,并配置后端认证中间件从查询参数读取令牌。
步骤1:修改ExportService注入并获取令牌
using Microsoft.AspNetCore.Components.WebAssembly.Authentication; public class ExportService { private readonly IAccessTokenProvider _tokenProvider; private readonly NavigationManager _navigationManager; public ExportService(IAccessTokenProvider tokenProvider, NavigationManager navigationManager) { _tokenProvider = tokenProvider; _navigationManager = navigationManager; } public async Task Export(string table, string type, Query query = null) { var tokenResult = await _tokenProvider.RequestAccessToken(); if (tokenResult.TryGetToken(out var token)) { var baseUrl = query != null ? query.ToUrl($"/export/{table}/{type}") : $"/export/{table}/{type}"; // 安全编码令牌并附加到URL参数 var tokenParam = $"access_token={Uri.EscapeDataString(token.Value)}"; var finalUrl = baseUrl.Contains("?") ? $"{baseUrl}&{tokenParam}" : $"{baseUrl}?{tokenParam}"; _navigationManager.NavigateTo(finalUrl, true); } else { // 令牌获取失败,跳转至登录页 _navigationManager.NavigateTo("authentication/login"); } } }
步骤2:配置后端认证中间件支持查询参数令牌
在后端Program.cs中修改JWT认证配置,添加从查询参数提取令牌的逻辑:
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using System.Text; var builder = WebApplication.CreateBuilder(args); // ...其他服务配置 builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = builder.Configuration["Jwt:Issuer"], ValidAudience = builder.Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])) }; // 监听消息接收事件,仅对导出接口提取查询参数中的令牌 options.Events = new JwtBearerEvents { OnMessageReceived = context => { var accessToken = context.Request.Query["access_token"]; var requestPath = context.HttpContext.Request.Path; if (!string.IsNullOrEmpty(accessToken) && requestPath.StartsWithSegments("/export")) { context.Token = accessToken; } return Task.CompletedTask; } }; }); // ...其他中间件配置 var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); // ...路由配置 app.Run();
方案2:Cookie认证场景下确保请求携带Cookie(同域环境)
如果项目使用ASP.NET Core Identity Cookie认证,同域下NavigationManager.NavigateTo发起的请求默认会自动携带Cookie。若未携带,检查以下配置:
- 后端
Program.cs中启用Cookie认证并配置Cookie属性:builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.Cookie.SameSite = SameSiteMode.Lax; options.Cookie.SecurePolicy = CookieSecurePolicy.Always; // HTTPS环境下启用 options.Cookie.HttpOnly = true; }); - Blazor WASM的
Program.cs中配置正确的同域基地址:builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
后端代码优化
将控制器中返回null的逻辑替换为合法的HTTP响应:
[HttpGet("/export/expense-claims-admin/excel")] public IActionResult ExportExpenseClaimsToExcelAdmin() { var companyId = User.FindFirst("companyId"); if (companyId != null) { var filteredClaims = context.expense_claims .Where(x => x.DeleteDate == null && x.CompanyId == int.Parse(companyId.Value)); return ToExcel(ApplyQuery(filteredClaims, Request.Query)); } return Unauthorized("Missing companyId claim"); }
注意事项
- URL传递令牌存在安全风险,生产环境必须使用HTTPS,且令牌设置较短的有效期。
- 若导出文件较大,建议优化后端
ToExcel方法,采用分批次写入数据的方式避免内存溢出。
内容的提问来源于stack exchange,提问作者Hannah Hayes
相关产品推荐
相关产品推荐

