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

2024-01-12

我有几个使用 Bearer 身份验证的 Asp.Net Core Web APIIdentityServer4.AccessTokenValidation用于内省令牌、验证用户身份并创建声明的中间件。这对于 HTTP 请求来说效果很好。

我正在将这些 API 配置为使用 RabbitMQ 作为传输的 MassTransit 端点(用于发布和消费消息)。我按照说明进行操作here https://masstransit-project.com/usage/configuration.html#asp-net-core用于将 MassTransit 添加到 API 以及设置消息使用者。典型的工作流程类似于:

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

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

到目前为止我尝试过的:

我想我可以通过在消息头中传递令牌(和/或单独的声明值)来解决这个问题。发布部分看起来很容易,就像 MassTransit 一样允许添加任意数量的自定义标头 https://masstransit-project.com/usage/producers.html#headers发布消息时使用MassTransit.PublishContextExecuteExtensions.Publish。这使我能够将包含标识用户的信息的消息发送到传输上,并且可以通过手动查看标头在消费者中查看此信息,例如

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

此时,我可以获取令牌并手动调用 Identity Server 中的内省端点,但随后我需要:

  1. 每次对每个消费者都这样做......
  2. ...将该信息手动传递给逻辑类等,而不是使用IHttpContextAccessor.HttpContext.User.Claims或者通过包装声明并使用依赖注入。

为了解决第 1 点,我创建了一个新的自定义中间件 https://masstransit-project.com/advanced/middleware/custom.html ...

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 课程,该课程涵盖了 Azure 服务总线上的这一场景,但相同的原理也适用于公共交通或使用消息总线的服务之间的任何其他异步通信。这是该部分的链接:保护 ASP.NET Core 中的微服务 https://app.pluralsight.com/course-player?clipId=08a60434-9799-4c7d-a335-686857fa16e6

Kevin 的技术是将访问令牌 (JWT) 作为总线消息的属性包含在内,然后使用IdentityModel.

总结一下:

在生产者中:

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

在消费者中:

  1. Use IdentityModel获取 IdP 发现文档
  2. 从发现响应中提取公共签名密钥(这些必须转换为RsaSecurityKey)
  3. Call JwtSecurityTokenHandler.ValidateToken()验证消息中的 JWT。这会返回一个ClaimsPrincipal如果成功的话。

如果您担心访问令牌过期,可以利用消息排队的日期时间作为使用者中令牌验证逻辑的一部分。

验证器的工作原理如下(简化):

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;
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Asp.Net Core Web API 中使用 MassTransit 消息时如何对用户进行身份验证? 的相关文章

  • C# 静态类型不能用作参数

    public static void SendEmail String from String To String Subject String HTML String AttachmentPath null String Attachme
  • 如何使用 openSSL 函数验证 PEM 证书的密钥长度

    如何验证以这种方式生成的 PEM 证书的密钥长度 openssl genrsa des3 out server key 1024 openssl req new key server key out server csr cp server
  • C# 中的 Stack<> 实现

    我最近一直在实现递归目录搜索实现 并且使用堆栈来跟踪路径元素 当我使用 string Join 连接路径元素时 我发现它们被颠倒了 当我调试该方法时 我查看了堆栈 发现堆栈内部数组中的元素本身是相反的 即最近 Push 的元素位于内部数组的
  • 在 C 语言中,为什么数组的地址等于它的值?

    在下面的代码中 指针值和指针地址与预期不同 但数组值和地址则不然 怎么会这样 Output my array 0022FF00 my array 0022FF00 pointer to array 0022FF00 pointer to a
  • 如何在 C# 中将 Json 转换为对象

    我想将 Json 转换为 C 中的对象 这里的 Json 是 值 e920ce0f e3f5 4c6f 8e3d d2fbc51990e4 如何使用 Object 问题看似愚蠢 但其实并不那么愚蠢 我没有简单的 Json 我有 IEnume
  • 如何修复错误:“检测到无法访问的代码”

    我有以下代码 private string GetAnswer private int CountLeapYears DateTime startDate return count String answer GetAnswer Respo
  • Makefile 和 .Mak 文件 + CodeBlocks 和 VStudio

    我对整个 makefile 概念有点陌生 所以我对此有一些疑问 我正在 Linux 中使用 CodeBlocks 创建一个项目 我使用一个名为 cbp2mak 的工具从 CodeBlocks 项目创建一个 make 文件 如果有人知道更好的
  • if constexpr 中的 not-constexpr 变量 – clang 与 GCC

    struct A constexpr operator bool const return true int main auto f auto v if constexpr v A a f a clang 6 接受该代码 GCC 8 拒绝它
  • JavaScript 错误:MVC2 视图中的条件编译已关闭

    我试图在 MVC2 视图页面中单击时调用 JavaScript 函数 a href Select a JavaScript 函数 function SelectBenefit id code alert id alert code 这里 b
  • Unity手游触摸动作不扎实

    我的代码中有一种 错误 我只是找不到它发生的原因以及如何修复它 我是统一的初学者 甚至是统一的手机游戏的初学者 我使用触摸让玩家从一侧移动到另一侧 但问题是我希望玩家在手指从一侧滑动到另一侧时能够平滑移动 但我的代码还会将玩家移动到您点击的
  • Unity c# 四元数:将 y 轴与 z 轴交换

    我需要旋转一个对象以相对于现实世界进行精确旋转 因此调用Input gyro attitude返回表示设备位置的四元数 另一方面 这迫使我根据这个四元数作为默认旋转来计算每个旋转 将某些对象设置为朝上的简单方法如下 Vector3 up I
  • 对于 C# Express 用户来说,有哪些好的工具可以识别可能重复的代码? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 也可以看看 有什么工具可以检查重复的 VB NET 代码吗 https stackoverflow c
  • 当Model和ViewModel一模一样的时候怎么办?

    我想知道什么是最佳实践 我被告知要始终创建 ViewModel 并且永远不要使用核心模型类将数据传递到视图 这就说得通了 让我把事情分开 但什么是Model 和ViewModel一模一样 我应该重新创建另一个类还是只是使用它 我觉得我应该重
  • Unity3D - 将 UI 对象移动到屏幕中心,同时保持其父子关系

    我有一个 UI 图像 它的父级是 RectTransform 容器 该容器的父级是 UI 面板 而 UI 面板的父级是 Canvas 我希望能够将此 UI 图像移动到屏幕中心 即画布 同时保留父级层次结构 我的目标是将 UI 图像从中心动画
  • 如何在C#中控制datagridview光标移动

    我希望 datagridview 光标向右移动到下一列 而不是在向单元格输入数据后移动到下一行 我试图通过 dataGridView1 KeyDown 事件捕获键来控制光标 但这并不能阻止光标在将数据输入到单元格后移动到下一行 提前感谢你的
  • 如何编写一个接受 int 或 float 的 C 函数?

    我想用 C 语言创建一个扩展 Python 的函数 该函数可以接受 float 或 int 类型的输入 所以基本上 我想要f 5 and f 5 5 成为可接受的输入 我认为我不能使用if PyArg ParseTuple args i v
  • 在 C# 的 WebAPI 中的 ApiController 上使用“传输编码:分块”提供数据

    我需要服务分块传输使用编码数据API控制器 因为我无权访问HttpContext or the Http请求 我有点不知道在哪里写入响应以及在哪里刷新它 设置如下 public class MyController ApiControlle
  • 如何组合两个 lambda [重复]

    这个问题在这里已经有答案了 可能的重复 在 C 中组合两个 lambda 表达式 https stackoverflow com questions 1717444 combining two lamba expressions in c
  • 如果找不到指定的图像文件,显示默认图像的最佳方式?

    我有一个普通的电子商务应用程序 我将 ITEM IMAGE NAME 存储在数据库中 有时经理会拼错图像名称 为了避免 丢失图像 IE 中的红色 X 每次显示产品列表时 我都会检查服务器中是否有与该产品相关的图像 如果该文件不存在 我会将其
  • 嵌入式linux编写AT命令

    我在向 GSM 模块写入 AT 命令时遇到问题 当我使用 minicom b 115200 D dev ttySP0 term vt100 时它工作完美 但我不知道如何在 C 代码中做同样的事情 我没有收到任何错误 但模块对命令没有反应 有

