天天看點

C#分布式登入——jwt一、傳統的session登入二、基于token的鑒權機制三、Json web token四、JWT的問題

文章目錄

  • 一、傳統的session登入
  • 二、基于token的鑒權機制
  • 三、Json web token
    • 1.生成jwt資料
    • 2.jwt解析驗證
  • 四、JWT的問題

一、傳統的session登入

在伺服器存儲一份使用者登入的資訊,這份登入資訊會在響應時傳遞給浏覽器,告訴其儲存為cookie,以便下次請求時發送給我們的應用,這樣我們的應用就能識别請求來自哪個使用者了,這就是傳統的基于session認證。

C#分布式登入——jwt一、傳統的session登入二、基于token的鑒權機制三、Json web token四、JWT的問題

在asp.net core中可以簡單實作:

代碼如下(示例):

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            //使用靜态檔案
            app.UseStaticFiles();
            //Cookie政策
            //app.UseCookiePolicy();
            //Session
            app.UseSession();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                   // template: "{controller=Home}/{action=Index}/{id?}");
                   //template: "{controller=Home}/{action=Server}/{id?}");
                   template: "{controller=Login}/{action=SignIn}/{id?}");
            });
        }
           
public IActionResult SignIn(UserModel userModel)
        {
            if (ModelState.IsValid)
            {
                //檢查使用者資訊
                if (userModel.Username.Equals("yswenli") && userModel.Password.Equals("yswenli"))
                {
                    //記錄Session
                    HttpContext.Session.Set("User", ByteConvertHelper.Object2Bytes(userModel));
                    //跳轉到系統首頁
                    return RedirectToAction("Server", "Home");
                }
                ViewBag.ErrorInfo = "使用者名或密碼錯誤";
                return View(userModel);
            }
            ViewBag.ErrorInfo = ModelState.Values.First().Errors[0].ErrorMessage;
            return View(userModel);
           

但是這種基于session的認證使應用本身很難得到擴充,随着不同用戶端使用者的增加,獨立的伺服器已無法承載更多的使用者,而這時候基于session認證應用的問題就會暴露出來。

傳統session的主要問題如下:

1.伺服器壓力: 每個使用者經過我們的應用認證之後,我們的應用都要在服務端做一次記錄,以友善使用者下次請求的鑒别,通常而言session都是儲存在記憶體中,而随着認證使用者的增多,服務端的開銷會明顯增大。

2.擴充性: 使用者認證之後,服務端做認證記錄,如果認證的記錄被儲存在記憶體中的話,這意味着使用者下次請求還必須要請求在這台伺服器上,這樣才能拿到授權的資源,這樣在分布式的應用上,相應的限制了負載均衡器的能力。這也意味着限制了應用的擴充能力。

3.CSRF: 因為是基于cookie來進行使用者識别的, cookie如果被截獲,使用者就會很容易受到跨站請求僞造的攻擊。

二、基于token的鑒權機制

基于token的鑒權機制是無狀态的,它不需要在服務端去保留使用者的認證資訊或者會話資訊,而是基于token去運算而實作鑒權。這就意味着基于token認證機制的應用不需要去考慮使用者在哪一台伺服器登入了,這就為服務實作大規模分布式提供了基礎。

C#分布式登入——jwt一、傳統的session登入二、基于token的鑒權機制三、Json web token四、JWT的問題

上圖是一種用token登入的實作方式,類似的還有很多,雖然實作了分布式的登入處理,但是由于不同的系統之間的不同實作,導緻開發量劇增。

三、Json web token

這裡推薦使用JWT——Json web token(官網連結)。一個典型的JWT看起來如下圖:

C#分布式登入——jwt一、傳統的session登入二、基于token的鑒權機制三、Json web token四、JWT的問題

jwt為一個字元串,字元之間通過"."分隔符分為三個子串。注意JWT對象為一個長字串,各字串之間也沒有換行符,此處為了示範需要,特意分行并用不同顔色表示了。每一個子串表示了一個功能塊,總共有以下三個部分:JWT頭、有效載荷和簽名,将它們寫成一行如下:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3QiLCJpYXQiOjE1OTM5NTU5NDMsInVpZCI6MTAsImV4cCI6MTU5Mzk1NTk3Mywic2NvcGVzIjpbImFkbWluIiwidXNlciJdfQ.VHpxmxKVKpsn2Iytqc_6Z1U1NtiX3EgVki4PmA-J3Pg

一般是将它放入HTTP請求的Header Authorization字段中

Authorization: Bearer

這裡可以打開nuget:https://www.nuget.org/packages/JWT.Standard/,或者在vs中使用

C#分布式登入——jwt一、傳統的session登入二、基于token的鑒權機制三、Json web token四、JWT的問題

輸入jwt.standard找到nuget包下載下傳

1.生成jwt資料

代碼如下(示例):

var jwtp = new JWTPackage<UserModel>(new UserModel()
{
    Id = "1",
    Name = "yswenli",
    Role = "Admin"
}, 180, _pwd);
var keyValuePair = jwtp.GetAuthorizationBearer();
context.HttpContext.Response.Headers[keyValuePair.Key] = keyValuePair.Value;
           

這樣就将需要的jwt内容資訊加入到Http頭部中,當然可以使用如下方式,以參數資料的方式傳遞,進而避免跨域問題

var password = Guid.NewGuid().ToString("N");

    var jwtp1 = new JWTPackage<User>(new User()
    {
        Id = "1",
        Name = "yswenli",
        Role = "Admin"
    }, 180, password);

    var sign = jwtp1.Signature;
           

2.jwt解析驗證

JWTPackage中使用JWTPackage.Parse方法解析jwt的内容,如果内容是header中的參數,則快捷解析驗證代碼如下:

代碼如下(示例):

var result = string.Empty;
try
{
    if (context.HttpContext.Request.Headers.ContainsKey(keyValuePair.Key))
    {
        var val = context.HttpContext.Request.Headers[keyValuePair.Key].ToString();

        val = val.Replace(JWTPackage.Prex, "");

        var jwt = JWTPackage<UserModel>.Parse(val, pwd);

        result = "OK";
    }
}
catch (IllegalTokenException iex)
{
    result = $"解析失敗:{iex.Message}";
}
catch (TokenExpiredException tex)
{
    result = $"解析失敗:{tex.Message}";
}
catch (SignatureVerificationException sex)
{
    result = $"解析失敗:{sex.Message}";
}
catch (Exception ex)
{
    result = $"解析失敗:{ex.Message}";
}
return result;
           

四、JWT的問題

經過上述的簡單介紹,JWT不僅可用于認證,還可用于資訊交換,善用JWT有助于減少伺服器請求資料的次數。但是如果不正确的使用JWT也會造成安全問題。

主要幾點如下:

1.保護好secret私鑰,加密的密碼不能洩漏,否則就失去了簽名的意義了

2.Replay Attacks,JWT的消息體中最好加入生成時間,在後端中進行時間判定,小于規定時間的直接攔截

3.不應該在JWT的payload部分存放敏感資訊,因為該部分是用戶端可解密的部分

4.建議的方式是通過SSL加密的傳輸(https協定),進而避免敏感資訊被嗅探