英文原文: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(); }