實現JWT原理


JWT學習文章:

第一篇:JWT原理

第二篇:JWT原理實現代碼

第三篇:在asp.net core中使用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://gitee.com/jingboweilanGO/Demo_jwt_core.git

說明:Demo-jwt-core2是本篇文章涉及到的源碼,是使用asp.net core 自帶的jwt方法;

     Demo-jwt-core是第三篇jwt文章的源碼,在asp.net core中的使用JWT


免責聲明!

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



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