.NET MVC5 默認的用戶登錄組件是AspNet.Identity ,支持owin,並且微軟自己實現的一套owin 中間件叫 katana
補充一下 katana項目源碼地址:https://katanaproject.codeplex.com/
如何用owin做用戶登錄 授權等這里就不詳細說了,大家可以自行搜索。
登錄就有用戶狀態,用戶狀態一般就是保存在cookie 里,cookie里肯定是保存的加密串了。
那么這個katana是如何解密跟加密呢?
翻了大半天源碼,終於找到核心的2個類 CookieAuthenticationMiddleware,CookieAuthenticationHandler
引用一下這篇比較全的文章,
作者最后說下回分解CookieAuthenticationMiddleware這個東西,一直沒等到下回,只能自己動手豐衣足食了。
CookieAuthenticationHandler 里面有2個方法
1,AuthenticateCoreAsync
2,ApplyResponseGrantAsync
前者是解密,后者是加密
我們直接看解密的
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync() { AuthenticationTicket ticket = null; try { string cookie = Options.CookieManager.GetRequestCookie(Context, Options.CookieName); if (string.IsNullOrWhiteSpace(cookie)) { return null; } ticket = Options.TicketDataFormat.Unprotect(cookie); //這里省略 ………… }
Unprotect(cookie); 這個方法就是解密的核心方法了
查找引用 找到SecureDataFormat<TData> 這個類
public TData Unprotect(string protectedText) { try { if (protectedText == null) { return default(TData); } byte[] protectedData = _encoder.Decode(protectedText); if (protectedData == null) { return default(TData); } byte[] userData = _protector.Unprotect(protectedData); if (userData == null) { return default(TData); } TData model = _serializer.Deserialize(userData); return model; } catch { // TODO trace exception, but do not leak other information return default(TData); } }
這里我們可以看到,解密步驟分成了三個步驟
Decode(解碼)
Unprotect(解除保護)
Deserialize(反序列化)
分別查看源碼后發現
解碼用的是 Base64 解保護用的windowsapi 里的CryptoAPI ,序列化用的是二進制序列化。
到這里我就停了,需要的知識已經搞清楚了。
那么我在項目里怎么解密呢?
我沒有直接用CookieAuthenticationMiddleware這個類,這個依賴較多,也可能是我沒全看懂,反正沒直接用。
既然知道三個步驟是什么了,干脆我也是3個步驟了。
Decode(解碼)用Base64UrlTextEncoder
Deserialize用TicketSerializer
這兩個類都是public 的,並且直接new 就能用的。katana源碼里也是用這兩個類。
麻煩的地方在
Unprotect
需要在Startup里面通過IAppBuilder 來創建 IDataProtector
不要問startup是什么,會用owin的都知道。
public partial class Startup { public static IDataProtector dataProtector=null; private ILog loger = LogManager.GetLogger(typeof (Startup)); public const string LoginCookieName = "xxx"; public void ConfigureAuth(IAppBuilder app) { var op = new CookieAuthenticationOptions { AuthenticationType = LoginCookieName, LoginPath = new PathString("/Login") //,ExpireTimeSpan = TimeSpan.FromHours(1) , ExpireTimeSpan = TimeSpan.FromDays(30) , SlidingExpiration = true }; app.UseCookieAuthentication(op); app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie); dataProtector = app.CreateDataProtector( typeof(CookieAuthenticationMiddleware).FullName, op.AuthenticationType, "v1"); } }
用了一個靜態變量來裝
dataProtector
(直覺用靜態變量不太好,有其他方案請不吝賜教啊)
那么真正實現解密cookie的地方就在某個Controller里
public async Task<ActionResult> DeCodeUser(string cookie) { byte[] protectedData = new Base64UrlTextEncoder().Decode(cookie); byte[] data = Startup.dataProtector.Unprotect(protectedData); var tick = new TicketSerializer().Deserialize(data); string userid = tick.Identity.GetUserId(); AppUser currentUser = await UserManager.FindByIdAsync(userid); return Json(currentUser); }
只要傳入這個需要解密的cookie就能獲取用戶信息出來了。
當然這個主要是用來探討怎么解密 cookie而已,真正你要獲取用戶信息,直接調用 controller 里User就可以了。
有人會擔心,如果知道了解密方式,那豈不是只要cookie信息被截獲 別人就能解出里面的信息?
當然不行,我寫的這個例子是因為加密 跟解密的方法都在同一台機器上運行,所以所創建的
IDataProtector 其實包含着相同的密鑰,相同的appname ,所以才能夠加密解密對稱。如果分開2台不同的機器就
應該不行了
(這個不太確定,如果知道了appname等信息不知道能不能模仿,但是還有winapi CryptoAPI 這個東西,底層實現我沒細看,有心的讀者可以去研究一下,記得把成果分享給我喲)。