Zabezpieczanie aplikacji przy użyciu uwierzytelniania i autoryzacji

autor: Microsoft

Pobierz plik PDF

Jest to krok 9 bezpłatnego samouczka aplikacji "NerdDinner" , który zawiera instrukcje dotyczące tworzenia małej, ale kompletnej aplikacji internetowej przy użyciu ASP.NET MVC 1.

Krok 9 pokazuje, jak dodać uwierzytelnianie i autoryzację w celu zabezpieczenia naszej aplikacji NerdDinner, aby użytkownicy musieli zarejestrować się i zalogować się do witryny w celu utworzenia nowych kolacji, a tylko użytkownik, który hostuje kolację, może go edytować później.

Jeśli używasz ASP.NET MVC 3, zalecamy skorzystanie z samouczków Wprowadzenie With MVC 3 lub MVC Music Store .

NerdDinner — krok 9: Uwierzytelnianie i autoryzacja

W tej chwili nasza aplikacja NerdDinner udziela każdemu, kto odwiedza witrynę, możliwość tworzenia i edytowania szczegółów każdej kolacji. Zmieńmy to tak, aby użytkownicy musieli zarejestrować się i zalogować się do witryny w celu utworzenia nowych kolacji i dodać ograniczenie, aby tylko użytkownik hostujący kolację mógł edytować go później.

Aby to umożliwić, użyjemy uwierzytelniania i autoryzacji w celu zabezpieczenia naszej aplikacji.

Opis uwierzytelniania i autoryzacji

Uwierzytelnianie to proces identyfikowania i weryfikowania tożsamości klienta, który uzyskuje dostęp do aplikacji. Mówiąc prościej, chodzi o identyfikację "kto" użytkownik końcowy jest po wizycie w witrynie internetowej. ASP.NET obsługuje wiele sposobów uwierzytelniania użytkowników przeglądarki. W przypadku aplikacji internetowych najczęściej używane podejście do uwierzytelniania to "Uwierzytelnianie formularzy". Uwierzytelnianie formularzy umożliwia deweloperowi utworzenie formularza logowania HTML w aplikacji, a następnie zweryfikowanie nazwy użytkownika/hasła przesyłanego przez użytkownika końcowego do bazy danych lub innego magazynu poświadczeń haseł. Jeśli kombinacja nazwy użytkownika/hasła jest poprawna, deweloper może poprosić ASP.NET o wystawienie zaszyfrowanego pliku cookie HTTP w celu zidentyfikowania użytkownika w przyszłych żądaniach. Użyjemy uwierzytelniania formularzy w naszej aplikacji NerdDinner.

Autoryzacja to proces określania, czy uwierzytelniony użytkownik ma uprawnienia dostępu do określonego adresu URL/zasobu, czy do wykonania jakiejś akcji. Na przykład w naszej aplikacji NerdDinner chcemy autoryzować, że tylko użytkownicy zalogowani mogą uzyskać dostęp do adresu URL /Dinners/Create i utworzyć nowe kolacje. Chcemy również dodać logikę autoryzacji, aby tylko użytkownik hostujący kolację mógł go edytować i uniemożliwić edycję wszystkim innym użytkownikom.

Uwierzytelnianie formularzy i kontrolka AccountController

Domyślny szablon projektu programu Visual Studio dla ASP.NET MVC automatycznie włącza uwierzytelnianie formularzy po utworzeniu nowych ASP.NET aplikacji MVC. Automatycznie dodaje również wstępnie utworzoną implementację strony logowania konta do projektu , co sprawia, że naprawdę łatwo integrować zabezpieczenia w witrynie.

Domyślna strona wzorcowa Site.master wyświetla link "Zaloguj się" w prawym górnym rogu witryny, gdy użytkownik, który uzyskuje do niej dostęp, nie jest uwierzytelniony:

Zrzut ekranu przedstawiający stronę Nerd Dinner Host a Dinner (Gospodarz kolacji). Logowanie zostało wyróżnione w prawym górnym rogu.

Kliknięcie linku "Zaloguj się" spowoduje przejście użytkownika do adresu URL /Account/LogOn :

Zrzut ekranu przedstawiający stronę Dziennik kolacji Nerd.

Odwiedzający, którzy nie zarejestrowali się, mogą to zrobić, klikając link "Zarejestruj", co spowoduje przejście do adresu URL /Account/Register i zezwolenie im na wprowadzenie szczegółów konta:

Zrzut ekranu przedstawiający stronę Nerd Dinner Create a New Account (Tworzenie nowego konta).

Kliknięcie przycisku "Zarejestruj" spowoduje utworzenie nowego użytkownika w systemie członkostwa ASP.NET i uwierzytelnienie użytkownika w witrynie przy użyciu uwierzytelniania formularzy.

