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

.NET 8 MAUI调用JWT认证Web API出现401未授权问题求助

Blazor .NET MAUI调用JWT授权的.NET 8 Web API返回401 Unauthorized问题排查

问题场景

我有一个采用JWT授权的.NET Core 8.0 Web API,Postman或Swagger登录获取Token后能正常访问受保护端点,但Blazor .NET MAUI客户端获取到相同Token后,调用GET请求返回401 Unauthorized。客户端已在请求头添加Token,但认证失败。

客户端请求代码

protected async Task OnGet()
{
    string? userDetailsStr = await SecureStorage.GetAsync(nameof(UserBasicDetail));

    if (!string.IsNullOrWhiteSpace(userDetailsStr))
    {
        var userBasicDetail = JsonSerializer.Deserialize<UserBasicDetail>(userDetailsStr);
        string? token = userBasicDetail!.AccessToken;

        // Create the HTTP client using the API named factory
        var httpClient = HttpClientFactory?.CreateClient("API");

        //httpClient!.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
        httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");

        // Execute the GET operation and store the response, the empty parameter
        // in GetAsync doesn't modify the base address set in the client factory 
        using HttpResponseMessage response = await httpClient.GetAsync("api/Paciente");


        // If the operation is successful deserialize the results into the data model
        if (response.IsSuccessStatusCode)
        {
            using var contentStream = await response.Content.ReadAsStreamAsync();
            Pacientes = await JsonSerializer.DeserializeAsync<IEnumerable<Paciente>>(contentStream);
        }
    }
}

HTTP响应头

{StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:
{
Date: Wed, 17 Apr 2024 15:13:46 GMT
Server: Kestrel
WWW-Authenticate: Bearer
Content-Length: 0
}}

API端日志

2024-04-17 11:42:46.5686|9|DEBUG|Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler|AuthenticationScheme: Bearer was not authenticated.
2024-04-17 11:42:46.5686|0|DEBUG|Microsoft.AspNetCore.Authorization.AuthorizationMiddleware|Policy authentication schemes did not succeed
2024-04-17 11:42:46.5686|2|INFO|Microsoft.AspNetCore.Authorization.DefaultAuthorizationService|Authorization failed. These requirements were not met:
RolesAuthorizationRequirement:User.IsInRole must be true for one of the following roles: (User|Admin)

自定义GetAuthenticationStateAsync实现

public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
    try
    {
        // Verifica se há informações de usuário armazenadas
        var userInfo = await SecureStorage.GetAsync(nameof(UserBasicDetail));

        if (string.IsNullOrEmpty(userInfo))
        {
            // Se não houver informações de usuário, retorna um estado de autenticação nulo
            return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
        }

        // Desserializa as informações do usuário
        var userBasicDetail = JsonSerializer.Deserialize<UserBasicDetail>(userInfo);

        // Cria claims para o usuário com base nas informações obtidas
        var claims = new List<Claim>
        {
            new(ClaimTypes.Name, userBasicDetail!.Email!),
            new(ClaimTypes.Role, userBasicDetail.Role!)
            // Adicione outras claims conforme necessário
        };

        // Cria um objeto ClaimsIdentity com as claims do usuário
        var identity = new ClaimsIdentity(claims, "Bearer");

        // Retorna o estado de autenticação com o usuário autenticado
        return new AuthenticationState(new ClaimsPrincipal(identity));
    }
    catch (Exception ex)
    {
        // Em caso de erro, lança uma exceção ou trata conforme necessário
        throw new ApplicationException("Erro ao obter o estado de autenticação", ex);
    }
}

排查与解决步骤

  • 核对Token角色Claim与API配置:从日志看,授权失败核心原因是角色不匹配。需确认Web API的JWT配置中,角色Claim的类型和Token实际携带的一致。比如Token里的角色Claim是role,但API默认使用ClaimTypes.Role(即http://schemas.microsoft.com/ws/2008/06/identity/claims/role),就会导致角色识别失败。可在API的JWT配置中指定RoleClaimType:
    options.TokenValidationParameters.RoleClaimType = "role"; // 匹配Token中的角色Claim名称
    
  • 验证Token完整性:在客户端发送请求前,输出token变量的完整内容,和Postman中可用的Token逐字符对比,排查是否存在SecureStorage读取、序列化/反序列化过程中的截断、空格或编码问题。
  • 修正请求头设置方式:避免直接用Add方法添加Authorization头,改用AuthenticationHeaderValue防止重复添加或格式错误:
    httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
    
  • 解析Token确认权限:用JWT解析工具查看Token的Payload,确认roles(或对应Claim)包含User/Admin,同时检查Token是否过期、签名是否有效。
  • 排除客户端认证状态方法影响:自定义的GetAuthenticationStateAsync是Blazor客户端本地的认证状态管理,和API端的JWT认证无关,不会影响API的授权结果,但需确保客户端存储的Token与发送的一致。
  • 检查API授权策略:确认受保护端点的[Authorize(Roles = "User,Admin")]配置正确,同时API的JWT中间件已正确启用认证和授权中间件顺序(先认证后授权)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 18:04:53