30.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--单体转微服务--公共代码--用户上下文会话

在前面的文章中,我们会看到使用ContextSession来获取当前用户的UserIdUserName。这篇文章我们就一起来看看如何实现ContextSession

一、ContextSession的实现

我们在公共类库SP.Common中创建一个名为ContextSession的类,用于获取当前请求的用户信息。这个类依赖于IHttpContextAccessor,它允许我们访问当前HTTP请求的上下文。

using Microsoft.AspNetCore.Http;namespace SP.Common;/// <summary>
/// 上下文会话(用于保存当前请求的用户信息)
/// </summary>
public class ContextSession
{private readonly IHttpContextAccessor _httpContextAccessor;public ContextSession(IHttpContextAccessor httpContextAccessor){_httpContextAccessor = httpContextAccessor;}/// <summary>/// 获取当前请求的用户ID/// </summary>public long UserId{get{// UserId存储在Claims中var userIdClaim = _httpContextAccessor.HttpContext?.User?.FindFirst("UserId");if (userIdClaim != null && long.TryParse(userIdClaim.Value, out var userId)){return userId;}return 0;}}/// <summary>/// 获取当前请求的用户名/// </summary>public string UserName{get{// UserName存储在Claims中var userNameClaim = _httpContextAccessor.HttpContext?.User?.FindFirst("UserName");return userNameClaim?.Value ?? string.Empty;}}
}

在这个类中,我们通过IHttpContextAccessor访问当前HTTP上下文,并从中获取用户的ID和用户名。用户信息通常存储在Claims中,这样可以方便地在整个应用程序中使用。具体来说,ContextSession通过读取HttpContext.User中的Claims来获取用户信息。通常在用户登录认证成功后,系统会将用户的相关信息(如UserId、UserName等)以Claim的形式添加到ClaimsPrincipal中。这样,在后续的每个HTTP请求中,都可以通过ContextSession统一获取当前用户的身份信息,无需在各个业务模块中重复解析。

这种做法有以下优点:

  • 解耦业务逻辑与认证实现:业务代码无需关心认证的具体实现细节,只需通过ContextSession获取用户信息即可。
  • 便于测试和维护:通过依赖注入IHttpContextAccessor,可以方便地进行单元测试和Mock。
  • 统一管理用户上下文:所有需要获取当前用户信息的地方都可以通过ContextSession访问,避免了代码重复。

需要注意的是,IHttpContextAccessor需要在Program中注册为服务,作用域设置为Scoped,否则在依赖注入时会报错。注册代码如下:

// 在Program.cs中添加
// 注册 IHttpContextAccessor
builder.Services.AddHttpContextAccessor(); 
// 注册 ContextSession
builder.Services.AddScoped<ContextSession>(); 

这样就可以在需要的地方通过依赖注入获取ContextSession实例,进而获取当前用户的相关信息。

二、写入UserId和UserName到Claims中

在用户登录认证成功后,我们需要将用户的UserIdUserName写入到Claims中,以便后续请求可以通过ContextSession获取。通常在认证过程中,我们需要编写通用中间件来处理用户登录后将用户信息添加到Claims中。

我们在SP.Common中的Middleware文件夹下创建一个名为ApplicationMiddleware的中间件,它是一个通用的中间件,用于处理用户登录认证和将用户信息写入Claims,所有业务微服务都要引用这个中间件。代码如下:

