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

ASP.NET Core 6微服务中RabbitMQ RPC获取登录用户名失败问题

问题描述

我有两个微服务:

  • 负责授权认证的Identity Service(运行在localhost:7096,基于Identity系统配置)
  • 为已认证用户提供资源的Application Service(运行在localhost:7141)

当通过RabbitMQ RPC模式从Identity Service获取已登录用户的用户名时出现异常:

  • Identity Service提供get-logged-in-user-name端点,通过自定义扩展方法ClaimPrincipleExtension.GetUsername获取当前登录用户的用户名
  • 直接调用该端点功能正常,但通过Application Service的RabbitMQ消费者发起HTTP请求调用时抛出异常
  • 若在端点中返回硬编码字符串(如"ExampleUser")则一切正常

相关代码

RabbitMQ消费者代码

private async void ConsumerReceived(object sender, BasicDeliverEventArgs e)
{
        var response = "";

        var body = e.Body.ToArray();
        var props = e.BasicProperties;
        var replyProps = _channel.CreateBasicProperties();
        replyProps.CorrelationId = props.CorrelationId;

        try
        {
            var wee = _serviceProvider.GetService<IHttpClientFactory>();

            var client = wee.CreateClient();

            var loggedUserName = await client.GetAsync("https://localhost:7096/get-logged-in-user-name");

            response = await loggedUserName.Content.ReadAsStringAsync();
        }
        catch (Exception E)
        {
            Console.WriteLine(E);
            response = "null";
        }
        finally
        {
            var responseBytes = Encoding.UTF8.GetBytes(response);
            _channel.BasicPublish(exchange: "",
                routingKey: props.ReplyTo,
                basicProperties: replyProps,
                body: responseBytes);

            _channel.BasicAck(deliveryTag: e.DeliveryTag,
                multiple: false);
        }
}

扩展方法代码

public static class ClaimPrincipleExtension
{
    public static string GetUsername(this ClaimsPrincipal user)
    {
        return user.FindFirst(ClaimTypes.Name).Value;
    }
}

端点代码

[HttpGet("get-logged-in-user-name")]
public string GetLoggedInUserName()
{
        string username = User.GetUsername(); //!< -获取当前登录用户名
        return username; //!< -在此处触发RPC服务端与客户端错误
        // return "ExampleUser"; //! <-返回该硬编码值则一切正常
}

错误堆栈信息

at Shared.Abstraction.Extensions.ClaimPrincipleExtension.GetUsername(ClaimsPrincipal user) in /Users/michal/Desktop/MyDateApp/Shared/Extensions/ClaimPrincipleExtension.cs:line 10
at IdentityServer.Application.Controllers.UserController.GetLoggedInUserName() in /Users/michal/Desktop/MyDateApp/MicroServices/IdentityServer/IdentityServer.Application/Controllers/UserController.cs:line 41
at lambda_method3(Closure , Object , Object[] )
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

请求头

Host: localhost:7096

问题分析与解决方案

核心原因

从错误堆栈可知,异常发生在ClaimPrincipleExtension.GetUsername方法第10行:user.FindFirst(ClaimTypes.Name)返回null,访问.Value时触发空引用异常。

直接调用端点正常,是因为浏览器/客户端请求携带了认证令牌(如JWT Cookie或Authorization头),Identity Service能识别用户并填充ClaimsPrincipal;而RabbitMQ消费者发起的HTTP请求未携带任何认证信息,Identity Service无法识别用户,User对象不存在ClaimTypes.Name声明。

解决方案

  1. 在HTTP请求中携带认证令牌
    当Application Service发送RabbitMQ RPC请求时,需将用户的认证令牌(如原始请求中的JWT)作为消息体传递给消费者。消费者拿到令牌后,在发起HTTP请求时添加Authorization头:

    // 从RabbitMQ消息体解析用户JWT令牌
    var token = Encoding.UTF8.GetString(body);
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
    var loggedUserName = await client.GetAsync("https://localhost:7096/get-logged-in-user-name");
    
  2. 优化扩展方法,添加空值检查
    修改ClaimPrincipleExtension.GetUsername方法,避免空引用异常,同时给出明确错误提示:

    public static class ClaimPrincipleExtension
    {
        public static string GetUsername(this ClaimsPrincipal user)
        {
            var nameClaim = user.FindFirst(ClaimTypes.Name);
            if (nameClaim == null)
            {
                throw new InvalidOperationException("当前用户未包含用户名声明");
                // 或返回默认值:return string.Empty;
            }
            return nameClaim.Value;
        }
    }
    
  3. 为端点添加授权策略
    确保get-logged-in-user-name端点添加授权特性,未认证请求直接返回401,避免走到获取用户名的逻辑:

    [HttpGet("get-logged-in-user-name")]
    [Authorize] // 添加授权特性
    public string GetLoggedInUserName()
    {
        string username = User.GetUsername();
        return username;
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 09:07:35