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

ASP.NET Core微服务间Token授权异常导致401未授权

问题描述

基于ASP.NET Core搭建微服务架构,包含两个核心服务:负责认证的AuthService和处理专业搜索的SearchService。用户通过AuthService完成认证后,SearchService需要利用请求头中的JWT令牌调用AuthService获取用户详情。但向SearchService发送GET请求时,SearchService调用AuthService会收到401未授权错误,AuthService日志显示“未找到Authorization头”。

关键代码实现

[Authorize]
[ApiController]
[Route("api/[controller]")]
public class ProfessionalSearchController : ControllerBase
{
    private readonly ILogger<ProfessionalSearchController> _logger;
    private readonly IHttpClientFactory _httpClientFactory;

    public ProfessionalSearchController(ILogger<ProfessionalSearchController> logger, IHttpClientFactory httpClientFactory)
    {
        _logger = logger;
        _httpClientFactory = httpClientFactory;
    }

    [HttpGet("search")]
    public async Task<IActionResult> SearchProfessionals([FromQuery] string specialisation)
    {
        var userId = User.FindFirst("uid")?.Value;

        if (userId == null)
        {
            _logger.LogWarning("User ID not found in token");
            return Unauthorized();
        }

        var patient = await GetUserById(userId);

        if (patient == null)
        {
            _logger.LogWarning("Patient not found");
            return NotFound("Patient not found.");
        }

        // Rest of the code...
    }

    private async Task<User> GetUserById(string userId)
    {
        var token = Request.Headers["Authorization"].ToString();

        if (string.IsNullOrEmpty(token))
        {
            _logger.LogWarning("No token provided.");
            return null;
        }

        var client = _httpClientFactory.CreateClient();
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", ""));

        var response = await client.GetAsync($"http://localhost:5203/api/User/{userId}");

        if (response.IsSuccessStatusCode)
        {
            return await response.Content.ReadFromJsonAsync<User>();
        }

        var errorContent = await response.Content.ReadAsStringAsync();
        _logger.LogWarning($"Failed to get user by ID {userId}, StatusCode: {response.StatusCode}, Error: {errorContent}");

        return null;
    }
}

已尝试的排查步骤

  • 验证令牌包含必要声明(包括角色)
  • 确认SearchService请求AuthService时Authorization头格式为Bearer {token}
  • 确保两个服务令牌验证参数(Issuer、Audience、签名密钥)完全一致
  • 通过Postman直接调用AuthService端点,使用同一令牌可正常运行

日志信息

System.Net.Http.HttpClient.Default.LogicalHandler: Information: 
Start processing HTTP request GET 
http://localhost:5203/api/User/b4e60ba8-79fa-4c33-a862- 
f60d06213d6b
System.Net.Http.HttpClient.Default.ClientHandler: Information: 
Sending HTTP request GET http://localhost:5203/api/User/b4e60ba8- 
79fa-4c33-a862-f60d06213d6b
BackendDocVillage.Startup: Information: Request Method: GET, Path: 
/api/User/b4e60ba8-79fa-4c33-a862-f60d06213d6b
BackendDocVillage.Startup: Information: Received Token: Bearer 
eyJhbGci..
BackendDocVillage.Startup: Information: Request Method: GET, Path: 
/api/User/b4e60ba8-79fa-4c33-a862-f60d06213d6b
BackendDocVillage.Startup: Information: No Authorization header 
found.
System.Net.Http.HttpClient.Default.ClientHandler: Information: 
Received HTTP response headers after 38.9651ms - 401
System.Net.Http.HttpClient.Default.LogicalHandler: Information: 
End processing HTTP request after 52.6424ms - 401
SearchService.Controllers.ProfessionalSearchController: Warning: 
Failed to get user by ID b4e60ba8-79fa-4c33-a862-f60d06213d6b, 
StatusCode: Unauthorized, Error: 
SearchService.Controllers.ProfessionalSearchController: Warning: 
Patient not found

两个服务均在本地运行:AuthService地址为http://localhost:5203,SearchService地址为http://localhost:5095,且均配置使用JWT Bearer认证。


解决方案

1. 检查HttpClient的自定义消息处理程序

如果SearchService的HttpClientFactory配置了自定义DelegatingHandler,可能会意外移除Authorization头。检查Program.cs或Startup.cs中的HttpClient注册代码,确保自定义处理程序不会修改或删除请求头。

2. 修正令牌提取逻辑

当前代码中token.Replace("Bearer ", "")存在格式风险,改用更可靠的提取方式:

private async Task<User> GetUserById(string userId)
{
    if (!Request.Headers.TryGetValue("Authorization", out var authHeaderValues))
    {
        _logger.LogWarning("No Authorization header provided.");
        return null;
    }

    var authHeader = authHeaderValues.FirstOrDefault();
    if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
    {
        _logger.LogWarning("Invalid Authorization header format.");
        return null;
    }

    var token = authHeader.Substring("Bearer ".Length).Trim();

    var client = _httpClientFactory.CreateClient();
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

    var response = await client.GetAsync($"http://localhost:5203/api/User/{userId}");

    if (response.IsSuccessStatusCode)
    {
        return await response.Content.ReadFromJsonAsync<User>();
    }

    var errorContent = await response.Content.ReadAsStringAsync();
    _logger.LogWarning($"Failed to get user by ID {userId}, StatusCode: {response.StatusCode}, Error: {errorContent}");

    return null;
}

3. 检查AuthService的CORS配置

如果AuthService启用了CORS,需确保允许接收Authorization头。在AuthService的Program.cs中配置CORS:

builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowSearchService",
        policy =>
        {
            policy.WithOrigins("http://localhost:5095")
                  .AllowAnyMethod()
                  .AllowAnyHeader();
        });
});

// 注意CORS中间件顺序:放在UseRouting之后,UseAuthorization之前
app.UseCors("AllowSearchService");

4. 启用HttpClient详细日志排查

在SearchService的appsettings.json中添加HttpClient调试日志,确认请求头是否正确发送:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "System.Net.Http": "Debug"
    }
  }
}

查看Debug级别的日志,确认Authorization头是否被包含在请求中。

5. 使用命名HttpClient替代默认客户端

注册命名客户端可避免全局配置影响请求,在SearchService的Program.cs中:

builder.Services.AddHttpClient("AuthServiceClient", client =>
{
    client.BaseAddress = new Uri("http://localhost:5203/");
});

在控制器中注入并使用:

private readonly HttpClient _authServiceClient;

public ProfessionalSearchController(ILogger<ProfessionalSearchController> logger, IHttpClientFactory httpClientFactory)
{
    _logger = logger;
    _authServiceClient = httpClientFactory.CreateClient("AuthServiceClient");
}

// 在GetUserById中调用
var response = await _authServiceClient.GetAsync($"api/User/{userId}");

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 21:58:10