2009-09-17 17 views
58

Mam istniejącą bazę danych z tabelą użytkowników i planujemy wzięcie bazy danych i użycie jej dla nowego systemu zbudowanego w ASP.NET MVC. Nie mam jednak pewności, czy mogę utworzyć system logowania, który nie korzysta z wbudowanego kontrolera konta lub zwykłego dostawcy członkostwa, abyśmy mogli nadal korzystać z istniejącej struktury tabeli.Czy można utworzyć system logowania za pomocą środowiska ASP.NET MVC, ale nie można użyć narzędzia MembershipProvider?

Moje pytanie brzmi: czy to będzie możliwe? A może nawet szczególnie trudne do zrobienia, jeśli tak jest?

Jaki jest najszerzej akceptowany sposób robienia rzeczy i najprostszy?

Odpowiedz

81

Miałem dokładnie to samo wymaganie. Miałem swój własny schemat roli i użytkownika i nie chciałem migrować do schematu członkostwa asp.net, ale chciałem użyć filtrów akcji ASP.NET MVC do sprawdzania autoryzacji i ról. Musiałem sporo kopać, żeby dowiedzieć się, co trzeba zrobić, ale w końcu było to stosunkowo łatwe. Uratuję ci kłopot i powiem ci, co zrobiłem.

1) Stworzyłem klasę wywodzącą się z System.Web.Security.MembershipProvider. MembershipProvider ma mnóstwo abstrakcyjnych metod dla wszelkiego rodzaju funkcji związanych z uwierzytelnianiem, takich jak zapomniane hasło, zmiana hasła, tworzenie nowego użytkownika itp. Wszystko, czego chciałem, to możliwość uwierzytelnienia na podstawie własnego schematu. Tak więc moja klasa zawierała głównie puste przesłonięcia. Właśnie overrode ValidateUser:

public override bool ValidateUser(string username, string password) 
{ 
    if (string.IsNullOrWhiteSpace(username) || 
     string.IsNullOrWhiteSpace(password)) 
     return false; 

    string hash = EncryptPassword(password); 
    User user = _repository.GetByUserName(username); 
    if (user == null) return false; 

    return user.Password == hash; 
} 

2) Stworzyłem klasę, że pochodzący z System.Web.Security.RoleProvider. Znowu miałem puste implementacje dla całego puchu, którego nie potrzebowałem, jak tworzenie i zmianę ról. Właśnie overrode dwie metody:

public override string[] GetRolesForUser(string username) 
{ 
    User user = _repository.GetByUserName(username); 
    string[] roles = new string[user.Role.Rights.Count + 1]; 
    roles[0] = user.Role.Description; 
    int idx = 0; 
    foreach (Right right in user.Role.Rights) 
     roles[++idx] = right.Description; 
    return roles; 
} 

public override bool IsUserInRole(string username, string roleName) 
{ 
    User user = _repository.GetByUserName(username); 
    if(user!=null) 
     return user.IsInRole(roleName); 
    else 
     return false; 
} 

3) Następnie podłączyłem te dwie klasy do mojego web.config:

<membership defaultProvider="FirstlookMemberProvider" userIsOnlineTimeWindow="15"> 
    <providers> 
    <clear/> 
    <add name="FirstlookMemberProvider" type="FirstlookAdmin.DomainEntities.FirstlookMemberProvider, FirstlookAdmin" /> 
    </providers> 
</membership> 
<roleManager defaultProvider="FirstlookRoleProvider" enabled="true" cacheRolesInCookie="true"> 
    <providers> 
    <clear/> 
    <add name="FirstlookRoleProvider" type="FirstlookAdmin.DomainEntities.FirstlookRoleProvider, FirstlookAdmin" /> 
    </providers> 
</roleManager> 

to wszystko. Domyślne filtry akcji autoryzacji będą korzystać z tych klas. Nadal będziesz musiał zalogować się na stronie logowania i się wylogować. Po prostu używaj standardowych klas uwierzytelniania formularzy w taki sposób, jak zwykle.

+1

+1. Dostosowanie dostawcy jest prawidłowym sposobem na zrobienie tego. Dziękuję za uwagę, że niekoniecznie musi to być dużo pracy. –

+4

Och, warto zauważyć, że hasło powinno być naprawdę solone z dodatkiem przed haszowaniem. –

+0

Przykro mi, że ożywam to i przeszkadzam, ale czy można uzyskać więcej informacji na temat rozwiązania? Jestem nieco zdezorientowany, jak radziłeś sobie z zarządzaniem rolą. – Ciel

10

Oczywiście, że możesz. Zrobiłem to dla moich projektów całkowicie ignorując dostawcę członkostwa.

