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

Blazor Webassembly非标准Bearer令牌授权与请求头访问咨询

问题结论

直接在客户端Blazor(WebAssembly模式)的CustomAuthenticationStateProvider中访问初始HTTP请求头不可行。Blazor WASM运行在浏览器沙箱环境中,没有权限读取首次加载应用时的原始HTTP请求头集合,不需要硬在AuthenticationStateProvider里找访问请求头的方法,换轻量实现路径即可,全程不需要接入完整OIDC或ASP.NET Core原生安全体系,完全匹配不需要做令牌校验的需求。


实现步骤

1. 应用入口层提取请求头令牌

根据Blazor部署模式选择对应方式拿到初始请求里的MyCustomJWTBearer令牌:

  • 如果是ASP.NET Core托管的Blazor WASM:在主机页面_Host.cshtml渲染根组件时,直接从当前请求头读取令牌,作为组件参数传入Blazor应用:
    <component type="typeof(App)" render-mode="WebAssemblyPrerendered" 
               param-InitialToken="Context.Request.Headers["MyCustomJWTBearer"].FirstOrDefault()" />
    
  • 如果是独立部署的Blazor WASM:在前置代理/网关层(Nginx、APISIX、CDN等)返回index.html时,动态将请求头里的MyCustomJWTBearer值注入到页面全局JS变量中,示例:
    <script>
      window.__INITIAL_TOKEN__ = "代理层读取到的MyCustomJWTBearer令牌值";
    </script>
    
  • 如果是Blazor Server模式:直接注入IHttpContextAccessor即可读取请求头,但注意SignalR连接建立后HttpContext不会持续存在,令牌必须在应用初始化时一次性读取存储,不能每次认证状态查询都从HttpContext取。

2. 实现内存令牌存储服务

注册一个单例服务统一管理令牌和解析后的声明,不需要做任何令牌有效性、发行方校验,仅做存储和基础的JWT payload解析:

public class AppTokenStore
{
    private string? _currentToken;
    private IReadOnlyList<Claim>? _currentClaims;

    /// <summary>
    /// 应用初始化时写入令牌
    /// </summary>
    public void SetToken(string token)
    {
        _currentToken = token;
        _currentClaims = ParseJwtClaims(token).ToList().AsReadOnly();
        TokenChanged?.Invoke();
    }

    /// <summary>
    /// 获取当前存储的原始令牌
    /// </summary>
    public string? GetRawToken() => _currentToken;

    /// <summary>
    /// 获取令牌解析后的所有声明
    /// </summary>
    public IReadOnlyList<Claim> GetClaims() => _currentClaims ?? Array.Empty<Claim>();

    /// <summary>
    /// 令牌变更事件,用于触发认证状态刷新
    /// </summary>
    public event Action? TokenChanged;

    // 仅解析JWT Payload,不做签名、有效期校验
    private IEnumerable<Claim> ParseJwtClaims(string jwt)
    {
        var payload = jwt.Split('.')[1];
        var jsonBytes = DecodeBase64Url(payload);
        var claims = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonBytes) 
                     ?? new Dictionary<string, object>();
        return claims.Select(kv => new Claim(kv.Key, kv.Value?.ToString() ?? string.Empty));
    }

    private byte[] DecodeBase64Url(string base64Url)
    {
        var base64 = base64Url.Replace('-', '+').Replace('_', '/');
        base64 += (base64.Length % 4) switch
        {
            2 => "==",
            3 => "=",
            _ => string.Empty
        };
        return Convert.FromBase64String(base64);
    }
}

在Program.cs中注册服务:

builder.Services.AddSingleton<AppTokenStore>();

3. 应用启动时初始化令牌

如果是托管模式传参的方式,在App.razor中接收初始令牌参数,写入存储服务:

@inject AppTokenStore TokenStore

<Router AppAssembly="@typeof(App).Assembly">
    <!-- 原有路由配置保持不变 -->
</Router>

@code {
    [Parameter]
    public string? InitialToken { get; set; }

    protected override void OnInitialized()
    {
        if (!string.IsNullOrWhiteSpace(InitialToken))
        {
            TokenStore.SetToken(InitialToken);
        }
    }
}

如果是代理注入JS全局变量的方式,在Program.cs启动应用前通过JS互操作读取全局变量,写入AppTokenStore即可。

4. 改造自定义认证状态提供器

CustomAuthenticationStateProvider不需要直接访问请求头,直接依赖AppTokenStore拿到解析好的声明生成认证状态即可:

public class CustomAuthenticationStateProvider : AuthenticationStateProvider
{
    private readonly AppTokenStore _tokenStore;

    public CustomAuthenticationStateProvider(AppTokenStore tokenStore)
    {
        _tokenStore = tokenStore;
        // 令牌变更时自动通知UI刷新认证状态
        _tokenStore.TokenChanged += () =>
        {
            NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
        };
    }

    public override Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        var claims = _tokenStore.GetClaims();
        var identity = claims.Any() 
            ? new ClaimsIdentity(claims, "CustomHeaderAuth") 
            : new ClaimsIdentity();
        var principal = new ClaimsPrincipal(identity);
        return Task.FromResult(new AuthenticationState(principal));
    }
}

在Program.cs中注册认证相关服务,之后就可以正常使用<AuthorizeView>、[Authorize]特性控制页面元素的显示隐藏:

builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthenticationStateProvider>();

5. 配置HttpClient自动携带令牌

实现一个自定义消息处理器,所有发往外站的请求自动从存储中读取令牌,添加到MyCustomJWTBearer请求头:

public class CustomTokenHeaderHandler : DelegatingHandler
{
    private readonly AppTokenStore _tokenStore;

    public CustomTokenHeaderHandler(AppTokenStore tokenStore)
    {
        _tokenStore = tokenStore;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var token = _tokenStore.GetRawToken();
        if (!string.IsNullOrWhiteSpace(token))
        {
            request.Headers.TryAddWithoutValidation("MyCustomJWTBearer", token);
        }
        return await base.SendAsync(request, cancellationToken);
    }
}

注册HttpClient时挂载该处理器:

builder.Services.AddScoped<CustomTokenHeaderHandler>();
builder.Services.AddScoped(sp =>
{
    var handler = sp.GetRequiredService<CustomTokenHeaderHandler>();
    handler.InnerHandler = new HttpClientHandler();
    return new HttpClient(handler)
    {
        BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
    };
});

注意:如果后续有令牌刷新需求,只需要调用AppTokenStore.SetToken()传入新令牌,UI认证状态和后续HttpClient请求会自动同步更新,不需要额外写刷新逻辑。

内容的提问来源于stack exchange,提问作者Adam

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 07:27:21