2015-01-07 18 views
5

有很多樣品在網上使用OWIN /卡塔納找到基於ausername /密碼組合的數據庫用戶,並生成一個聲明主體,如...定製OWIN /武士刀的UserManager工廠行爲

var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>(); 
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password); 
// generate claims here... 

這很好,如果你正在創建一個新的應用程序,並希望實體框架做骯髒的工作。但是,我有一個八年前的單體網站,剛剛更新爲使用基於聲明的身份驗證。我們的數據庫命中是通過DAL/SQL手動完成的,然後從那裏生成ClaimsIdentity。

有些人暗示OWIN是很容易,我們的手工方式使用,但我想從那些使用它的一些輸入。

是否可以改變UserManager工廠根據用戶憑據找到用戶的方式?或者,還有另一種我錯過的方法嗎?我可以在網上找到的所有示例似乎都使用了讓Entity Framework創建數據庫並管理搜索的樣板化方法。

+0

是的,你可以自定義的UserManager的行爲。它獲取數據的方式是通過實現'IUserStore'。這裏有一個答案可以幫助你解決如何自定義這個問題。 http://stackoverflow.com/questions/19940014/asp-net-identity-with-ef-database-first-mvc5/21122865#21122865 – Shoe

回答

12

ASP.NET身份有點過於複雜,我會說。
2014年8月,他們宣佈了新版本2.1,事情又改變了。
首先,讓我們擺脫EntityFramework

Uninstall-Package Microsoft.AspNet.Identity.EntityFramework 

現在我們執行我們自己的User定義實現接口IUserMicrosoft.AspNet.Identity):

public class User: IUser<int> 
{ 
    public User() 
    { 
     this.Roles = new List<string>(); 
     this.Claims = new List<UserClaim>(); 
    } 

    public User(string userName) 
     : this() 
    { 
     this.UserName = userName; 
    } 

    public User(int id, string userName): this() 
    { 
     this.Id = Id; 
     this.UserName = userName; 
    } 

    public int Id { get; set; } 
    public string UserName { get; set; } 
    public string PasswordHash { get; set; } 

    public bool LockoutEnabled { get; set; } 
    public DateTime? LockoutEndDateUtc { get; set; } 
    public bool TwoFactorEnabled { get; set; } 

    public IList<string> Roles { get; private set; } 
    public IList<UserClaim> Claims { get; private set; } 
} 

,你可以請參閱我已經定義了我的Idint)的類型。

然後,您必須定義從Microsoft.AspNet.Identity.UserManager繼承的自定義UserManager,指定您的用戶類型和密鑰類型。

public class UserManager : UserManager<User, int> 
{ 
    public UserManager(IUserStore<User, int> store): base(store) 
    { 
     this.UserLockoutEnabledByDefault = false; 
     // this.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(10); 
     // this.MaxFailedAccessAttemptsBeforeLockout = 10; 
     this.UserValidator = new UserValidator<User, int>(this) 
     { 
      AllowOnlyAlphanumericUserNames = false, 
      RequireUniqueEmail = false 
     }; 

     // Configure validation logic for passwords 
     this.PasswordValidator = new PasswordValidator 
     { 
      RequiredLength = 4, 
      RequireNonLetterOrDigit = false, 
      RequireDigit = false, 
      RequireLowercase = false, 
      RequireUppercase = false, 
     }; 
    } 
} 

我已經在這裏實施了我的驗證規則,但如果您願意,您可以將它保留在外面。

UserManager需要一個UserStoreIUserStore)。

您將在此處定義您的DB邏輯。有幾個接口可以實現。但並非所有這些都是強制性的。

public class UserStore : 
    IUserStore<User, int>, 
    IUserPasswordStore<User, int>, 
    IUserLockoutStore<User, int>, 
    IUserTwoFactorStore<User, int>, 
    IUserRoleStore<User, int>, 
    IUserClaimStore<User, int> 
{ 

    // You can inject connection string or db session 
    public UserStore() 
    { 
    } 

} 

我沒有包括每個接口的所有方法。一旦你這樣做,你就可以寫你的新用戶:

public System.Threading.Tasks.Task CreateAsync(User user) 
{ 
} 

憑身份證取,

public System.Threading.Tasks.Task<User> FindByIdAsync(int userId) 
{ 
} 

等。

然後,您需要定義從Microsoft.AspNet.Identity.Owin.SignInManager繼承的SignInManager

