Artykuł

freeimages.com freeimages.com
cze 26 2014
0

Filtry autoryzacji w ASP.NET MVC

W poprzednim odcinku opowiedziałem Wam o nowości wprowadzonej w ASP.NET MVC 5, czyli o filtrach uwierzytelniania. Nowy rodzaj filtru ma istotny wpływ na omawiane dzisiaj filtry autoryzacji, dlatego też w poprzednim odcinku opisałem istotne różnice pomiędzy tymi typami.

Nie da się ukryć że nowy rodzaj filtru odciążył omawiane dziś filtry autoryzacji, jednak te wciąż mają się dobrze i wciąż wiodą prym w procesie dopuszczania użytkownika do cennych zasobów. Dalej możliwe jest tworzenie aplikacji webowych w stary sposób ponieważ jakakolwiek autoryzacja nie jest możliwa jeśli użytkownik się wcześniej nie uwierzytelni, a sprawdzenie tego typu dotychczas odbywało się niejako automatycznie już w samym procesie autoryzacji.

Pochylmy się więc mocniej nad zagadnieniem filtrów autoryzacji, bez których trudno wyobrazić sobie jakąkolwiek nowoczesną aplikację webową.

Klasa AuthorizeAttribute

Tak jak już zaznaczyłem w poprzednich tekstach, w przypadku autoryzacji możemy skorzystać z gotowej klasy AuthorizeAttribute. W większości przypadków będzie ona wystarczająca, dlatego też bezpieczniej jest wykorzystać coś sprowadzonego, niż tworzyć w obszarze bezpieczeństwa nowe rozwiązanie od zera. Klasa ta oczywiście rozszerza poznaną wcześniej konstrukcję FilterAttribute oraz implementuje interfejs IAuthorizationFilter, który sam w sobie posiada tylko jedną metodę OnAuthorization.

Klasa AuthorizeAttribute domyślnie pozwala na:

  • Ograniczenie dostępu do zasobu wszystkim niezalogowanym użytkownikom
  • Ograniczenie dostępu do zasobu w taki sposób by mogli z niego korzystać tylko wskazani użytkownicy
  • Ograniczenie dostępu do zasobu w taki sposób by mogli z niego korzystać tylko użytkownicy o wybranych rolach

Na poniższym listingu zobaczycie prezentację wszystkich trzech opcji:

public class HomeController : Controller
{
    // Wszyscy uwierzytelnieni użytkownicy
    [Authorize]
    public ActionResult Index()
    {
        return View();
    }

    // Uwierzytelnieni użytkownicy należący do grupy (roli) users
    [Authorize(Roles="users")]
    public ActionResult List()
    {
        return View();
    }

    // Uwierzytelniony użytkownik o nazwie admin
    [Authorize(Users="admin")]
    public ActionResult Manage()
    {
        return View();
    }
}

Wyraźnie widać, że w najprostszej postaci filtr autoryzacji robi prawie to samo co nasz filtr uwierzytelnienia napisany w poprzednim odcinku. Tak naprawdę dopiero druga i trzecia jego wersja, stawiają przed użytkownikiem konkretne wymaganie - musisz należeć do określonej grupy, bądź też musisz mieć określoną nazwę.

W powyższym przykładzie filtr Authorize zastosowałem w odniesieniu do poszczególnych metod, ale nic nie stoi na przeszkodzie, być przypisać go nawet do samych kontrolerów (przy odpowiedniej konfiguracji można nadpisywać filtr umieszczony wyżej filtrem bardziej szczegółowym ustawionym przy metodzie).

Teoretycznie można go również zastosować globalnie, ale jest to niezalecana opcja, w końcu pewne strony - takie jak choćby strony logowania, powinny mieć nieograniczony dostęp.

Własny filtr autoryzacji

Domyślna implementacja filtru autoryzacji daje sporo możliwości, jednak w niektórych przypadkach będzie ona niewystarczająca. Co zrobić w sytuacji gdy chcemy by każdy użytkownik miał uprawnienia do edycji tylko własnych elementów (np. zadania TODO)? Najprościej byłoby wewnątrz określonej metody kontrolera, sprawdzić czy użytkownik jest właścicielem określonego elementu. Szkopuł jednak w tym, że tego rodzaju problem powtórzy się z całą pewnością również w innych metodach takowego kontrolera. I tutaj z pomocą przychodzą właśnie filtry autoryzacji.

Tak jak pisałem wcześniej, na szczęście nie trzeba tego rodzaju rozwiązań tworzyć od zera. Wystarczy skorzystać z klasy AuthorizeAttribute i przesłonić jej metodę AuthorizeCore, która jest najlepszym miejscem na własne działania weryfikacyjne (przykład zaczerpnięty z wątku stackoverflow):

public class AuthorizeAuthorAttribute : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var isAuthorized = base.AuthorizeCore(httpContext);
        if (!isAuthorized)
        {
			// brak autoryzacji - bazowe sprawdzenie
            return false;
        }

        // nazwa zalogowanego użytkownika
        var username = httpContext.User.Identity.Name;

        // id zadania
        var id = httpContext.Request.RequestContext.RouteData.Values["id"] as string;
        return IsUserOwnerOfArticle(username, id);
    }

    private bool IsUserOwnerOfArticle(string username, string todoId)
    {
		// sprawdzenie w bazie
        throw new NotImplementedException();
    }
}

W tym przypadku zadziałała prosta logika. Najpierw sprawdzamy czy użytkownik przeszedł standardową autoryzację. Jeśli tak, to kontynuujemy i weryfikujemy jego dane. Z kontekstu pobieramy identyfikator zadania (został przekazany w wywołaniu akcji za pomocą parametru id), a następnie za pomocą autorskiej metody weryfikujemy (np. w bazie danych) czy użytkownik posiada uprawnienia do tego zadania. Wyjściowo metoda zawsze musi zwracać wartość bool, która informuje o sukcesie (bądź też jego braku) autoryzacji.

Jak widać, stworzenie własnego filtru autoryzacji naprawdę nie jest trudne, a z pewnością może być bardzo pomocne w określonych sytuacjach.

Materiały

Cykl

Data ostatniej modyfikacji: 31.08.2014, 19:06.

Podoba Ci się ten wpis? Powiedz o tym innym!

Send to Kindle

Komentarze

blog comments powered by Disqus