zoukankan      html  css  js  c++  java
  • AspNet Identity 和 Owin 谁是谁

    英文原文:http://tech.trailmax.info/2014/08/aspnet-identity-and-owin-who-is-who/

    最近我发现Stackoverflow上有一个非常好的问题.提问者问:为什么在调用AuthenticationManager.SignIn后,claim仍然可以被添加到Identity并持久化到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已经设置完成后还可用.

    在深入研究后,我发现AspNet Identity框架不设置cookie,而OWIN会设置,OWIN是Katana开源项目的一部分.有源码可用是一件好事--你可以发现为什么事情有或没有按你预期的方式工作.

    在这个案例里,我花了一些时间探索Katana项目和 AuthenticationManager 工作方式.结果证明SignIn方法不设置cookie.它把Identity对象保存在内存里,直到设置响应cookies的时刻到来,然后claims被转化为一个cookie,所有的事情就这样魔法般地工作着 -)

    这又引发了另一个问题.现下Identity没有开源的代码,所以OWIN在Identity中扮演什么角色,Claims又是如何工作的?

    结果证明Identity框架只处理user持久化,密码哈希,验证密码是否正确,发送密码重置邮件,等等.但是Identity实际上不验证users或创建cookies.而Cookies是被OWIN处理的.

    看一下登录的代码:

    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,如下所示:

    • User.Id:类型为“http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier” 或ClaimTypes.NameIdentifier.
    • Username:类型为“http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name” 或ClaimTypes.Name.
    • "ASP.NET Identity":保存为“http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider“.这在你使用OpenId做验证时非常有用.不过如果只是使用数据库存储users时没什么用.点击查看更多信息.
    • 包含user的安全邮戳的Guid,在Claim中持久化类型为“AspNet.Identity.SecurityStamp“.安全邮戳是user状态的一个主要快照,如果验证的密码/方法,email,等等发生变化,安全邮戳就会发生变化,这允许你通过改变证书实现"在任何地方登出".从Kung的回答中获取更多有关安全邮戳的信息.
    • 最有用的claims是role.所有分配给user的role被保存成ClaimTypes.Role或“http://schemas.microsoft.com/ws/2008/06/identity/claims/role“.所以下次你需要检查当前user的roles,检查这个claims,不会到数据库中查找,这样非常快.实际上,如果你调用ClaimsPrincipal的.IsInRole("RoleName"),框架会进入claims并检查用户是否分配了这个指定值的Role.

    你可以在.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();
    }
  • 相关阅读:
    使用Docker-compose部署MySQL测试环境
    使用MySQL SQL线程回放Binlog实现恢复
    MySQL 插件之 连接控制插件(Connection-Control)
    sysbench工具使用
    故障分析--主从复制故障1
    MySQL性能指标计算方式
    AJAX的 同步异步;EZView.js 图片预览和pdf预览
    Caused by: java.lang.ClassNotFoundException: org.springframework.context.App
    解决Myeclipse或Eclipse出现JPA project Change Event Handler问题的解决办法
    java中转换为string的方法;eques和==区别
  • 原文地址:https://www.cnblogs.com/kid1412/p/6403518.html
Copyright © 2011-2022 走看看