public class SignInManager: SignInManager<User, int> 
{ 
    public SignInManager(UserManager userManager, IAuthenticationManager authenticationManager): base(userManager, authenticationManager) 
    { 
    } 

    public override Task SignInAsync(User user, bool isPersistent, bool rememberBrowser) 
    { 
     return base.SignInAsync(user, isPersistent, rememberBrowser); 
    } 
} 

我只實現SignInAsync:它會產生一個ClaimsIdentity

就是這樣。

現在在您的Startup類中,您必須告知Owin如何創建UserManagerSignInManager

app.CreatePerOwinContext<Custom.Identity.UserManager>(() => new Custom.Identity.UserManager(new Custom.Identity.UserStore())); 
// app.CreatePerOwinContext<Custom.Identity.RoleManager>(() => new Custom.Identity.RoleManager(new Custom.Identity.RoleStore())); 
app.CreatePerOwinContext<Custom.Identity.SignInService>((options, context) => new Custom.Identity.SignInService(context.GetUserManager<Custom.Identity.UserManager>(), context.Authentication)); 

我沒用過,你會在默認的模板,因爲我希望讓事情儘可能的簡單找到工廠。

而且使應用程序可以使用cookie:

app.UseCookieAuthentication(new CookieAuthenticationOptions 
{ 
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, 
     LoginPath = new PathString("/Account/Login"), 
     Provider = new CookieAuthenticationProvider 
     { 
     // Enables the application to validate the security stamp when the user logs in. 
     // This is a security feature which is used when you change a password or add an external login to your account. 
     OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<Custom.Identity.UserManager, Custom.Identity.User, int>(
     validateInterval: TimeSpan.FromMinutes(30), 
     regenerateIdentityCallback: (manager, user) => 
     { 
     var userIdentity = manager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie); 
       return (userIdentity); 
    }, 
     getUserIdCallback: (id) => (Int32.Parse(id.GetUserId())) 
     )} 
}); 

現在,在您的帳戶控制器 - 或控制器負責登錄 - 你必須得到UserManagerSignInManager

public Custom.Identity.SignInManager SignInManager 
{ 
    get 
    { 
    return HttpContext.GetOwinContext().Get<Custom.Identity.SignInManager>(); 
    } 
} 

public Custom.Identity.UserManager UserManager 
{ 
    get 
    { 
    return HttpContext.GetOwinContext().GetUserManager<Custom.Identity.UserManager>(); 
    } 
} 

您將使用SignInManager進行登錄:

var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false); 

UserManager創建用戶,添加角色和索賠:

if (ModelState.IsValid) 
{ 
     var user = new Custom.Identity.User() { UserName = model.Email }; 

     var result = await UserManager.CreateAsync(user, model.Password); 
     if (result.Succeeded) 
    { 
     // await UserManager.AddToRoleAsync(user.Id, "Administrators"); 
       // await UserManager.AddClaimAsync(user.Id, new System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.Country, "England")); 

       await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false); 

     return RedirectToAction("Index", "Home"); 
    } 
     AddErrors(result); 
} 

看來複雜...這是...種。

如果您想了解更多關於它的信息,請點擊這裏herehere

如果你想運行一些代碼,看看它是如何工作的,我已經加入了一些code,它與Biggy一起工作(因爲我不想浪費很多時間來定義表格和類似的東西)。

如果您有機會從github repo下載我的代碼,您會注意到我已經創建了一個輔助項目(Custom.Identity),我已經將所有的東西都保存在了我的產品中。

唯一的NuGet包,你需要有:

  1. Microsoft.AspNet.Identity.Core
  2. Microsoft.AspNet.Identity.Owin
+0

令人驚歎的答案 - 非常感謝。儘管對於一些相對簡單的代碼來說可能太多這一切的驅動力是什麼?是否允許應用程序不知道數據實際存在的位置,例如更好的互操作性? – EvilDr

+1

同意你的意見。我想知道我們是否真的需要通過所有這些步驟來實現應該是直截了當的事情。驅動程序是爲了安全而擁有自己的表/模式。我 - 個人 - 不喜歡實體框架,並想擺脫它。可擴展性也許。在某些時候,你可能會決定切換到一些NoSql數據庫。 – LeftyX

+1

同意實體框架。出於興趣,你是如何熟悉上述代碼的?有人指出你在正確的方向,你從經驗中擷取它?我發現我沒有足夠的時間來跟上ASP.NET的快速變化...... – EvilDr