矛魚盾

使用JSON Web Token(JWT)進行身分驗證與授權

使用JSON Web Token(JWT)進行身分驗證與授權 JWT(JSON Web Token)是一種廣泛使用的身份驗證和授權方法。它通常用於傳輸用戶訊息,可以使用HMAC 演算法或 RSA 公鑰/私鑰對進行簽署。 我們預計將天氣預報資訊,設為需要進行驗證,才能讀取。 驗證流程 當使用者未進行驗證,進行訪問受保護資料時,顯示 401 錯誤 使用者登入,Server 認證成功後,傳送一組 JWT 給使用者。 使用者將取得 JWT,於 header 中的 Authorization 加上 Bearer token。 重新訪問受保護資料,顯示結果。 JWT 可以夾帶使用者資料,我們將訪問權限設為管理者,並嘗試不同角色的結果。 環境 Microsoft.AspNetCore.Authentication.JwtBearer 安裝 Microsoft.AspNetCore.Authentication.JwtBearer 設置 Issuer 和 Key 在 appsettings.json 中,設置 Issuer 和 Key 1 2 3 4 "JwtSettings": { "Issuer": "Solution1", "SecretKey": "ZG0JZtDY3^FnkbCYy@!vJfVE922k9MJG" } 設定驗證與授權 在 Program.cs 設定驗證,並讀取 JwtSettings 設定 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 using Microsoft.

CQRS與AutoMapper或Mapster(六)

