You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor WASM使用Navigation Manager下载文件时用户Claims获取问题

问题解决:Blazor WASM中NavigationManager导出时传递授权令牌获取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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 06:33:27