我們現在大多數轉向ASP.NET Core來使用開發的團隊,應該都不是從0開始搭建系統,而是老的業務系統已經在運行,ASP.NET Core用來開發新模塊。那么解決用戶認證的問題,成為我們的第一個攔路虎。本文將給大家簡單闡述一下認證與授權的基本概念,以及基於ASP.NET Core 中間件實現的認證和改造JwtBearer 認證中間件來實現的認證達到與老系統(主要是token-based認證)的集成。
目錄
- 認證與授權
- 什么是認證
- 何謂授權
- 用Middleware攔截
- 定制JWT Bearer 認證
- 更改token來源
- 更改token驗證方式
- 開始授權
認證與授權
用Middleware攔截

public void Configure(IApplicationBuilder app) { app.Use(async (context, next) => { if (context.Request.Headers.ContainsKey("token")) { var token = context.Request.Headers["token"].FirstOrDefault(); if (token == "jessetalk.cn") { await next(); } } context.Response.StatusCode = 401; }); app.UseMvc(); }
定制JWTBearer Authentication
ASP.NET Core為我們實現了JWTBearer Authentication,關於 JWTBearer Authentication的實現可以參考另外一篇文章《在ASP.NET Core中使用JWTBearer Authentication》。我們今天要做的就是通過定制JWTBearer Authentication來達到讓它讀取我們自定義的Token並且用我們自己的方式來校驗這個Token。有點時代倒退的感覺是不是?
如果在時間和人員都足夠的情況下,我們是可能直接整體替換成標准的JWT方案,甚至做到SSO。但是架構是沒有止境的,在一定的時間框架下,要做到高效且安全的切換,這不失為一種好辦法。
首先我們需要看一下在JWTBearer中默認獲取的token是在Authorization的頭里,Bearer空格加上token。而如果有不規范的做法,可能是直接在headers里面加了一個token,里面有一個用我們自己的算法生成的token。
更改token的來源
JWTBearer authentication handler提供的Events中有一個OnMessageReceived委托可以讓我們從另外的地方讀取token。
services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(o => { o.Events = new JwtBearerEvents() { OnMessageReceived = context => { var token = context.Request.Headers["token"]; context.Token = token.FirstOrDefault(); return Task.CompletedTask; }, }; });
定制token的驗證方式
從headers里面拿到token之后,下一步就是要把它的驗證算法改成我們自己的。這一步可以通過自定義 ISecurityTokenValidator來實現 。我們在這個Validator里面,校驗token生成一個ClaimsPrincipal,這個principal就會被賦值到 HttpContext.User上。
同時我們還根據當前的token添加了一個Role Claim,它的值有user和admin。這個可能用來做基於角色的授權 。
public class MyTokenValidator : ISecurityTokenValidator { public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken) { validatedToken = null; var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme); identity.AddClaim(new Claim("name", "jesse")); identity.AddClaim(new Claim(ClaimsIdentity.DefaultRoleClaimType, securityToken == "jessetalk.cn" ? "admin" : "user")); var principal = new ClaimsPrincipal(identity); return principal; } }
注意ClaimsIdentity的AuthenticationScheme一定要與我們在UseAuthentication時設置的名稱一樣。否則Identity.IsAuthenticated無法正確設置為true,我們的授權就沒有辦法完成。
有了我們自定義的Validator之后,我們要對JwtBearer進行改造,去掉它默認的Validator,加上我們自己定義的這個。
services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(o => { o.SecurityTokenValidators.Clear(); o.SecurityTokenValidators.Add(new MyTokenValidator()); });
開始進行授權
為了給大家演示上面的功能,我們新建兩個Controller,一個是Admin,另一個是Home。兩者都需要用戶有token才能正常訪問,但是對於Admin我們需要用戶具有admin的role才可以,否則會被拒絕返回403。
HomeController.cs
[Authorize] public class HomeController : Controller { public IActionResult Index() { return Ok(); } }
當Headers里面沒有token 值的時候,API請求返回 401。
當Headers里面有token值時,API可以被正常訪問。
我們又加了一個AdminController,不一樣的是這次我們給Authorize加上了Role=”admin”,也就是只有擁有admin的Role才可以訪問這個API。
[Authorize(Roles ="admin")] public class AdminController : Controller { public IActionResult Index() { return Ok(); } }
當我們用user的token訪問時,我們會得到403。

ISecurityTokenValidator
來實現對token的驗證。