CQRS與AutoMapper或Mapster(六) 呈上篇,我們已經將驗證透過IPipelineBehavior進行簡化了,以下是目前的程式碼 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private readonly IMediator _mediator; public WeatherForecastController(IMediator mediator) { _mediator = mediator; } [HttpPost] public async Task<IActionResult> CreateWeatherForecast( [FromBody] CreateWeatherForecastRequest request) { var command = new CreateWeatherForecastCommand { Nation = request.

CQRS與MediatR(五)

CQRS與MediatR(五) 有些時候我們想在命令執行前後統一做額外的事情,例如每個命令執行前驗證資料欄位、紀錄請求者,或是命令執行後,統一執行資料庫交易等。若是使用自訂的Command,通常會使用裝飾者模式(Decorator Pattern)做處理,而 MediatR 則使用管線的概念協助。 MediatR管線: IRequestPreProcessor<> 請求執行前的預處理 IRequestPostProcessor<,> 請求執行後的再處理 IPipelineBehavior<,> 自訂管線行為 以下是我們目前的程式碼,每次執行命令前,我們都會使用 FluentValidation,協助我們驗證資料欄位是否正確,並使用方法注入,更好的顯示程式碼意圖。 然而隨著時間流逝,相似的程式碼,會逐步在每個請求中重複出現,所以我們希望能透過 MediatR 管線,來協助我們自動執行驗證。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private readonly IMediator _mediator; public WeatherForecastController(IMediator mediator) { _mediator = mediator; } [HttpPost] public async Task<IActionResult> CreateWeatherForecast( [FromBody] CreateWeatherForecastRequest request, [FromServices] IValidator<CreateWeatherForecastCommand> validator) { var command = new CreateWeatherForecastCommand { Nation = request.

CQRS與MediatR(四)

CQRS與MediatR(四) 上一篇談到我們介紹了引入 MediatR 的改變,並簡化自行注入 CommandHandler Service。這篇我們先介紹多個處理 CommandHandler 的方法。 有時候不會像上一篇這麼單純,一個 Command 對應一個 CommandHandler,而是一個Command 對應多個 CommandHander。例如,訂購商品,先新增客戶資料,再填寫購物明細,或者達成交易時,寄送Email通知物流人員等。 在開始前,這邊記錄一篇 stackoverflow 上的文章,談到關於一個Handler去呼叫另一個Handler的問題,而 Steven(Simple Injector作者,同時也是依賴注入:原理、實作與設計模式,該書的共同作者),給出他的見解,覺得很受用。下面是翻譯文章 用例和命令之間存在一對一的映射是很自然的。在這種情況下,表示層應該(在單一使用者操作期間,例如單擊按鈕)只執行建立命令並執行它。 此外,它應該只執行單一命令,而不應該執行更多命令。執行該用例所需的一切都應該由該命令完成。 也就是說,發送文字訊息、寫入資料庫、進行複雜的計算、與 Web 服務通訊以及操作業務需求所需的所有其他操作都應該在該命令的上下文中完成(或可能排隊稍後進行)。 這並不意味著命令處理程序本身應該完成所有這些工作。 將許多邏輯轉移到處理程序所依賴的其他服務是很自然的。 因此,我可以想像您的處理程序取決於 ITextMessageSender 介面。(Google 翻譯) 我們修改範例,新增氣溫後,若低於0度,則發佈低溫特報,寄送Email通知相關人員。本篇介紹三種方法,沒有絕對正確的做法,依照個人偏好或團隊共識為主。 方法一:Controller 調用兩個 Command 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 [HttpPost] public async Task<IActionResult> CreateWeatherForecast( [FromBody] CreateWeatherForecastRequest request, [FromServices] IValidator<CreateWeatherForecastCommand> validator) { var command = new CreateWeatherForecastCommand { Nation = request.

CQRS與MediatR(三)

CQRS與MediatR(三) MediatR簡介 在 NET 中說到 CQRS,常伴隨著 MediatR,如前篇所示,其實 CQRS 與 MediatR 並沒有絕對的掛鉤,即使不使用 MediatR 也可以實作 CQRS,那麼 MediatR 魅力到底是什麼?,為什麼多數情況下,CQRS 的專案,通常會使用 MediatR 套件呢? MediatR 的作者是 Jimmy Bogard (同時也是 AutoMapper 作者),是一種中介者模式實現 中介者模式定義了一個中介者對象,該對象封裝了系統中對象間的交互方式,對象間的通信過程被封裝在一個中介者(調解人)對象之中。 對象之間不再直接交互,而是通過調解人進行交互。 這麼做可以減少可交互對象間的依賴,從而降低耦合。(Wiki) 以現實角度解釋,就是類似仲介般的角色,如自己找房子與委託房仲的差異。自行買賣房屋,買家要面的是無數的賣家,而賣家也要面對無數的買家,帶屋看房這些繁雜的事物,均要親力親為;委託仲介可以將許多複雜的交易情況簡化,我們不需要自己去找房子/賣房子,而是委託仲介面對買家/賣家,不自行處理大小雜事,只在最後關鍵的時刻達成交易共識。 在程式中,即是透過 MediatR 去調用不同的服務,如請求/回應、命令、查詢、通知和事件、同步和非同步等。 環境 FluentValidation.DependencyInjectionExtensions MediatR 在 MediatR v12 之前的版本,我們必須額外安裝 MediatR.Extensions.Microsoft.DependencyInjection 套件。 安裝 MediatR 修改IQueryHandler、ICommandHandler介面 在 MediatR 中,不論是 Query 或是 Command,都是透過 IRequest及 IRequestHandler實作,但我習慣分開使用,這屬於個人偏好,此步驟可以省略。 若省略後續文章請使用IRequest、IRequestHandler 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public interface ICommand<out TResponse> : IRequest<TResponse> { } public interface ICommandHandler<in TCommand, TResponse> : IRequestHandler<TCommand, TResponse> where TCommand : ICommand<TResponse> { } // OR 不回傳值 //public interface ICommand : IRequest //{ //} //public interface ICommandHandler<TCommand> : IRequestHandler<TCommand> where TCommand : ICommand //{ //} 1 2 3 4 5 6 7 8 public interface IQuery<out TResponse> : IRequest<TResponse> { } public interface IQueryHandler<in TQuery, TResponse> : IRequestHandler<TQuery, TResponse> where TQuery : IQuery<TResponse> { } 修改原本的Command和ICommandHandler方法

CQRS與FluentValidation(二)

CQRS與FluentValidation(二) 呈上篇,第二篇開始,就是開始使用一些套件,來簡化、優化原本的程式碼。預計會使用 FluentValidation、MediatR、AutoMapper,導入並沒有一定順序,都是獨立,也不一定都要使用,完全自由選擇,這邊只是逐篇引入,看一下最後的程式碼大概會長怎樣。 另外這些套件並不會逐一介紹使用方法,避免模糊焦點。 DataAnnotations 與 FluentValidation 各有優缺點,DataAnnotations 不需要額外安裝,且大部分情境都可以解決,而 FluentValidation 能以更優雅的方式將驗證切割開來,口語化驗證,易於客製化等。相關優缺點,放在參考。 環境 FluentValidation.DependencyInjectionExtensions 安裝 FluentValidation FluentValidation.DependencyInjectionExtensions 版本已經包含 FluentValidation,並提供簡單的 DI註冊方式,所以我們直接安裝該套件即可。 Application 層建立 Validator 驗證 FluentValidation 驗證寫法不是本篇討論重點,可以參考官網,或 FluentValidation簡介 1 2 3 4 5 6 7 8 9 10 public class CreateWeatherForecastValidator : AbstractValidator<CreateWeatherForecastCommand> { public CreateWeatherForecastValidator() { RuleFor(x => x.Nation).NotEmpty().WithName("國家"); RuleFor(x => x.City).NotEmpty().WithName("城市"); RuleFor(x => x.TemperatureC).InclusiveBetween(-50, 50).WithName("溫度"); RuleFor(x => x.Summary).MaximumLength(300); } } 註冊Validator 1 2 3 using FluentValidation; builder.Services.AddValidatorsFromAssemblyContaining<CreateWeatherForecastValidator>(); Controller 使用 Validator 因為CommandHandler和Validator都是特定相依物件,並非整個Controller都使用,所以我選擇了方法注入(Method Injection),當然也可以選擇其他注入方式,但要留意建構子過於龐大的異樣。