AspNetCore 成長雜記(一):JWT授權鑑權之生成JWT(其二)

2023-04-27 06:00:57

引子

前面說了用第三方類庫生成JWT的故事,給我帶來了很大的方便,並且我也承諾要寫一篇用常規方法生成JWT的文章(一般都是用微軟官方的類庫),因此才有了這篇文章。
另外,在前面的文章中,我要糾正一下一些錯誤JWT的整個結構決定了JWT只能作為臨時的授權認證解決方案,如果對使用者的機密性要求比較高,必須用有狀態控制管理的解決方案,JWT只能作為一般性方案使用,它的應用場合主要是由多個WebAPI構成的多程序多執行緒多介面這樣的微服務架構,是為了解決使用狀態管理帶來的不便才應用而生,一般JWT必須和HTTPS配合才會具有安全性。這是因為JWT作為一種可被破解的資料,只有TLS加密後,才不會被真正破解。
在微軟的Indentiy認證框架中(我個人感覺應該叫ASP.NET Core Indentity),MVC和WebAPI雖然使用不同的Nuget包,但本質是一樣的……廢話不多說了,直接正題開始:有請我們的主角:JwtSecurityTokenHandler(從名字中就知道它和資料庫操作有關,JWT中的資料一般都是來自使用者資料庫,helper一般用來表示資源管理),後面我們會通過它來實現JWT的生成。

實施

和前面一樣,我們這裡主要講解最常用的非對稱演演算法的JWT,這裡採用的演演算法是RSA,當然你也可以採用其他演演算法來達到目標。
首先需要安裝nuget包Microsoft.AspNetCore.Authentication.JwtBearer,當然,有的文章會讓你安裝一個System.IdentityModel.Tokens.JWT的包,這個完全不需要,因為前面的包已經包含後者了,你只要在參照包以後,構建一下工程,在dubug目錄裡面找到專案生成檔案,就會發現這個dll被放進去了。
由於ASP.NET Core是以依賴注入為主的,而這個包作為一個Service(服務),需要使用其自身提供的擴充套件方法來注入ASP.NET Core 的WebApplication的Service物件(IOC容器)中進行集中管理。

點選檢視實現程式碼
builder.Services.AddAuthentication()..AddJwtBearer(jwtOptions =>{
    jwtOptions.Authority = "https://jwtserver.test.net";
    //jwtOptions.Audience = "jwtresouce";
    jwtOptions.TokenValidationParameters = new TokenValidationParameters()
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = builder.Configuration["Jwt:Issuer"],//發行者
        ValidAudience = builder.Configuration["Jwt:Audience"], //訂閱者
        IssuerSigningKey = new RsaSecurityKey(RSA.Create(2048))  //jwt簽名演演算法
    };
})
之後再建立一個用來生成JWT的控制器(~~個人比較推薦使用控制器,而不是MiniAPI,感覺MiniAPI更適合比較簡單的場景,比較複雜的場景還是得用控制器~~)。這樣可以使得我們每次存取時獲取到一個臨時的JWT。 先說說大體流程:
  1. 先建立一個用來生成JWT的JwtSecurityTokenHandler物件,為後面的工作提供基礎
  2. 再建立一個陣列,用來存放使用者資訊
  3. 建立SecurityTokenDescriptor物件,設定JWT的加密演演算法,有效期等屬性
  4. 呼叫之前建立的JwtSecurityTokenHandler物件的CreateToken方法,建立一個令牌物件,再呼叫WriteToken方法,獲得到字串格式的JWT
  5. 返回結果
點選檢視實現程式碼
[HttpPost]
    public string CreateJwtSecurityToken()
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        

        var mookdata = new Dictionary<string, string>();
        mookdata[ClaimTypes.Name] = "John Doe";
        mookdata[ClaimTypes.Email] = "[email protected]";
        mookdata[ClaimTypes.Role] = "vistor";

        var claims = new Claim[mookdata.Count - 1];
        foreach (var item in mookdata)
        {
            for (int i = 0; i < claims.Length; i++)
            {
                claims[i] = new Claim(item.Key, item.Value);
            }
        }

        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(claims),
            Expires = DateTime.UtcNow.AddDays(7),
            SigningCredentials = new SigningCredentials(new RsaSecurityKey(RSA.Create(2048)),
                SecurityAlgorithms.RsaSha256Signature)
        };

        var token = tokenHandler.CreateToken(tokenDescriptor);
        var tokenString = tokenHandler.WriteToken(token);

        return tokenString;
    }

總結

上述講到的方法一般都是比較常用的方法(這個好像是來自微軟官方檔案),先對於上篇文章,它相對比較靈活,不會涉及到x509證書的問題,而且解決方案比較多,容易應用。
關於生成JWT,我也是簡單的瞭解和使用,因此層次不少太深,如果讀者在其中發現了問題,也歡迎各位提出寶貴的意見,謝謝。
關於JWT,我想說這只是WebAPI授權鑑權的開端,不是終點,如果有時間,我會再寫一篇結合JWT來授權鑑權的文章,來更好的理解JWT的應用,希望我的文章會給您帶來幫助,讓我們一起期待吧!