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();
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM