最近不知怎麼的,自從學了WebAPI(為什麼是這個,而不是MVC,還不是因為MVC的Razor語法比較難學,生態不如現有的Vue等框架,webapi很好的結合了前端生態)以後,使用別人的元件一帆風順,但是不知其意,突然很想自己實現一個基於的JWT認證服務,來好好了解一下這個內容。
自從Session-Cookie方案逐漸用的越來越少,JWT的使用也變得成為主流的安全方案之一,但是在.NET Core的檔案(這裡的.NET Core指代原來的.Net Core以及之後的版本,檔案是微軟的開發者檔案)並沒有對JWT做詳細的介紹(可能是在微軟看來太簡單了,不值得細說),僅僅略帶一提而已,範例程式碼更是少得可憐,根本沒有什麼建設性的幫助作用,更像檔案工程師在水任務(但不得不說微軟的Indentity框架是真的強大,Spring Security的功能基本都實現了)。縱然是費盡心機找資料,鑽研檔案,還是所獲甚少。但是在不斷的努力之下還是找到很多方案的,其中比較有用的就拿幾個,我仔細研究實踐後得到了這幾篇文章,不求它有多大幫助,之希望它能幫更多人少走彎路。
然而這幾個方案大概可以分成兩類:
ApiFox
或者EOLink
。首先建立一個WebAPI專案,至於是否在啟動後使用HTTPS,根據自己的需要,一般都是需要的。然後用Nuget或者Dotnet安裝JWT
這個Nuget包即可開始,如果是ASP.NET Core這樣需要依賴注入環境的,推薦JWT.Extensions.AspNetCore
這個包(強力推薦),可以更好的讓你開始,僅僅需要基本功能的只用JWT即可。
由於我這裡使用的是RSA1024bit,所以需要一個HTTPS的PEM或者CRT證書做CA,各位可以自己生成一個。
首先,我們需要為服務注入這個包的依賴,即使用builder.Services.AddAuthentication().AddJwt()
來新增相關依賴。那為什麼是要使用這個方法呢?如果你通過物件瀏覽器
檢視API會發現一個AddJwtDecoder
的方法,同樣可以新增依賴,並且更靈活,如果反編譯就發現——AddJwt
方法是對AddJwtDecoder
的某個過載的呼叫,後面可以呼叫其他方法達成同樣的效果,所以推薦使用這個方法注入。
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtAuthenticationDefaults.AuthenticationScheme;
})
.AddJwt();
然後在應用認證中介軟體即可。
app.UseAuthentication();
完成這些工作以後,還需要建立一個用來根據使用者資訊生成JWT的控制器,為了防止使用HTTPGet被攻擊,我這裡採用了HTTPPost。
根據這個包的檔案,生成一個JWT字串非常容易,只需要建立一個x509物件或者兩個RSA物件作為公鑰和私鑰即可,我推薦使用這個包裡面提供的FluentApi方式,寫起來非常舒服,最後編碼生成JWT,完成。
var token = JwtBuilder.Create()
.WithAlgorithm(algorithm) // 加密演演算法
.AddClaim<string>("Account", accountName) //新增使用者資訊
.AddClaim<string>("Passwd", passwdContext) //新增使用者密碼
.Encode(); //編碼生成jwt
[Route("api/[controller]")]
[ApiController]
public class JwtController : ControllerBase
{
private RSA publicKey = RSA.Create();
private RSA privateKey = RSA.Create();
private RS2048Algorithm? algorithm { get; set; }
public JwtController()
{
algorithm = new RS2048Algorithm(publicKey, privateKey);
}
[HttpPost]
public async Task<string> CreateJwt(string accountName, string passwdContext)
{
return await Task<string>.Run<string>(() =>
{
var token =
JwtBuilder.Create()
.WithAlgorithm(algorithm)
.AddClaim<string>("Account", accountName)
.AddClaim<string>("Passwd", passwdContext)
.Encode();
return token;
});
}
}
JWT.Extensions.AspNetCore
這個包是一個整合了常用jwt操作的包,可以讓你不必關心JWT的建立過程,這大大化簡了我們使用JWT的過程,在一定程度上提高了生產力。如果您喜歡這個庫,可以到專案主頁上新增一顆星。
經過本人的親身經歷,x509在.NET6之後的類庫X509Certificate2
不能直接生成私鑰,需要使用該類的成員方法:public System.Security.Cryptography.X509Certificates.X509Certificate2 CopyWithPrivateKey (System.Security.Cryptography.ECDiffieHellman privateKey);
建立一個帶有私鑰的副本,否則會出現私鑰在物件構造成功後出現NULL
的情況。
如果沒有特殊必要,建議直接使用Rsa
的成員方法直接生成一個Rsa物件來操作比較簡便,目前這個辦法還可以改進,歡迎各位留言。