using Microsoft.AspNetCore.Http;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using SP.Common.ExceptionHandling.Exceptions;
using SP.Common.ConfigService;namespace SP.Common.Middleware;/// <summary>
/// 应用程序中间件,所有微服务都要引入
/// </summary>
public class ApplicationMiddleware
{private readonly RequestDelegate _next;private readonly JwtConfigService _jwtConfigService;/// <summary>/// 应用程序中间件构造函数/// </summary>/// <param name="next">下一个中间件</param>/// <param name="jwtConfigService">Jwt配置服务</param>public ApplicationMiddleware(RequestDelegate next, JwtConfigService jwtConfigService){_next = next;_jwtConfigService = jwtConfigService;}/// <summary>/// 中间件处理请求/// </summary>/// <param name="context">HTTP上下文</param>/// <returns>异步任务</returns>public async Task InvokeAsync(HttpContext context){// 1. 获取Authorization头var authHeader = context.Request.Headers["Authorization"].FirstOrDefault();if (!string.IsNullOrEmpty(authHeader) && authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)){var token = authHeader.Substring("Bearer ".Length).Trim();try{var handler = new JwtSecurityTokenHandler();var jwtToken = handler.ReadJwtToken(token);var claims = jwtToken.Claims.ToList();// 查找UserId和UserNamevar userId = claims.FirstOrDefault(c => c.Type == "UserId")?.Value;var userName = claims.FirstOrDefault(c => c.Type == "UserName")?.Value;// 如果HttpContext.User没有身份,则新建if (context.User == null || !context.User.Identities.Any()){var identity = new ClaimsIdentity(claims, "jwt");context.User = new ClaimsPrincipal(identity);}else{// 合并claims到现有identityvar identity = context.User.Identities.First();if (!string.IsNullOrEmpty(userId) && !identity.HasClaim(c => c.Type == "UserId"))identity.AddClaim(new Claim("UserId", userId));if (!string.IsNullOrEmpty(userName) && !identity.HasClaim(c => c.Type == "UserName"))identity.AddClaim(new Claim("UserName", userName));}}catch{throw new UnauthorizedException("用户未登录");}}// 调用下一个中间件await _next(context);}
}

在这个中间件的实现过程中,首先会对每一个进入的HTTP请求检查其请求头中的Authorization字段,判断其是否存在且以Bearer 作为前缀。如果满足条件,则从该字段中提取出JWT令牌,并利用JwtSecurityTokenHandler对令牌进行解析。解析后,可以从JWT的Claims中获取到用户的UserIdUserName等关键信息。接下来,中间件会判断当前HttpContext.User是否已经包含身份信息。如果没有身份信息,则会新建一个ClaimsIdentity,并将解析得到的Claims(包括UserIdUserName)添加进去,随后将其赋值给HttpContext.User。如果已经存在身份信息,则会检查当前身份中是否已经包含了UserIdUserName这两个Claim,如果没有,则将其补充进去。通过这种方式,无论请求是否已经有身份信息,都能确保UserIdUserName被正确地写入到Claims中。这样一来,后续的业务处理中,只需要通过ContextSession即可方便地获取当前用户的身份信息,实现了用户上下文的统一管理和解耦,极大地方便了微服务架构下的用户认证与授权流程。

三、总结

在这篇文章中,我们实现了ContextSession类,用于获取当前请求的用户信息,并通过IHttpContextAccessor访问HTTP上下文。同时,我们创建了一个通用的中间件ApplicationMiddleware,用于处理用户登录认证和将用户信息写入Claims中。这样,我们就可以在微服务架构中统一管理用户上下文,方便地获取当前用户的身份信息。
这种设计模式不仅提高了代码的可维护性和可读性,还使得用户认证和授权的逻辑更加清晰和集中。通过将用户信息存储在Claims中,我们可以在整个应用程序中轻松访问用户的身份信息,而无需在每个业务模块中重复解析。也为后续的扩展和修改提供了便利,使得在需要添加新的用户信息或修改现有逻辑时,只需在ContextSession或中间件中进行调整即可,而不需要在每个微服务中都进行修改。它在微服务架构中尤为重要,因为它允许我们在多个服务之间共享用户信息,同时保持服务的独立性和解耦性。通过这种方式,我们可以更好地管理用户身份,确保系统的安全性和一致性。这种方法的实现也展示了ASP.NET Core中间件和依赖注入的强大功能,使得我们可以轻松地在应用程序中实现复杂的用户认证和授权逻辑,而无需编写大量重复的代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/91365.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/91365.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BaseDao

