AspNet Identity 和 Owin 誰是誰

英文原文: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,以下所示:

  • 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();
}
相關文章
相關標籤/搜索