gpt4 book ai didi

c# - 在 Asp.Net Core Web API 中使用 MassTransit 消息时如何对用户进行身份验证?

转载 作者:行者123 更新时间:2023-12-04 11:33:07 26 4
gpt4 key购买 nike

我有几个使用承载身份验证的 Asp.Net Core Web API 和 IdentityServer4.AccessTokenValidation中间件来检查 token ,验证用户并创建声明。这适用于 HTTP 请求。

我正在将这些 API 配置为使用 RabbitMQ 作为传输的 MassTransit 端点(用于发布和使用消息)。我按照说明操作 here用于将 MassTransit 添加到 API 和设置消息使用者。典型的工作流程类似于:

对 API 的 HTTP 请求 > 在 MassTransit 上发布消息 > RabbitMQ > 在另一个 API 中使用的消息

我正在努力理解的是如何创建 ClaimsPrincipal当从总线上消费消息以便我知道代表哪个用户执行操作时?如果不是 HTTP 请求,则不会调用 AuthenticationHandler。

到目前为止我尝试过的:

我想我会通过在消息头中传递一个 token (和/或个人声明值)来解决这个问题。发布部分似乎很容易,因为 MassTransit allows adding any number of custom headers使用 MassTransit.PublishContextExecuteExtensions.Publish 发布消息时.这使我能够将带有标识用户的信息的消息发送到传输上,并且可以通过手动查看标题(例如)在消费者中查看此信息。

public class SomeEventConsumer : IConsumer<SomeEventData>
{
public async Task Consume(ConsumeContext<SomeEventData> context)
{
var token = context.Headers["token"];
}
}

此时,我可以获取 token 并手动调用 Identity Server 中的 Introspection 端点,但随后我需要:
  • 每次都在每个消费者中执行此操作,然后...
  • ...手动将这些信息传递给逻辑类等,而不是使用 IHttpContextAccessor.HttpContext.User.Claims或者通过包装声明并使用依赖注入(inject)。

  • 为了解决第 1 点,我创建了一个新的 custom middleware ...
    public class AuthenticationFilter<T> : IFilter<ConsumeContext<T>> where T : class
    {
    public void Probe(ProbeContext context)
    {
    var scope = context.CreateFilterScope("authenticationFilter");
    }

    public async Task Send(ConsumeContext<T> context, IPipe<ConsumeContext<T>> next)
    {
    var token = context.Headers.Where(x => x.Key == "token").Select(x => x.Value.ToString()).Single();

    // TODO: Call token introspection

    await next.Send(context);
    }
    }

    public class AuthenticationFilterSpecification<T> : IPipeSpecification<ConsumeContext<T>> where T : class
    {
    public void Apply(IPipeBuilder<ConsumeContext<T>> builder)
    {
    var filter = new AuthenticationFilter<T>();
    builder.AddFilter(filter);
    }

    public IEnumerable<ValidationResult> Validate()
    {
    return Enumerable.Empty<ValidationResult>();
    }
    }

    public class AuthenticationFilterConfigurationObserver : ConfigurationObserver, IMessageConfigurationObserver
    {
    public AuthenticationFilterConfigurationObserver(IConsumePipeConfigurator receiveEndpointConfigurator) : base(receiveEndpointConfigurator)
    {
    Connect(this);
    }

    public void MessageConfigured<TMessage>(IConsumePipeConfigurator configurator)
    where TMessage : class
    {
    var specification = new AuthenticationFilterSpecification<TMessage>();
    configurator.AddPipeSpecification(specification);
    }
    }

    public static class AuthenticationExtensions
    {
    public static void UseAuthenticationFilter(this IConsumePipeConfigurator configurator)
    {
    if (configurator == null)
    {
    throw new ArgumentNullException(nameof(configurator));
    }

    _ = new AuthenticationFilterConfigurationObserver(configurator);
    }
    }

    ...然后将其添加到管道中...
    IBusControl CreateBus(IServiceProvider serviceProvider)
    {
    return Bus.Factory.CreateUsingRabbitMq(cfg =>
    {
    cfg.Host("rabbitmq://localhost");
    cfg.UseAuthenticationFilter();
    // etc ...
    });
    }

    这就是我被困的地方。我不知道如何对请求范围内的用户进行身份验证。如果不是 HTTP 请求,我不确定这里的最佳实践是什么。任何建议或指示将不胜感激。谢谢...

    最佳答案

    我刚刚在 Pluralsight 上观看了 Kevin Dockx 类(class),该类(class)涵盖了 Azure 服务总线上的这种情况,但相同的原则适用于大众运输或使用消息总线的服务之间的任何其他异步通信。这是该部分的链接:Securing Microservices in ASP.NET Core
    Kevin 的技术是将访问 token (JWT) 作为属性包含在总线消息中,然后使用 IdentityModel 在使用者中进行验证。 .
    总结一下:
    在生产者中:

  • 从请求中获取访问 token (例如 HttpContext.GetUserAccessTokenAsync() )。
  • 在发送之前将其设置为消息中的属性。

  • 在消费者中:
  • 使用 IdentityModel获取 IdP 发现文档
  • 从发现响应中提取公共(public)签名 key (这些必须转换为 RsaSecurityKey )
  • 调用 JwtSecurityTokenHandler.ValidateToken()从消息中验证 JWT。这将返回 ClaimsPrincipal如果成功。

  • 如果您担心访问 token 过期,您可以利用消息入队的日期时间作为消费者中 token 验证逻辑的一部分。
    以下是验证器的工作方式(简化):
    var discoveryDocumentResponse = await httpClient.GetDiscoveryDocumentAsync("https://my.authority.com");

    var issuerSigningKeys = new List<SecurityKey>();

    foreach (var webKey in discoveryDocumentResponse.KeySet.Keys)
    {
    var e = Base64Url.Decode(webKey.E);
    var n = Base64Url.Decode(webKey.N);

    var key = new RsaSecurityKey(new RSAParameters
    { Exponent = e, Modulus = n })
    {
    KeyId = webKey.Kid
    };

    issuerSigningKeys.Add(key);
    }

    var tokenValidationParameters = new TokenValidationParameters()
    {
    ValidAudience = "my-api-audience",
    ValidIssuer = "https://my.authority.com",
    IssuerSigningKeys = issuerSigningKeys
    };

    var claimsPrincipal = new JwtSecurityTokenHandler().ValidateToken(tokenToValidate,
    tokenValidationParameters, out var rawValidatedToken);

    return claimsPrincipal;

    关于c# - 在 Asp.Net Core Web API 中使用 MassTransit 消息时如何对用户进行身份验证?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60064118/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com