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
英文地址,請注明出處:http://en.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…