c#關於JWT跨域身份驗證解決方案

學習程序,不是記代碼,而是學習一種思想,以及對代碼的理解和思考。php

JSON Web Token(JWT)是目前最流行的跨域身份驗證解決方案。爲了網絡應用環境間傳遞聲明而執行的一種基於JSON的開發標準(RFC 7519),java

該token被設計爲緊湊且安全的,特別適用於分佈式站點的單點登錄(SSO)場景。JWT的聲明通常被用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息,python

以便於從資源服務器獲取資源,該token也可直接被用於認證,也可被加密。json

1、JWT的組成跨域

  下面是JWT的一段示例,分爲三個部分,分別是頭部(header),載荷(payload)}和簽證(signature),他們之間用點隔開。安全

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
eyJpc3MiOiLmtYHmnIjml6Dlj4wiLCJleHAiOjE1NzExMDIxNTMsInN1YiI6InRlc3RKV1QiLCJhdWQiOiJVU0VSIiwiaWF0IjoiMjAxOS8xMC8xNSA5OjE1OjQzIiwiZGF0YSI6eyJuYW1lIjoiMTExIiwiYWdlIjoxMSwiYWRkcmVzcyI6Imh1YmVpIn19.
25IbZpAbSXBQsr2k3h0IzKRAC6z3OJTWg38VDtcEER8

 2、和傳統session的對比服務器

JWT是基於json的鑑權機制,並且是無狀態的,服務器端是沒有如傳統那樣保存客戶端的登陸信息的,這就爲分佈式開發提供了便利,網絡

由於傳統的方式是在服務端保存session信息,session是保存在內存中的,當客戶量變大的時候,對服務器的壓力天然會增大,session

最關鍵的是在集羣分佈式中,每一次登陸的服務器可能不同,那麼可能致使session保存在其中一個服務器,而另一個服務器被請求的框架

時候仍是無狀態,除非你再次登陸,這就形成了很大的麻煩,也有人說把session存放在專門的服務器,每次都去那個服務器請求,

我不認爲這是很好的解決方案,原本集羣就是爲了高可用,若是你配置session的服務器壞了,你們都跟着完蛋,因此JWT這種無狀態的方式

就很是適合這種分佈式的系統。

 3、代碼 JwtHelper

光說不練假把式,下面仍是來一段代碼。

仍是老方式,先用NuGet把JWT引用進來,這裏須要引入JWT和newtonsoft.json

以下圖所示:

 

而後就是生成JWT的方法。

 
 

  static IJwtAlgorithm algorithm = new HMACSHA256Algorithm();//HMACSHA256加密
  static IJsonSerializer serializer = new JsonNetSerializer();//序列化和反序列
  static IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();//Base64編解碼
  static IDateTimeProvider provider = new UtcDateTimeProvider();//UTC時間獲取

const string secret = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4aKpVo2OHXPwb1R7duLgg";//服務端
public
static string CreateJWT(Dictionary<string, object> payload) { IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder); return encoder.Encode(payload, secret); }

看到這段代碼,你說以爲怎麼這麼簡單,沒錯,就是這麼簡單,這個方法是被咱們引用進來的這個JWT給封裝了,因此看起來很簡單

 

 當時我看到這裏也是有點吃驚,不過我仍是對源碼進行了研究,下面貼出一段源碼給你們看看

以下所示,這段代碼是比較核心的代碼,在沒有傳遞header的時候,他幫你默認加了header,

其實下面的這段代碼很容易看懂,無非就是對header和payload進行base64加密(其實這裏說加密也不是很恰當)。

這裏的header你能夠本身傳遞進來。

public string Encode(IDictionary<string, object> extraHeaders, object payload, byte[] key) { if (payload is null) throw new ArgumentNullException(nameof(payload)); var segments = new List<string>(3); var header = extraHeaders is null ? new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase) : new Dictionary<string, object>(extraHeaders, StringComparer.OrdinalIgnoreCase); header.Add("typ", "JWT"); header.Add("alg", _algorithm.Name); var headerBytes = GetBytes(_jsonSerializer.Serialize(header)); var payloadBytes = GetBytes(_jsonSerializer.Serialize(payload)); segments.Add(_urlEncoder.Encode(headerBytes)); segments.Add(_urlEncoder.Encode(payloadBytes)); var stringToSign = String.Join(".", segments.ToArray()); var bytesToSign = GetBytes(stringToSign); var signature = _algorithm.Sign(key, bytesToSign); segments.Add(_urlEncoder.Encode(signature)); return String.Join(".", segments.ToArray()); }

 下面一段就是對JWT進行驗證的代碼,這裏的寫法都差很少,反正都是調用JWT裏面的方法,咱們傳遞參數便可。

public static bool ValidateJWT(string token, out string payload, out string message) { bool isValidted = false; payload = ""; try { IJwtValidator validator = new JwtValidator(serializer, provider);//用於驗證JWT的類
IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder);//用於解析JWT的類
payload = decoder.Decode(token, secret, verify: true);
isValidted = true;
message
= "驗證成功"; } catch (TokenExpiredException)//當前時間大於負載過時時間(負荷中的exp),會引起Token過時異常 { message = "過時了!"; } catch (SignatureVerificationException)//若是簽名不匹配,引起簽名驗證異常 { message = "簽名錯誤!"; } return isValidted; }

4、調用

class Program { static void Main(string[] args) { //載荷(payload)
            var payload = new Dictionary<string, object> { { "iss","流月無雙"},//發行人
                { "exp", DateTimeOffset.UtcNow.AddSeconds(10).ToUnixTimeSeconds() },//到期時間
                { "sub", "testJWT" }, //主題
                { "aud", "USER" }, //用戶
                { "iat", DateTime.Now.ToString() }, //發佈時間 
                { "data" ,new { name="111",age=11,address="hubei"} } }; //生成JWT
            Console.WriteLine("******************生成JWT*******************"); string  JWTString = JwtHelper.CreateJWT(payload); Console.WriteLine(JWTString); Console.WriteLine(); //校驗JWT
            Console.WriteLine("*******************校驗JWT,得到載荷***************"); string ResultMessage;//須要解析的消息
            string Payload;//獲取負載
            if (JwtHelper.ValidateJWT(JWTString, out Payload, out ResultMessage)) { Console.WriteLine(Payload); } Console.WriteLine(ResultMessage);//驗證結果說明
            Console.WriteLine("*******************END*************************"); } }

結果如圖:

 

5、總結

 一、由於json是通用的,因此jwt能夠在絕大部分平臺能夠通用,如java,python,php,.net等

 二、基於jwt是無狀態的,jwt能夠用於分佈式等如今比較流行的一些框架中。

 三、jwt自己不是加密的,因此安全性不是很高,別人知道了你的token就能夠解析了,

  固然你本身也能夠對jwt進行加密,設置的過時時間不宜過長,同時不要保存一些重要的信息,如密碼。

 四、儘可能使用https,這也是爲了安全。

 五、JWT字節佔用不多,很是的輕便,因此便於傳輸。

 六、JWT通常放在http的頭部Header中傳輸。

 

---若有錯誤歡迎指出,你們相互進步。

相關文章
相關標籤/搜索