ASP.NET Core集成現有系統認證


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

目錄

  • 認證與授權
    • 什么是認證
    • 何謂授權
    • 用Middleware攔截
  • 定制JWT Bearer 認證
    • 更改token來源
    • 更改token驗證方式
    • 開始授權 

認證與授權 

什么是認證? 

首先認證並不是登錄。認證是一個知道用戶是誰的一個過程。我們最早使用的基於Session的認證,拿到用戶輸入的用戶名和密碼到數據庫里面校驗一,看看是否正確,如果是正確的我們就放到session里面。這是一個完成認證的過程,系統現在知道你是我的某一個用戶了。

那么何謂授權? 

現在用戶登錄之后我們跳轉到了另一個頁面,這個頁面可能會寫一段這樣的代碼。
if(Session["user"]==null)
{
  Response.redirect("/login.aspx")
}
如果用戶登錄的Session不存在則再跳回到登錄頁面讓用戶登錄。 檢查當前用戶有沒有某個權限的這個過程叫授權。如果沒有怎么辦?我們就會跳轉用戶到一個沒有權限的提示頁面,或者返回  Forbidden 403 的HTTP 狀態碼,這是最簡單的授權。
 
復雜的授權方式包括對角色,對具體資源訪問以及操作的授權,這塊我們后面再講。
 
當我們的ASP.NET Core項目需要與老的項目兼容的時候,就需要兼容老項目的認證方式,比如某種自定義的token(這是之前比較常見的做法)。我們需要在ASP.NET Core中根據當前用戶header里面的token來判斷是否為一個合法的用戶。

用Middleware攔截

第一種簡單粗暴的方法即用Middleware來攔截。
 
在ASP.NET Core下,MVC以一個Middleware加入到整個HTTP管道。在此之前還會添加一個Routing的Middleware,注意這里的意思也就是說 Routing不再和ASP.NET MVC一樣屬於它的一部分。正好相反,在ASP.NET Core里面是有一個MVCRouteHandler被 Routing Middleware 加載出來處理請求。關於路由這塊我們后面再說。
 
如果我們要在MVC Middleware執行之請攔截請求只要加一個Middleware在 MVC Middleware或者Routing之前即可。
 
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();
}
上面是我們有簡易的方法實現的一個Middleware,被加到了MVC之前。當Request的Headers中沒有一個值為“jessetalk.cn” 以及 name為” token”的項的時候,我們就返回401狀態,並且不執行后面的處理。(不調用 next方法)
 
但是這種辦法相當於一刀切,我們添加的這個Middleware發生在 MVC Middleware之前把所有沒有認證信息的請求全部攔截掉了。好處是有節省服務器資源(如果確定是要攔截的就沒有必須再經過MVC的一些處理了),壞處是無法實現單個Controller或者Action的靈活配置。

定制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。

只有用admin的token,才能正常訪問。
 
以是就是基於JWT Authentication來定制的我們自己的認證方案的一個基本思路,主要是實現OnMessageReceived來改造token的來源,以及定義自己的  ISecurityTokenValidator 來實現對token的驗證。
本文首發於公眾號jessetalk。 如需轉載,請保留公眾號二維碼,謝謝。
 

更多精彩文章:

 
ASP.NET Core依賴注入全知道:  https://mp.weixin.qq.com/s/lR9O7bXiI704kSu7bKdLGg
我心中的ASP.NET Core新核心對象之WebHost(一)  https://mp.weixin.qq.com/s/4Sm2dxMe_WeVOizhqX4ZdA
極簡版ASP .NET Core學習路徑   https://mp.weixin.qq.com/s/7oKnYLOrff_FmMLm7thnBg
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM