一、前言
1、本教程主要內容
- ASP.NET Core 中間件介紹
- 通過自定義 ASP.NET Core 中間件實現請求驗簽
2、本教程環境信息
軟件/環境 | 說明 |
---|---|
操作系統 | Windows 10 |
SDK | 2.1.401 |
ASP.NET Core | 2.1.3 |
MySQL | 8.0.x |
IDE | Visual Studio Code 1.32.3 |
瀏覽器 | Chrome 70 |
VS Code插件 | 版本 | 說明 |
---|---|---|
C# | 1.17.1 | 提供C#智能感知, .NET Core 調試、編譯等 |
vscdoe-solution-explorer | 0.3.1 | 提供解決方案視圖 |
本篇代碼以下代碼進行調整:https://github.com/ken-io/asp.net-core-tutorial/tree/master/chapter-02
3、前置知識
可能需要的前置知識
- C# 委托(Delegate)
http://www.runoob.com/csharp/csharp-delegate.html
- C# 擴展方法
二、ASP.NET Core 中間件介紹
1、ASP.NET Core 中間件基本說明
當 ASP.NET Core MVC應用從Kestrel接收到請求,會建立HttpContext並交由Application來處理請求。在Application中會有一個處理該請求的通道,這就是ASP.NET Core 管道,通常稱之為:請求處理管道
在這個管道中,有一系列有序處理請求的組件,就是中間件(Middleware)。
圖中藍色的部分可以認為是系統內置比較靠前的中間件或者我們自定義的中間件,MVC是一個特殊的中間件且通常放在最后,所以這里單獨畫出來
對於MVC中間件,如果請求的URL與路由匹配,那么后面的中間件均不會生效。所以MVC通常放在最后。
ASP.NET Core中會內置一些中間件,例如:身份驗證、靜態文件處理、MVC等。每個中間件在接受到請求后都可以選擇是交由下一個中間件處理還是直接返回結果。例如:
- 身份驗證中間件驗證未通過會直接引導到登陸頁
- 靜態文件中間件判斷為靜態文件就會直接返回靜態文件內容
所以,中間件可以理解為請求處理管道中的請求處理器。我們也可以通過自定義中間件注冊到管道中來干預請求。
2、ASP.NET Core 中間件基礎使用
在程序中,中間件是基於委托來構建的。在應用啟動時通過IApplicationBuilder注冊到通道中。
具體見啟動類Startup.cs
:
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseMvc(routes => { //配置默認路由 routes.MapRoute( name: "Default", template: "{controller}/{action}", defaults: new { controller = "Home", action = "Index" } ); }); }
UseDeveloperExceptionPage
、UseMvc
都是接口IApplicationBuilder
的擴展方法。
三、使用 ASP.NET Core 中間件實現請求驗簽
如果你開發的API是為手機App服務的,那么你的API是一定要暴露給公網的,如果有人拿到API地址進行非法請求,獲取用戶信息或者是篡改數據,用戶隱私、數據就會受到損害。這是很不安全的,我們可以讓客戶端請求的時候必須攜帶簽名,在服務器端鑒權(驗證簽名)通過了再放行,這樣就安全很多了。
1、創建驗簽中間件
在項目Ken.Tutorial.Web
創建目錄Middlewares
,然后創建類:TokenCheckMiddleware.cs
using System; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; namespace Ken.Tutorial.Web.Middlewares { public class TokenCheckMiddleware { private readonly RequestDelegate _next; public TokenCheckMiddleware(RequestDelegate requestDelegate) { this._next = requestDelegate; } public Task Invoke(HttpContext context) { //先從Url取token,如果取不到就從Form表單中取token var token = context.Request.Query["token"].ToString() ?? context.Request.Form["token"].ToString(); if (string.IsNullOrWhiteSpace(token)) { //如果沒有獲取到token信息,那么久返回token missing return context.Response.WriteAsync("token missing"); } //獲取前1分鍾和當前的分鍾 var minute0 = DateTime.Now.AddMinutes(-1).ToString("yyyy-MM-dd HH:mm"); var minute = DateTime.Now.ToString("yyyy-MM-dd HH:mm"); //當token和前一分鍾或當前分鍾任一時間字符串的MD5哈希一致,就認為是合法請求 if (token == MD5Hash(minute) || token == MD5Hash(minute0)) { return _next.Invoke(context); } //如果token未驗證通過返回token error return context.Response.WriteAsync("token error"); } public string MD5Hash(string value) { using (var md5 = MD5.Create()) { var result = md5.ComputeHash(Encoding.ASCII.GetBytes(value)); var strResult = BitConverter.ToString(result); return strResult.Replace("-", ""); } } } }
由於是側重自定義中間件,所有驗簽的邏輯就寫的非常簡單,如果實際項目使用,可以按照自己需求調整
2、創建擴展方法
在Middlewares
目錄下新建類:MiddlewareExtension.cs
using Microsoft.AspNetCore.Builder; namespace Ken.Tutorial.Web.Middlewares { public static class MiddlewareExtension { public static IApplicationBuilder UseTokenCheck(this IApplicationBuilder builder) { return builder.UseMiddleware<TokenCheckMiddleware>(); } } }
這里我們通過擴展方法,將TokenCheckMiddleware
掛在接口IApplicationBuilder
上
3、中間件注冊/引用
在啟動類Startup.cs
的Configure
方法中注冊/引用中間件
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { //省略部分代碼 app.UseTokenCheck(); app.UseMvc(routes => { //省略路由配置代碼 }); }
如果你覺得擴展方法有點多余,也可以直接使用UseMiddleware
方法注冊
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { //省略部分代碼 app.UseMiddleware<TokenCheckMiddleware>(); app.UseMvc(routes => { //省略路由配置代碼 }); }
這里要注意的是,如果你是一個MVC應用,請一定要把MVC這個中間件作為最后一個注冊。因為中間件是按照注冊順序被調用的。如果放在MVC之后,請求的URL也有對應路由適配,那么整個請求已經被MVC接管。后面的中間件就不會被調用了。
4、驗簽中間件測試
啟動應用,然后驗證不同情況下的訪問結果
URL | Response |
---|---|
localhost:5001 | token missing |
localhost:5001?token=test | token error |
localhost:5001?token=3D76FEA1D0ADD0C7639B73023436C6EA | Hello World ! -ken.io |
為了方便測試,MD5哈希的值我們可以在線生成:ttp://tool.chinaz.com/tools/md5.aspx
把當前分鍾,例如:2019-03-27 23:23
通過MD5在線生成那就是3D76FEA1D0ADD0C7639B73023436C6EA
四、備注
- 本文代碼示例
https://github.com/ken-io/asp.net-core-tutorial/tree/master/chapter-09
- 本文參考
https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.1
- 延伸閱讀
https://www.cnblogs.com/artech/p/inside-asp-net-core-pipeline.html
本文首發於我的獨立博客:https://ken.io/note/asp.net-core-tutorial-middleware