随机推荐

  • 无法从 eclipse 运行 Tomcat 7

    我正在使用 Eclipse EE Helios 我想将我创建的项目部署到 Tomcat 7 我已经下载了Tomcat并创建了动态项目 我打开了Server View在 Eclipse 中 我按下New在 Eclipse 中定义一个新的 To
  • 如何按定义的顺序迭代 Python 字典?

    我试图迭代以特定顺序定义的字典 但它总是以与我在代码中定义的顺序不同的顺序进行迭代 这只是我正在尝试做的事情的一个基本示例 我正在迭代的字典更大 具有更复杂的命名键 并且不按字母 数字顺序排列 level lookup PRIORITY 1
  • 需要将光标置于文本区域中文本的开头

    我体内有这个并且有效 onLoad document forms post message focus 但我需要将光标放在textarea在任何现有文本的开头 而不是结尾 这就把它放在最后了 function moveCaretToStar
  • 布尔注释导致重复?

    我正在尝试实现一个基于外键表的基本 收藏夹 系统 假设我有以下简单模型 class Item models Model id models IntegerField class User models Model id models Int
  • 如何在 JavaScript 中复制 div

    我想知道如何复制DIV通过 JavaScript 多次编辑元素 而不在我的 html 代码中复制 DIV 假设您选择了 div 执行以下操作 var myDiv document getElementById myDivId DOM API
  • Twitter Bootstrap 使用 JSON 进行预输入

    我如何将 Twitter Bootstrap Typeahead 与 JSON 结合使用 我的 Bootstrap 版本是2 2 1 我的 JSON 响应 label Sistemski Administrator value 1 labe
  • 完全滑动 UITableViewCell 删除 UITableView iOS 8

    我想模仿 UITableViewCell 的滑动删除功能 就像 iOS 8 中的邮件应用程序一样 我不是指滑动以显示删除按钮 我指的是当你滑动时 它会显示 3 个操作 但如果你继续向左滑动 电子邮件就会被删除 在 iOS 8 中 UITab
  • Ionic - 仅在具体页面中显示侧面菜单

    我正在使用 Ionic Framework 开发一个应用程序 我只想在一些具体视图中显示侧面菜单 而不是在每个视图中 我有我的菜单 html file
  • ORA-12519,TNS:未找到适当的服务处理程序

    我正在正确关闭休眠连接 但在几次点击或刷新一些页面后我收到此错误 我不明白有什么问题 我还检查了会话数量 它也通过以下命令增加 并且还给出了程序代码 托管bean代码 public List
  • Whatsapp 身份验证如何运作?

    我想开发一个移动应用程序并使用类似whatsapp的用户注册 现在我记得几年前讨论过的安全问题 Whatsapp 过去仅通过电话号码和 IMEI 来验证用户身份 当然 这并不是真正安全 但我真的不知道如何更安全 现在我已经很长一段时间没有听
  • 调用 python webbrowser 时抑制/重定向 stderr

    我有一个 python 程序 它在新的浏览器窗口中的单独选项卡中打开多个网址 但是当我从命令行运行该程序并使用打开浏览器时 webbrowser open new url Firefox 的 stderr 打印到 bash 查看文档我似乎找
  • 术语的含义 - 资源获取就是初始化

    我知道 RAII 是做什么的 这都是为了在代码抛出异常时防止内存泄漏等 现在我想明白这句话的意思smart term http en wikipedia org wiki 收购 http en wikipedia org wiki Acqu
  • 如何在点击按钮时发出小闪光?

    所以我正在制作一个有超过 100 个按钮的 Android 应用程序 但是你知道当你正常点击一个按钮时 当你不改变背景或任何东西时 它会闪烁橙色 然而 由于我已经为按钮添加了背景颜色 当点击它们时 它只会进入下一个屏幕 并且您无法看出您已经
  • 在Monaco编辑器中,如何从IEditor界面获取选定的文本

    迷失在 API 的电报文档中 如何从 IEditor 界面获取选定的文本 这selection返回的对象getSelection只有行号和列号 那么我是否需要在编辑器的文本中搜索该文本 并且 与返回的所有其他选择相比 主要选择是什么getS
  • 为什么我的 ibtool 失败,退出代码为 255?

    突然间我无法构建我的项目 我收到以下编译器错误 Applications Xcode app Contents Developer Platforms iPhoneSimulator platform Developer usr bin i
  • LLVM 检索 AllocaInst 的名称

    我正在尝试检索传递给 a 的指针的名称cudaMalloc https docs nvidia com cuda cuda runtime api group CUDART MEMORY html group CUDART MEMORY 1
  • UI Router 从列表页面加载详细信息页面

    使用 ui router 的 AngularJS 应用程序 我的列表页面加载正确 但是当单击列表页面上的链接时 我的 url 发生了变化 但页面上的 html 没有变化 它仍然保留在列表页面上 这个路由有什么问题吗 app js var m
  • CGContext 擦除错误

    我不断收到此错误 Jan 31 13 56 51 Michaels MacBook Air local CocoaDrawing 2129
  • Spring加密和解密属性文件中的API密钥

    原始问题 我有一个位于 Tomcat 中的属性文件和一个位于 src test resources 中的用于测试的属性文件 目前我有以下设置 我的属性文件加载到我的 XML 文件中配置文件
  • 在 Asp.Net Core Web API 中使用 MassTransit 消息时如何对用户进行身份验证?

    我有几个使用 Bearer 身份验证的 Asp Net Core Web APIIdentityServer4 AccessTokenValidation用于内省令牌 验证用户身份并创建声明的中间件 这对于 HTTP 请求来说效果很好 我正