本文版權歸博客園和作者吳雙本人共同所有,轉載和爬蟲請註明原文地址 www.cnblogs.com/tdws 相信瞭解了MS Identity認證體系的一定知道UserManager的作用,他是整個體系中的調度者,他定義了一套用戶行為來幫助我們管理用戶信息,角色信息,處理密碼等。而其實現則在UserS ...
本文版權歸博客園和作者吳雙本人共同所有,轉載和爬蟲請註明原文地址 www.cnblogs.com/tdws
相信瞭解了MS Identity認證體系的一定知道UserManager的作用,他是整個體系中的調度者,他定義了一套用戶行為來幫助我們管理用戶信息,角色信息,處理密碼等。而其實現則在UserStore當中,我們可以實現其為我們定義的比如IUserStore,IUserPasswordStore,IRoleStore等等. 我們可以基於一整套用戶行為,自定義自己的用戶信息和數據結構以及數據存儲。那麼關於Password的Hasher,MS依然為我們提供了完整的行為定義,也由UserManager來調度。比如
1 UserManager.PasswordHasher.HashPassword(password)
PasswordHasher在UserManager介面中是這樣定義的:
我原本對其預設實現是沒有興趣的,出於獨立多個應用的登陸認證的目的,所以需要一個獨立的用戶認證項目來作為認證服務,其僅生產token,認證成功後,用戶的HTTP Request Header的Authorization帶著 token來訪問應用伺服器上的各種資源。
就是因為這樣的原因,在多個應用的密碼認證上出現了這樣一個問題:
比如應用A採用了實現IPasswordHasher來自定義加密方式——MD5+salt的形式,而應用B則採用了Identity預設的PasswordHasher來實現,通過反編譯得到如下代碼:
所以為了相容多個應用不同的加密方式,我不得不反編譯出源碼,拿到其預設加密方式,根據不同應用名稱,來判斷對密碼加密或者解密,或者直接通過某種方式來對比資料庫和用戶輸入的密碼。先上MS預設的PasswordHasher具體實現
1 // Decompiled with JetBrains decompiler
2 // Type: Microsoft.AspNet.Identity.Crypto
3 // Assembly: Microsoft.AspNet.Identity.Core, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
4 // MVID: E3A10FFD-023A-4BC3-AD53-32D145ABF1C9
5 // Assembly location: C:\Sport\NewProject\V2.0\Api\Fantasy.Sport\packages\Microsoft.AspNet.Identity.Core.2.2.1\lib\net45\Microsoft.AspNet.Identity.Core.dll
6
7 using System;
8 using System.Runtime.CompilerServices;
9 using System.Security.Cryptography;
10
11 namespace Microsoft.AspNet.Identity
12 {
13 internal static class Crypto
14 {
15 private const int PBKDF2IterCount = 1000;
16 private const int PBKDF2SubkeyLength = 32;
17 private const int SaltSize = 16;
18
19 public static string HashPassword(string password)
20 {
21 if (password == null)
22 throw new ArgumentNullException("password");
23 byte[] salt;
24 byte[] bytes;
25 using (Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, 16, 1000))
26 {
27 salt = rfc2898DeriveBytes.Salt;
28 bytes = rfc2898DeriveBytes.GetBytes(32);
29 }
30 byte[] inArray = new byte[49];
31 Buffer.BlockCopy((Array) salt, 0, (Array) inArray, 1, 16);
32 Buffer.BlockCopy((Array) bytes, 0, (Array) inArray, 17, 32);
33 return Convert.ToBase64String(inArray);
34 }
35
36 public static bool VerifyHashedPassword(string hashedPassword, string password)
37 {
38 if (hashedPassword == null)
39 return false;
40 if (password == null)
41 throw new ArgumentNullException("password");
42 byte[] numArray = Convert.FromBase64String(hashedPassword);
43 if (numArray.Length != 49 || (int) numArray[0] != 0)
44 return false;
45 byte[] salt = new byte[16];
46 Buffer.BlockCopy((Array) numArray, 1, (Array) salt, 0, 16);
47 byte[] a = new byte[32];
48 Buffer.BlockCopy((Array) numArray, 17, (Array) a, 0, 32);
49 byte[] bytes;
50 using (Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, salt, 1000))
51 bytes = rfc2898DeriveBytes.GetBytes(32);
52 return Crypto.ByteArraysEqual(a, bytes);
53 }
54
55 [MethodImpl(MethodImplOptions.NoOptimization)]
56 private static bool ByteArraysEqual(byte[] a, byte[] b)
57 {
58 if (object.ReferenceEquals((object) a, (object) b))
59 return true;
60 if (a == null || b == null || a.Length != b.Length)
61 return false;
62 bool flag = true;
63 for (int index = 0; index < a.Length; ++index)
64 flag &= (int) a[index] == (int) b[index];
65 return flag;
66 }
67 }
68 }
有人可能會問,拿到了這些源碼要如何應用呢。下麵就是淺述到這個問題。
一開始我天真的認為,不就是一個加密麽,不用仔細看了,拿來用就好了?
在註冊用戶和修改密碼的時候,都是通過上面 HashPassword 方法來做的密碼加密,那我在新的自定義PasswordHasher中,為應用B對比用戶登錄密碼的時候,把用戶輸入直接通過HashPassword加密一邊不就好了?所以我自定義的VerifyHashedPassword (Verify譯為核實)方法,就是比較資料庫中的Pwd和經過hasher處理的結果是否相等。 可結果是,每次相同的字元串,會產生不同的加密結果,和以前玩md5+salt不一樣呀。所以我又想到了其預設實現的 VerifyHashedPassword 方法。
所以最後要說的就是 你可以拿來微軟Identity的加密方式(上面的Hasher)直接使用 , 在比較用戶輸入和資料庫中已經經過hash的存儲結果進行對比的時候,使用其 VerifyHashedPassword()方法。即使不使用Identity認證 也可以用此加密演算法。
如果需要更多瞭解Identity和OWIN,請看騰飛的如下四篇文章:
http://www.cnblogs.com/jesse2013/category/551550.html
如果,您認為閱讀這篇博客讓您有些收穫,不妨點擊一下右下加【推薦】按鈕。
如果,您希望更容易地發現我的新博客,不妨點擊下方紅色【關註】的。
因為,我的分享熱情也離不開您的肯定支持。
感謝您的閱讀,我將持續輸出分享,我是蝸牛, 保持學習,謹記謙虛。不端不裝,有趣有夢。