#### 10.1 DAO概念> DAO&#xff1a;Data Access Object&#xff0c;数据访问对象。 > > Java是面向对象语言&#xff0c;数据在Java中通常以对象的形式存在。一张表对应一个实体类&#xff0c;一张表的操作对应一个DAO对象&#xff01;>> 在Java操作数据库时&a…

USRP捕获手机/路由器数据传输信号波形(中)

目录&#xff1a; USRP捕获手机/路由器数据传输信号波形&#xff08;上&#xff09; USRP捕获手机/路由器数据传输信号波形&#xff08;中&#xff09; USRP捕获手机/路由器数据传输信号波形&#xff08;下&#xff09; 三、双工通信信号捕获 3.1 信号接收系统 5805e6Hz&a…

使用 Kiro AI IDE 3小时实现全栈应用Admin系统

Hello&#xff0c; 大家好&#xff0c;我是程序员海军, 全栈开发 |AI爱好者 &#xff5c; 独立开发。 之前我是采用Node生态开发的大模型以及MCP Server,大模型开发的生态主要是Python语言&#xff0c;为了更好的学习大模型开发&#xff0c;于是开了新坑。开始学习Python, 以及…

浏览器pdf、image显示

浏览器地址栏 pdf data:application/pdf;base64, data:application/pdf;base64,JVBERi0xLjcKJeLjz9MKMjMgMCBvYmoKPDwv image data:image/jpeg;base64, data:image/jpeg;base64,/9j/4Q3fRXhpZgAATU0AKgAAAAgABwE

《Linux运维总结:银河麒麟V10 SP3启动docker容器报错permission denied》

总结&#xff1a;整理不易&#xff0c;如果对你有帮助&#xff0c;可否点赞关注一下&#xff1f; 更多详细内容请参考&#xff1a;Linux运维实战总结 一、环境信息 二、背景 1、使用docker启动一个nginx容器&#xff0c;报错信息如下&#xff1a; docker: Error response from…

PDF源码解析

PDF源码解析打开PDF解析PDF​0. 文件头关键信息解析技术原理图解文件头的重要性实际文件结构示例开发者注意事项历史背景1. 根目录整体结构关键字段解析核心概念解释实际应用场景完整对象关系图技术总结2. 页面树对象结构关键字段解析页面树工作原理技术要点总结实际应用3. 图像…

java开闭原则 open-closed principle

基本知识 1.核心思想&#xff1a;面向抽象编程 2.基本内涵&#xff1a;对修改关闭&#xff0c;对扩展开放 3.要求&#xff1a;尽可能不修改源码而是增加新功能 例子 以spring5核心原理与30个类手写实战中的为例 package com.gupaoedu.vip.design.principle.openclose;/*** Crea…

拥抱智慧物流时代:数字孪生技术的应用与前景

概述 在数字经济全面推进的当下&#xff0c;物流行业正经历着前所未有的智能化升级。作为新一代信息技术的重要代表&#xff0c;数字孪生技术正悄然改变着物流的运作方式和决策模式。所谓数字孪生&#xff0c;是指在虚拟空间中创建与现实物流系统高度一致的数字模型&#xff0…

libnest2d-头文件分析-libnest2d.hpp-几何类型-策略类型-参数配置

libnest2d 库的主头文件&#xff0c;定义了一个用于 二维不规则形状自动排样&#xff08;Nesting&#xff09; 的C接口。以下是详细解析&#xff1a;1. 头文件结构 (1) 防止重复包含 #ifndef LIBNEST2D_HPP #define LIBNEST2D_HPP // ... #endif // LIBNEST2D_HPP确保头文件只被…

【Docker】部署Docker可视化管理面板Dpanel

一、DPanel 介绍 1.1 DPanel 简介 DPanel 是一款专为 Docker 设计的可视化管理面板&#xff0c;旨在降低容器、镜像及相关资源的管理门槛。通过简洁直观的图形化界面&#xff0c;即使是对 Docker 不熟悉的用户&#xff0c;也能轻松上手&#xff0c;实现容器化应用的高效部署与管…

