英文原文:http://tech.trailmax.info/2014/08/aspnet-identity-and-owin-who-is-who/數據庫
最近我發現Stackoverflow上有一個很是好的問題.提問者問:爲何在調用AuthenticationManager.SignIn後,claim仍然能夠被添加到Identity並持久化到cookie裏.安全
示例代碼以下所示:cookie
ClaimsIdentity identity = UserManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie ); var claim1 = new Claim(ClaimTypes.Country, "Arctica"); identity.AddClaim(claim1); AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = true }, identity ); var claim2 = new Claim(ClaimTypes.Country, "Antartica"); identity.AddClaim(claim2);
是的,爲何claim2在cookie已經設置完成後還可用.app
在深刻研究後,我發現AspNet Identity框架不設置cookie,而OWIN會設置,OWIN是Katana開源項目的一部分.有源碼可用是一件好事--你能夠發現爲何事情有或沒有按你預期的方式工做.框架
在這個案例裏,我花了一些時間探索Katana項目和 AuthenticationManager 工做方式.結果證實SignIn方法不設置cookie.它把Identity對象保存在內存裏,直到設置響應cookies的時刻到來,而後claims被轉化爲一個cookie,全部的事情就這樣魔法般地工做着 -)async
這又引起了另外一個問題.現下Identity沒有開源的代碼,因此OWIN在Identity中扮演什麼角色,Claims又是如何工做的?ide
結果證實Identity框架只處理user持久化,密碼哈希,驗證密碼是否正確,發送密碼重置郵件,等等.可是Identity實際上不驗證users或建立cookies.而Cookies是被OWIN處理的.學習
看一下登陸的代碼:flex
public async Task SignInAsync(Microsoft.Owin.Security.IAuthenticationManager authenticationManager, ApplicationUser applicationUser, bool isPersistent) { authenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie); ClaimsIdentity identity = await UserManager.CreateIdentityAsync(applicationUser, DefaultAuthenticationTypes.ApplicationCookie); authenticationManager.SignIn(new Microsoft.Owin.Security.AuthenticationProperties() { IsPersistent = isPersistent }, identity); }
Identity只建立ClaimsIdentity(學習網站 ReferenceSource ),而ClaimsIdentity是.Net framework的一部分,而不是來自於互聯網的nuget包.而後這個ClaimsIdentity被傳給擁有一個設置cookies回調的OWIN的AuthenticationManager,而AuthenticationManager擁有一個在寫響應頭時設置cookies的回調.網站
到目前爲止都很好,已有三部分:Identity框架建立一個ClaimsIdentity,OWIN根據這個ClaimsIdentity建立一個cookie,和.Net framework掌控ClaimsIdentity的類.
當在你的類中要訪問ClaimsPrincipal.Current時,你只用到.Net framework,不須要用到其它類庫,這是很是方便的!
默認的Claims
Identity框架爲你作了一件很漂亮的事,默認狀況下當你登陸時,它爲一個principal添加了一些claims,以下所示:
你能夠在.Net Reference 網站查看這些claim類型,這個列表不是完整的,你能夠建立你本身的claim類型--就是一個string.
若是你想添加你本身的owin claim類型,我建議你使用本身的符號,例如:「MyAppplication:GroupId」 ,並保持全部的claim類型做爲常量在一個類中:
public class MyApplicationClaimTypes { public string const GroupId = "MyAppplication:GroupId"; public string const PersonId = "MyAppplication:PersonId"; // other claim types }
這種方式,你老是能夠找到claims,並不會與框架中的claim類型衝突,除非你的claims與框架中的claims類型徹底一致,例如:ClaimTypes.Email.
添加默認的claims
我老是在user登陸裏,添加user的email到claims列表中,就如最前面示例裏的claim1和claim2:
public async Task SignInAsync(IAuthenticationManager authenticationManager, ApplicationUser applicationUser, bool isPersistent) { authenticationManager.SignOut( DefaultAuthenticationTypes.ExternalCookie, DefaultAuthenticationTypes.ApplicationCookie); var identity = await this.CreateIdentityAsync(applicationUser, DefaultAuthenticationTypes.ApplicationCookie); // using default claim type from the framework identity.AddClaim(new Claim(ClaimTypes.Email, applicationUser.Email)); authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity); }
你能夠在這裏爲全部user添加默認的claims,但有一個IClaimsIdentityFactory類(賦給UserManager),只有一個方法:
public interface IClaimsIdentityFactory<TUser, TKey> where TUser : class, IUser<TKey> where TKey : IEquatable<TKey> { /// <summary> /// Create a ClaimsIdentity from an user using a UserManager /// </summary> Task<ClaimsIdentity> CreateAsync(UserManager<TUser, TKey> manager, TUser user, string authenticationType); }
AspNet Identity的默認實現是:建立ClaimsIdentity,添加如上所述的默認claims,爲user在數據庫中存儲IdentityUserClaims類型的claims.你能夠重寫這個實現,並插入你本身的邏輯/claims:
public class MyClaimsIdentityFactory : ClaimsIdentityFactory<ApplicationUser, string> { public override async Task<ClaimsIdentity> CreateAsync(UserManager<ApplicationUser, string> userManager, ApplicationUser user, string authenticationType) { var claimsIdentity = await base.CreateAsync(userManager, user, authenticationType); claimsIdentity.AddClaim(new Claim("MyApplication:GroupId", "42")); return claimsIdentity; } }
而後在賦給UserManger:
public UserManager(MyDbContext dbContext) : base(new UserStore<ApplicationUser>(dbContext)) { // other configurations // Alternatively you can have DI container to provide this class for better application flexebility this.ClaimsIdentityFactory = new MyClaimsIdentityFactory(); }