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

Blazor WASM 如何对接自定义JWT认证API实现身份验证

核心问题

你项目模板默认带的AddOidcAuthentication是为标准OpenID Connect身份提供商设计的,它会自动请求OIDC发现文档、走OAuth2授权码流程,和你当前实现的「用户名密码提交直接返回JWT」的自定义登录接口协议完全不兼容,仅修改Authority字段不可能正常对接。

最简实现方案

不需要强行适配OIDC框架,直接用Blazor提供的自定义认证状态提供者对接现有接口即可,所有原生授权组件(AuthorizeView、[Authorize]特性等)都可以正常使用,步骤如下:

  • 清理默认OIDC配置
    直接删除Program.cs中builder.Services.AddOidcAuthentication(...)的整段代码,不需要保留相关配置,appsettings.json里的OIDC节点也可以删掉。
  • 安装本地存储依赖
    引入Blazored.LocalStorage包,用于在浏览器本地持久化存储JWT和刷新令牌,避免页面刷新后登录状态丢失。
  • 实现自定义认证状态提供者
    新建类继承AuthenticationStateProvider,实现登录、登出、认证状态读取逻辑,参考代码:
public class CustomAuthStateProvider : AuthenticationStateProvider
{
    private readonly HttpClient _httpClient;
    private readonly ILocalStorageService _localStorage;
    private readonly ClaimsPrincipal _anonymousUser = new(new ClaimsIdentity());

    public CustomAuthStateProvider(HttpClient httpClient, ILocalStorageService localStorage)
    {
        _httpClient = httpClient;
        _localStorage = localStorage;
    }

    public override async Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        var savedJwt = await _localStorage.GetItemAsync<string>("jwt_token");
        if (string.IsNullOrWhiteSpace(savedJwt))
        {
            return new AuthenticationState(_anonymousUser);
        }
        // 可在此处添加JWT过期校验,过期则自动调用刷新令牌接口,刷新失败则清空本地存储返回匿名状态
        var claims = ParseJwtClaims(savedJwt);
        var identity = new ClaimsIdentity(claims, "custom_jwt");
        return new AuthenticationState(new ClaimsPrincipal(identity));
    }

    public async Task Login(string username, string password)
    {
        var loginResponse = await _httpClient.PostAsJsonAsync("api/account/login", new UserLogin
        {
            Username = username,
            Password = password
        });

        if (!loginResponse.IsSuccessStatusCode)
        {
            throw new UnauthorizedAccessException("用户名或密码错误");
        }

        var tokenResult = await loginResponse.Content.ReadFromJsonAsync<LoginResponse>();
        await _localStorage.SetItemAsync("jwt_token", tokenResult.JWT);
        await _localStorage.SetItemAsync("refresh_token", tokenResult.RefreshToken);
        
        // 通知框架认证状态变更
        NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
    }

    public async Task Logout()
    {
        await _localStorage.RemoveItemAsync("jwt_token");
        await _localStorage.RemoveItemAsync("refresh_token");
        NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
    }

    private IEnumerable<Claim> ParseJwtClaims(string jwt)
    {
        var payload = jwt.Split('.')[1];
        var jsonBytes = DecodeBase64WithoutPadding(payload);
        var claimDict = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonBytes);
        return claimDict.Select(kv => new Claim(kv.Key, kv.Value.ToString()));
    }

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

public class LoginResponse
{
    public string JWT { get; set; }
    public string RefreshToken { get; set; }
}
  • 注册相关服务
    在Program.cs中添加服务注册,同时配置HttpClient自动在请求头携带JWT:
builder.Services.AddBlazoredLocalStorage();
builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<CustomAuthStateProvider>();
builder.Services.AddScoped<AuthenticationStateProvider>(sp => sp.GetRequiredService<CustomAuthStateProvider>());

// 配置HttpClient,建议额外实现DelegatingHandler统一处理请求头注入、401响应跳转登录逻辑
builder.Services.AddScoped(sp =>
{
    return new HttpClient
    {
        BaseAddress = new Uri("你的后端API根地址")
    };
});
  • 替换默认登录UI
    把模板自带的OIDC登录跳转按钮替换为自定义登录表单,表单提交时调用CustomAuthStateProvider的Login方法即可,原有的授权判断逻辑不需要修改。

补充说明

如果硬要使用默认OIDC框架对接,你需要在后端额外实现完整的OIDC协议端点,包括发现文档端点、授权端点、令牌端点、用户信息端点等,工作量远大于上述自定义方案,没有实际必要。

  • 生产环境建议打开JWT的Issuer、Audience校验,避免令牌被伪造
  • 记得实现刷新令牌的自动轮换逻辑,减少用户重复登录的频率
  • 推荐单独实现DelegatingHandler统一处理令牌注入、令牌过期刷新、未授权响应跳转逻辑,不要在每个接口请求里重复处理

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 02:54:26