JWT學習文章:
第一篇:JWT原理
第二篇:JWT原理實作代碼
上一篇學習了JWT的基本理論,這一篇將根據原理進行代碼實作,
要想實作jwt的加密解密,要先生成一個SecurityKey,大家可以在網上工具生成一個隨機的密鑰,我是在這里生成的,
下面篇幅大量都是代碼,因為注釋寫得很清楚,因此就不再有過多文字說明,
代碼實作
新建常量類:Const
public class Const { public const string SecurityKey= "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDSfLGu+kcFDcJUCV46J+SbgR0lNc2NqgCGzojQTWW9xqjuzPF3mpisvTggYZSGfBzN+88YLZYbBLrDTUMJ4nTieElbP6SHkBFu8F+7fFBi7w3UPsaAXDr2E2srQYU5ZlKAcFBoNajNWj3sfSVRoYRPdqDTj4WdJlUPSNGz0wgRrQIDAQAB"; public const string Domain = "http://localhost:5000"; }
新建控制器:AuthController
[ApiController] [Route("[controller]")] public class AuthController : ControllerBase { [HttpGet] public IActionResult Get(string userName, string pwd) { //此處只簡單的驗證用戶名和密碼的不為空,實際中使用時不要這樣 if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(pwd)) { //Header var header = "{\"alg\": \"HS256\",\"typ\": \"JWT\"}"; var headerBase = Base64UrlTextEncoder.Encode(Encoding.UTF8.GetBytes(header)); //Payload var payloadDic = new Dictionary<string, object>(); payloadDic["iss"]= Const.Domain; //添加jwt可用時間 var now = DateTime.UtcNow; payloadDic["nbf"] = now.ToUniversalTime();//可用時間起始 payloadDic["exp"] = now.AddMinutes(30).ToUniversalTime();//可用時間結束 var payload = JsonConvert.SerializeObject(payloadDic); var payloadBase = Base64UrlTextEncoder.Encode(Encoding.UTF8.GetBytes(payload)); //Signature //宣告hs256物件 var hs256 = new HMACSHA256(Encoding.UTF8.GetBytes(Const.SecurityKey)); //生成signature var signature = hs256.ComputeHash(Encoding.UTF8.GetBytes(headerBase + "." + payloadBase)); var signatureBase = Base64UrlTextEncoder.Encode(signature); return Ok(new { token = headerBase + "." + payloadBase + "." + signatureBase }) ; } else { return BadRequest(new { message = "username or password is incorrect." }); } } }
為了過濾哪些介面需要驗證,此處新建一個特性:AuthAttribute
public class AuthAttribute : Attribute { public AuthAttribute() { } }
修改原有的Home控制器:
[ApiController] [Route("[controller]")] public class HomeController : ControllerBase { [HttpGet] [Route("api/value1")] public ActionResult<IEnumerable<string>> Get() { return new string[] { "value1", "value1" }; } [HttpGet] [Route("api/value2")] [Auth] public ActionResult<IEnumerable<string>> Get2() { return new string[] { "value2", "value2" }; } }
Value2介面標記了Auth特性,在下面驗證時有Auth特性標記的介面才會被要求token,
新建靜態類:AuthExtension,并且增加一個IApplicationBuilder的擴展方法:
public static class AuthExtension { public static void AddAuthorize(this IApplicationBuilder applicationBuilder) { applicationBuilder.Use(async (currentContext, nextContext) => { //獲取是否具有自定義的auth特性 var authAttribute = currentContext.GetEndpoint()?.Metadata.GetMetadata<AuthAttribute>(); if (authAttribute != null) { if (currentContext.Request.Headers.ContainsKey("Authorization")) { var authorize = currentContext.Request.Headers["Authorization"].ToString(); if (authorize.Contains("Bearer")) { var info = authorize.Replace("Bearer ", string.Empty); var jwtStr = info.Split('.').ToArray(); //宣告hs256物件 var hs256 = new HMACSHA256(Encoding.UTF8.GetBytes(Const.SecurityKey)); //生成signature var signature = Base64UrlTextEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(jwtStr[0] + "." + jwtStr[1]))); //驗證加密后是否相等 if (jwtStr[2] == signature) { //驗證是否在有效時間內 var now = DateTime.UtcNow.ToUniversalTime(); var payload = JsonConvert.DeserializeObject<Dictionary<string, object>>(Encoding.UTF8.GetString(Base64UrlTextEncoder.Decode(jwtStr[1]))); if (now >= Convert.ToDateTime(payload["nbf"]) && now <= Convert.ToDateTime(payload["exp"])) { //await currentContext.Response.WriteAsync("驗證通過").ConfigureAwait(true); await nextContext?.Invoke(); return; } currentContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized; await currentContext.Response.WriteAsync("Authorization time has passed, please log in again!").ConfigureAwait(true); } } } currentContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized; await currentContext.Response.WriteAsync("Verification failed, no permission to access!").ConfigureAwait(true); } await nextContext?.Invoke(); }); } }
在啟動類Startup的請求管道中(Configure)添加上面的擴展方法:
//添加身份驗證 app.AddAuthorize();
注意一定要把這句話添加在UseRouting()之后,因為在擴展方法中獲取Auth特性只有在注冊了Routing規則后才能獲取到值,
測驗
訪問無需權限的Value1介面:

?
訪問成功!!!
獲取token:

?
我啟用了swagger,如果沒有啟用在postman中請求https://localhost:5001/Auth?userName=admin&pwd=admin也是一樣的,
代碼中用戶名和密碼我只是簡單的驗證了下是否為空,所以這里填寫什么都能通過,
獲取token成功說明獲取token的代碼沒有問題,邏輯有沒有問題還不能確定,所以要經過后面介面的確認看是否成功,
訪問要求權限驗證的Value2介面:
不帶token:

?
訪問失敗!!!
帶上token:

?
成功!!!
靜待三十分鐘(代碼中設定token過期時間為三十分鐘),呼叫Value2介面:

?
失敗了!!!錯誤提示是token過期,
如果覺得不保險,還可以逐步除錯看一下是否所有邏輯都正確執行,這里就不再進行贅述了,
至此證明我們依照jwt原理寫的權限驗證成功!!!
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/270437.html
標籤:.NET Core