GCC/G++ + Makefile/make 使用

一、gcc\g编译器 什么是gcc和g&#xff0c;它们的区别又是什么&#xff1f; gcc编译器是专门用来编译C语言的&#xff0c;而g编译器既可以编译C语言又可以用来编译C&#xff0c;但是主要还是用来编译C。 我们都知道代码形成可执行程序都是需要经过预处理、编译、汇编和链接这四…

重复文件查找工具:DataSecurity Plus 全面提升企业文件管理效率

企业日常运营经常会出现这样的场景&#xff1a;员工在文件传输、共享与备份过程中&#xff0c;因操作不当或系统设置问题&#xff0c;出现文件重复存储&#xff1b;跨部门协作时&#xff0c;相同的项目资料可能被多次保存&#xff1b;随着数据迁移与系统升级&#xff0c;重复文…

【软件架构】资源池架构设计中的三种主流模式

在资源池架构设计中&#xff0c;三种主流模式——集中式、分布式和混合式资源池——各有其独特的优势、劣势和适用场景。理解它们的区别对于设计高效、可靠和可扩展的系统至关重要。 下面是对这三种模式的详细分析和比较&#xff1a;集中式资源池 核心概念&#xff1a; 将所有计…

Java 类加载冲突

在某次线上部署过程中&#xff0c;我们遇到了一个十分诡异的问题&#xff1a;同样的应用&#xff0c;在 ext3 文件系统下运行正常&#xff0c;但部署到 ext4 文件系统下却出现了如下异常&#xff1a;The methods class, com.ctc.wstx.io.StreamBootstrapper, is available from…

VMware安装 统信UOS桌面专业版

前言 近年来&#xff0c;随着Linux发行版在开发者、企业环境中的应用逐渐增多&#xff0c;国产操作系统统信UOS&#xff08;基于Debian&#xff09;因其良好的图形化界面和本地化支持&#xff0c;成为不少用户体验Linux生态的选择之一。本文将以VMware Workstation Pro 17为例…

SAP Datasphere 02 - 建模

创建连接创建到 HANA Cloud 实例的连接查看 HANA Cloud实例连接 Endpoint创建连接选择连接类型配置连接信息&#xff0c;授权方式&#xff0c;用户名密码等配置连接名称验证连接导入数据源表创建目录 Hotel &#xff0c;放置建模对象点击新建目录&#xff0c;导入远程表选择数据…

isasssim robotiq夹爪踩坑

1. usd导出urdf失败在isasssim的仿真中的 robotiq 2f夹爪&#xff0c;首先目前4.5asset里面的usd不能直接转urdf&#xff0c;因为模型中存在 “闭环连接”&#xff0c;即某个部件&#xff08;或关节&#xff09;同时与两个及以上的父部件相连&#xff0c;形成类似 “三角形” 的…

50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | Pokedex(宝可梦图鉴)

&#x1f4c5; 我们继续 50 个小项目挑战&#xff01;—— Pokedex组件 仓库地址&#xff1a;https://github.com/SunACong/50-vue-projects 项目预览地址&#xff1a;https://50-vue-projects.vercel.app/ 使用 Vue 3 结合 PokeAPI 来创建一个炫酷的宝可梦图鉴应用。通过这个…

【Practical Business English Oral Scene Interpretation】在职主持会议-安排任务+结束会议

文章目录Introduction1. 讨论代办事项2. 分配工作任务3. 说明截止日期4. 说明截止日期5. 感谢参会者Introduction Note that each row of the table represents the content of the conversation in order. 1. 讨论代办事项 AlexBarryNoteLet’s review the to-dos from the…

ansible简单playbook剧本例子

1. 创建主机清单vim inventory.ini192.168.100.181[web:vars] ansible_userroot ansible_passwordAdmin123456[web] 192.168.100.1822. 创建一个简单的剧本vim playbook.yaml- name: My first playhosts: webtasks:- name: Ping my hostsansible.builtin.ping:- name: Print me…