Musisz zaimplementować własny ActionFilter. Zasadniczo, przechwytuje kontrolę przed trafieniem akcji kontrolera. Wewnątrz tego zdecydujesz, czy kontynuować akcję, czy przekierujesz użytkownika na stronę logowania.

Dla tego atrybutu można zdefiniować dowolne parametry potrzebne do obsługi modelu uwierzytelniania/autoryzacji.

public class AuthorizationAttribute : ActionFilterAttribute, IActionFilter 
{ 
    public MyRole UserRole { get; set; } 

    void IActionFilter.OnActionExecuting (ActionExecutedContext filterContext) 
    { 
     // Decide whether to grant access to the action or redirect away 
    } 
} 

[Authorization (UserRole = MyRole.All)] 
public class UserController : Controller 
{ 
    [Authorization (UserRole = MyRole.Admin)] 
    public ActionResult Delete() 
    { 
    } 
} 

Odnośnie do obaw wyrażonych w komentarzach. Tak, włączenie wyjściowej pamięci podręcznej zakłóci autoryzację. Trzeba tylko zdawać sobie z tego sprawę.

Wyjaśnienie problemu: ASP.NET MVC Tip #40 - Don’t Cache Pages that Require Authorization

+0

Czy można tworzyć niestandardowe? Oczywiście firmy różnią się, więc możesz potrzebować 'Role.IsAccountsAdmin' zamiast tylko' Role.Admin'. – Kezzer

+0

Są to niestandardowe, tylko nazwy pokrywają się. Zaktualizuje je za chwilę, aby usunąć niejednoznaczność. –

+0

Czy po prostu przejdzie to do wstępnie wygenerowanego pliku AccountController, czy też muszę utworzyć własne? – LiamGu

1

mieć co najmniej dwa możliwości

  • atrybut filtru niestandardowego działania, które zapewnią czek autoryzacji
  • zwyczaj IHttpModule że wypełni wszystkie niezbędne dane dla zalogowanego użytkownika (w tym ról) i można użyć istniejących filtrów działania

Drugi wybór może być również używany w zwykłych formularzach internetowych.

11

Ilekroć ktoś ci mówi, że coś związanego z bezpieczeństwem jest "łatwe", prawie zawsze są w błędzie. Istnieje wiele subtelności w bezpieczeństwie, których nie mają eksperci.

W szczególności, każda forma uwierzytelniania, która nie dotyczy jawnie buforowania, jest z natury uszkodzona. Gdy wynik akcji jest buforowany, dzieje się to w ASP.NET, niekoniecznie w stosie ASP.NET MVC. Jeśli przyjrzysz się kodowi źródłowemu AuthorizeAttribute, zobaczysz, że zawiera on trochę delikatny, ale skuteczny kod, który zapewnia, że ​​zawsze będzie działał, nawet jeśli wynik działania zostanie zapisany w pamięci podręcznej.

The best way, by far, to customize ASP.NET MVC authentication is to write a custom ASP.NET membership provider. Nie będę twierdził, że jest to niezawodny, ale istnieje mniej sposobów, aby wpaść w kłopoty ze złamaniem zabezpieczeń na tej trasie, a następnie innymi metodami. Istotną zaletą tej techniki jest to, że można zastąpić inny system autoryzacji prawie w dowolnym momencie bez żadnych zmian kodu.

Jeśli musisz zaimplementować niestandardowy atrybut MVC, powinieneś podtyp AuthorizeAttribute i zastąpić AuthorizeCore, zwracając uwagę na komentarze w kodzie źródłowym dotyczące bezpieczeństwa wątków.

+0

Obecnie nie mam "ustawionego" sposobu robienia rzeczy, wiem, że muszę użyć istniejącej struktury danych i tabel, więc najprostszy, najprostszy i najbezpieczniejszy sposób robienia tego jest idealnie to, co ja " m po. Czy napisanie niestandardowego dostawcy członkostwa jest szczególnie łatwe? Nie robiłem tego wcześniej i na pierwszy rzut oka wydaje się to trochę trudniejsze niż w rzeczywistości? – LiamGu

+1

Oto wideo http://www.asp.net/learn/videos/video-189.aspx oraz artykuł http://www.devx.com/asp/Article/29256 na temat tego, jak to zrobić. To praca, ale nie ciężka praca. Jeśli wydaje się to zbyt dużą ilością pracy do wykonania, cóż, tak jak powiedziałem, inne rozwiązania mogą wydawać się tylko "łatwiejsze", ponieważ są w zasadzie zepsute, z ogromnymi lukami w zabezpieczeniach, funkcjach i modułowej budowie. –

+1

Wolałbym używać sposobu, który nie jest łamany osobiście, zwłaszcza, że ​​ta aplikacja będzie również dostępna zewnętrznie. Muszę iść i zobaczyć, co się stanie. – LiamGu

Powiązane problemy