Gdy użytkownik jest zalogowany, plik Site.master zmienia prawy górny wierzchołek strony, aby wyświetlić komunikat "Witaj [nazwa użytkownika]!" i renderuje link "Wyloguj" zamiast "Zaloguj się". Kliknięcie linku "Wyloguj się" spowoduje wylogowanie użytkownika:

Zrzut ekranu przedstawiający stronę formularza kolacji Nerd Dinner Host. Przyciski Witamy i Wyloguj są wyróżnione w prawym górnym rogu.

Powyższe funkcje logowania, wylogowania i rejestracji są implementowane w klasie AccountController, która została dodana do naszego projektu przez program Visual Studio podczas tworzenia projektu. Interfejs użytkownika dla kontrolki AccountController jest implementowany przy użyciu szablonów widoków w katalogu \Views\Account:

Zrzut ekranu przedstawiający drzewo nawigacji po kolacji w witrynie Nerd. Kontroler konta dot c s jest wyróżniony. Elementy folderu konta i menu są również wyróżnione.

Klasa AccountController używa systemu uwierzytelniania formularzy ASP.NET do wystawiania zaszyfrowanych plików cookie uwierzytelniania oraz interfejsu API członkostwa ASP.NET do przechowywania i weryfikowania nazw użytkowników/haseł. Interfejs API członkostwa ASP.NET jest rozszerzalny i umożliwia korzystanie z dowolnego magazynu poświadczeń haseł. ASP.NET dostarczane z wbudowanymi implementacjami dostawcy członkostwa, które przechowują nazwy użytkownika/hasła w bazie danych SQL lub w usłudze Active Directory.

Możemy skonfigurować dostawcę członkostwa, którego powinna używać nasza aplikacja NerdDinner, otwierając plik "web.config" w katalogu głównym projektu i wyszukując <w nim sekcję członkostwa> . Domyślny web.config dodany podczas tworzenia projektu rejestruje dostawcę członkostwa SQL i konfiguruje go tak, aby używał parametrów połączenia o nazwie "ApplicationServices", aby określić lokalizację bazy danych.

Domyślne parametry połączenia "ApplicationServices" (określone w <sekcji connectionStrings> pliku web.config) są skonfigurowane do używania programu SQL Express. Wskazuje ona bazę danych SQL Express o nazwie "ASPNETDB". MDF" w katalogu "App_Data" aplikacji. Jeśli ta baza danych nie istnieje przy pierwszym użyciu interfejsu API członkostwa w aplikacji, ASP.NET automatycznie utworzy bazę danych i zaaprowizuje odpowiedni schemat bazy danych członkostwa w nim:

Zrzut ekranu przedstawiający drzewo nawigacji po kolacji w witrynie Nerd. Dane aplikacji są rozwinięte, a wybrano pozycję S P NET D D dot D F.

Jeśli zamiast korzystać z programu SQL Express, chcieliśmy użyć pełnego wystąpienia SQL Server (lub nawiązać połączenie z zdalną bazą danych), wystarczy zaktualizować parametry połączenia "ApplicationServices" w pliku web.config i upewnić się, że odpowiedni schemat członkostwa został dodany do bazy danych, na którą wskazuje. Możesz uruchomić narzędzie "aspnet_regsql.exe" w katalogu \Windows\Microsoft.NET\Framework\v2.0.50727\, aby dodać odpowiedni schemat członkostwa i innych usług aplikacji ASP.NET do bazy danych.

Autoryzowanie adresu URL /Dinners/Create przy użyciu filtru [Autoryzuj]

Nie musieliśmy pisać żadnego kodu, aby umożliwić bezpieczną implementację uwierzytelniania i zarządzania kontami dla aplikacji NerdDinner. Użytkownicy mogą rejestrować nowe konta za pomocą naszej aplikacji i logować się do witryny.

Teraz możemy dodać logikę autoryzacji do aplikacji i użyć stanu uwierzytelniania i nazwy użytkownika odwiedzających, aby kontrolować, co mogą i nie mogą robić w witrynie. Zacznijmy od dodania logiki autoryzacji do metod akcji "Utwórz" klasy DinnersController. W szczególności wymagamy, aby użytkownicy, którzy uzyskiwali dostęp do adresu URL /Dinners/Create , muszą być zalogowani. Jeśli nie są one zalogowane, przekierowujemy je na stronę logowania, aby mogli się zalogować.

Implementacja tej logiki jest dość łatwa. Wystarczy dodać atrybut filtru [Autoryzuj] do metod tworzenia akcji w następujący sposób:

//
// GET: /Dinners/Create

[Authorize]
public ActionResult Create() {
   ...
} 

//
// POST: /Dinners/Create

[AcceptVerbs(HttpVerbs.Post), Authorize]
public ActionResult Create(Dinner dinnerToCreate) {
   ...
}

ASP.NET MVC obsługuje możliwość tworzenia "filtrów akcji", które mogą służyć do implementowania logiki wielokrotnego użytku, która może być deklaratywnie stosowana do metod akcji. Filtr [Autoryzuj] jest jednym z wbudowanych filtrów akcji udostępnianych przez ASP.NET MVC i umożliwia deweloperowi deklaratywne stosowanie reguł autoryzacji do metod akcji i klas kontrolera.

Po zastosowaniu bez żadnych parametrów (takich jak powyżej) filtr [Autoryzuj] wymusza, że użytkownik wysyłający żądanie metody akcji musi być zalogowany — i automatycznie przekierowuje przeglądarkę do adresu URL logowania, jeśli nie są. W przypadku tego przekierowania pierwotnie żądany adres URL jest przekazywany jako argument ciągu zapytania (na przykład : /Account/LogOn? ReturnUrl=%2fDinners%2fCreate). Funkcja AccountController przekierowuje użytkownika z powrotem do pierwotnie żądanego adresu URL po zalogowaniu się.

Filtr [Autoryzuj] opcjonalnie obsługuje możliwość określenia właściwości "Użytkownicy" lub "Role", która może służyć do wymagania, aby użytkownik był zalogowany i na liście dozwolonych użytkowników lub członka dozwolonej roli zabezpieczeń. Na przykład poniższy kod umożliwia dostęp do adresu URL /Dinners/Create tylko dwóch konkretnych użytkowników: "scottgu" i "billg":

[Authorize(Users="scottgu,billg")]
public ActionResult Create() {
    ...
}

Osadzanie określonych nazw użytkowników w kodzie jest jednak dość nie do utrzymania. Lepszym rozwiązaniem jest zdefiniowanie ról wyższego poziomu, względem których kod sprawdza, a następnie mapowania użytkowników na rolę przy użyciu bazy danych lub systemu usługi Active Directory (dzięki czemu rzeczywista lista mapowań użytkowników będzie przechowywana zewnętrznie z poziomu kodu). ASP.NET zawiera wbudowany interfejs API zarządzania rolami, a także wbudowany zestaw dostawców ról (w tym dla usług SQL i Active Directory), który może pomóc w wykonaniu tego mapowania użytkowników/ról. Następnie można zaktualizować kod, aby zezwolić tylko użytkownikom w ramach określonej roli "administrator" na dostęp do adresu URL /Dinners/Create:

[Authorize(Roles="admin")]
public ActionResult Create() {
   ...
}

Korzystanie z właściwości User.Identity.Name podczas tworzenia kolacji

Możemy pobrać nazwę użytkownika aktualnie zalogowanego użytkownika żądania przy użyciu właściwości User.Identity.Name uwidocznionej w klasie bazowej Controller.

Wcześniej, gdy zaimplementowaliśmy wersję akcji HTTP-POST metody akcji Create(), zakodowaliśmy na stałe właściwość "HostedBy" kolacji do ciągu statycznego. Teraz możemy zaktualizować ten kod, aby zamiast tego użyć właściwości User.Identity.Name, a także automatycznie dodać rsVP hosta tworzącego kolację:

//
// POST: /Dinners/Create

[AcceptVerbs(HttpVerbs.Post), Authorize]
public ActionResult Create(Dinner dinner) {

    if (ModelState.IsValid) {
    
        try {
            dinner.HostedBy = User.Identity.Name;

            RSVP rsvp = new RSVP();
            rsvp.AttendeeName = User.Identity.Name;
            dinner.RSVPs.Add(rsvp);

            dinnerRepository.Add(dinner);
            dinnerRepository.Save();

            return RedirectToAction("Details", new { id=dinner.DinnerID });
        }
        catch {
            ModelState.AddModelErrors(dinner.GetRuleViolations());
        }
    }

    return View(new DinnerFormViewModel(dinner));
}

Ponieważ dodaliśmy atrybut [Authorize] do metody Create(), ASP.NET MVC gwarantuje, że metoda akcji jest wykonywana tylko wtedy, gdy użytkownik odwiedzający /Dinners/Create URL jest zalogowany w witrynie. W związku z tym wartość właściwości User.Identity.Name zawsze będzie zawierać prawidłową nazwę użytkownika.

Używanie właściwości User.Identity.Name podczas edytowania kolacji

Teraz dodajmy logikę autoryzacji, która ogranicza użytkowników, aby mogli edytować tylko właściwości kolacji, które sami hostują.

