2015-01-07 15 views
5

Istnieje wiele próbek online za pomocą OWIN/Katana znaleźć użytkowników w bazie danych opartej na ausername połączeniu/hasło i generować kapitał zastrzeżeń, takie jak ...Dostosowywanie zachowanie fabrycznego OWIN/Katana UserManager

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

To dobrze, jeśli tworzysz nową aplikację i chcesz, aby Entity Framework wykonywał brudną robotę. Ale mam ośmio-letnią monolityczną stronę internetową, która właśnie została zaktualizowana, aby używać uwierzytelniania opartego na roszczeniach. Nasze trafienie w bazę danych jest wykonywane ręcznie za pomocą DAL/SQL, a następnie generowane jest narzędzie ClaimsIdentity.

Niektórzy sugerują, że OWIN jest łatwiejszy w użyciu niż nasze podejście manualne, ale chciałbym trochę informacji od tych, którzy go używają.

Czy można zmienić sposób, w jaki fabryka UserManager wyszukuje użytkowników na podstawie ich poświadczeń? Czy istnieje inne podejście, które przegapiłem? Wszystkie próbki, które mogę znaleźć online, wydają się wykorzystywać podejście oparte na standardach, pozwalając Entity Framework tworzyć bazę danych i zarządzać wyszukiwaniami.

+0

Tak można dostosować sposób UserManager zachowuje się. Sposób uzyskiwania danych odbywa się poprzez implementację 'IUserStore'. Oto odpowiedź, która może Ci pomóc w dostosowaniu tego. http://stackoverflow.com/questions/19940014/asp-net-identity-with-ef-database-first-mvc5/21122865#21122865 – Shoe

Odpowiedz

12

Identyfikacja ASP.NET jest nieco zbyt skomplikowana, powiedziałbym.
W sierpniu 2014 r. Ogłosili nową wersję 2.1, a sytuacja znów się zmieniła.
Przede wszystkim niech pozbyć EntityFramework:

Uninstall-Package Microsoft.AspNet.Identity.EntityFramework 

Teraz możemy realizować naszą własną definicję User wykonawczych interfejs IUser (Microsoft.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; } 
} 

jak można zobacz Mam zdefiniowany typ mojego Id (int).

Następnie należy zdefiniować niestandardowe UserManager dziedzicząc po Microsoft.AspNet.Identity.UserManager, określając typ użytkownika i typ klucza.

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, 
     }; 
    } 
} 

I zostały wdrożone moje zasady weryfikacji tutaj, ale można zachować go na zewnątrz, jeśli wolisz.

UserManager potrzebuje UserStore (IUserStore).

Tutaj zdefiniujesz swoją logikę DB. Istnieje kilka interfejsów do wdrożenia. Nie wszystkie z nich są jednak obowiązkowe.

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

} 

Nie uwzględniłem wszystkich metod dla każdego interfejsu. Kiedy już to zrobisz, że będziesz w stanie napisać nowego użytkownika:

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

sprowadzić go przez ID:

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

i tak dalej.

Następnie musisz zdefiniować swoją SignInManager dziedzicząc po Microsoft.AspNet.Identity.Owin.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); 
    } 
} 

mam tylko realizowane SignInAsync: To generuje ClaimsIdentity.

To wszystko.

Teraz w klasie Startup musisz powiedzieć Owin, jak utworzyć UserManager i SignInManager.

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

Nie używałem fabryki znajdą Państwo w domyślnym szablonie bo chciał zachować rzeczy tak proste, jak to możliwe.

i umożliwić aplikacji do korzystania z cookies:

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

Teraz w kontrolerze konta - lub kontrolera odpowiedzialnego za logowania - trzeba będzie uzyskać UserManager i SignInManager:

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

Będziesz używać SignInManager do logowania:

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

i UserManager stworzyć użytkownika, dodawanie ról i roszczeń:

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

Wydaje się komplikować ... i to jest ... rodzaju.

Jeśli chcesz przeczytać więcej na ten temat, istnieje dobre wyjaśnienie: here i here.

Jeśli chcesz uruchomić kod i zobaczyć, jak to działa, przygotowałem dla ciebie code, który działa z Biggy (ponieważ nie chciałem tracić czasu na definiowanie tabel i podobnych rzeczy).

Jeśli masz szansę pobrać mój kod z repozytorium github, zauważysz, że utworzyłem projekt wtórny (Custom.Identity), w którym przechowywałem wszystkie moje rzeczy związane z ASP.NET Identity.

Jedynym Nuget pakiety będą potrzebne są:

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

Niesamowita odpowiedź - dziękuję bardzo. Tyle kodu jednak na coś stosunkowo prostego. Jaki jest sterownik tego wszystkiego? Czy pozwala aplikacji nie wiedzieć, * gdzie * dane faktycznie znajdują się, np. lepsza interoperacyjność? – EvilDr

+1

Zgódź się z tobą. Zastanawiam się, czy naprawdę musimy przejść przez wszystkie te kroki, aby wdrożyć coś, co powinno być proste. Sterownik ma mieć własne tabele/schematy dla bezpieczeństwa.Ja - osobiście - nie lubię struktury encji i chcę się jej pozbyć. Możliwość rozszerzania. W pewnym momencie możesz zdecydować się na przejście do niektórych baz danych NoSql. – LeftyX

+1

Zgadzam się co do struktury obiektu. Ciekawe, w jaki sposób poznałeś powyższy kod? Czy ktoś wskazał ci właściwy kierunek i wybrałeś go z doświadczenia? Uważam, że nie mam wystarczająco dużo czasu w ciągu dnia, aby nadążyć za szybkimi zmianami ASP.NET ... – EvilDr