Aby pomóc w tym celu, najpierw dodamy metodę pomocniczą "IsHostedBy(username)" do obiektu Dinner (w ramach klasy częściowej Dinner.cs utworzonej wcześniej). Ta metoda pomocnika zwraca wartość true lub false w zależności od tego, czy dostarczona nazwa użytkownika jest zgodna z właściwością Dinner HostedBy, i hermetyzuje logikę niezbędną do wykonania porównania ciągu bez uwzględniania wielkości liter:

public partial class Dinner {

    public bool IsHostedBy(string userName) {
        return HostedBy.Equals(userName, StringComparison.InvariantCultureIgnoreCase);
    }
}

Następnie dodamy atrybut [Authorize] do metod akcji Edit() w naszej klasie DinnersController. Dzięki temu użytkownicy muszą być zalogowani, aby zażądali adresu URL /Dinners/Edit/[id].

Następnie możemy dodać kod do metod Edit, które używają metody pomocnika Dinner.IsHostedBy(nazwa użytkownika), aby sprawdzić, czy zalogowany użytkownik jest zgodny z hostem kolacji. Jeśli użytkownik nie jest hostem, wyświetlimy widok "InvalidOwner" i zakończymy żądanie. Kod, który ma to zrobić, wygląda następująco:

//
// GET: /Dinners/Edit/5

[Authorize]
public ActionResult Edit(int id) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    if (!dinner.IsHostedBy(User.Identity.Name))
        return View("InvalidOwner");

    return View(new DinnerFormViewModel(dinner));
}

//
// POST: /Dinners/Edit/5

[AcceptVerbs(HttpVerbs.Post), Authorize]
public ActionResult Edit(int id, FormCollection collection) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    if (!dinner.IsHostedBy(User.Identity.Name))
        return View("InvalidOwner");

    try {
        UpdateModel(dinner);

        dinnerRepository.Save();

        return RedirectToAction("Details", new {id = dinner.DinnerID});
    }
    catch {
        ModelState.AddModelErrors(dinnerToEdit.GetRuleViolations());

        return View(new DinnerFormViewModel(dinner));
    }
}

Następnie możemy kliknąć prawym przyciskiem myszy katalog \Views\Dinners i wybrać polecenie menu Add-View>, aby utworzyć nowy widok "InvalidOwner". Wypełnimy go poniższym komunikatem o błędzie:

<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
    You Don't Own This Dinner
</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Error Accessing Dinner</h2>

    <p>Sorry - but only the host of a Dinner can edit or delete it.</p>

</asp:Content>

A teraz, gdy użytkownik próbuje edytować kolację, której nie jest właścicielem, otrzyma komunikat o błędzie:

Zrzut ekranu przedstawiający komunikat o błędzie na stronie internetowej Nerd Dinner.

Możemy powtórzyć te same kroki dla metod akcji Delete() w naszym kontrolerze, aby zablokować uprawnienie do usuwania kolacji, a także upewnić się, że tylko host kolacji może go usunąć.

Łączymy się z metodą akcji Edytuj i Usuń klasy DinnersController z adresu URL szczegółów:

Zrzut ekranu przedstawiający stronę kolacji Nerd. Przyciski Edytuj i Usuń są zakreśline u dołu. Szczegóły U R L są zakreśline u góry.

Obecnie wyświetlane są linki akcji Edytuj i Usuń niezależnie od tego, czy odwiedzający adres URL szczegółów jest gospodarzem kolacji. Zmieńmy to tak, aby linki są wyświetlane tylko wtedy, gdy odwiedzający użytkownik jest właścicielem kolacji.

Metoda akcji Details() w ramach metody DinnersController pobiera obiekt Dinner, a następnie przekazuje go jako obiekt modelu do szablonu widoku:

//
// GET: /Dinners/Details/5

public ActionResult Details(int id) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    if (dinner == null)
        return View("NotFound");

    return View(dinner);
}

Możemy zaktualizować szablon widoku, aby warunkowo pokazywać/ukrywać linki Edytuj i Usuń przy użyciu metody pomocnika Dinner.IsHostedBy(), jak pokazano poniżej:

<% if (Model.IsHostedBy(Context.User.Identity.Name)) { %>

   <%= Html.ActionLink("Edit Dinner", "Edit", new { id=Model.DinnerID }) %> |
   <%= Html.ActionLink("Delete Dinner", "Delete", new {id=Model.DinnerID}) %>    

<% } %>

Następne kroki

Przyjrzyjmy się teraz, jak możemy włączyć uwierzytelnionych użytkowników do rsVP na kolacje przy użyciu AJAX.