Sichern von Anwendungen mithilfe von Authentifizierung und Autorisierung

von Microsoft

PDF herunterladen

Dies ist Schritt 9 eines kostenlosen "NerdDinner"-Anwendungstutorial , in dem Sie schrittweise eine kleine, aber vollständige Webanwendung mit ASP.NET MVC 1 erstellen.

Schritt 9 zeigt, wie Sie Authentifizierung und Autorisierung hinzufügen, um unsere NerdDinner-Anwendung zu schützen, sodass Benutzer sich registrieren und sich bei der Website anmelden müssen, um neue Abendessen zu erstellen, und nur der Benutzer, der ein Abendessen hostet, kann es später bearbeiten.

Wenn Sie ASP.NET MVC 3 verwenden, empfehlen wir Ihnen, die Tutorials Erste Schritte Mit MVC 3 oder MVC Music Store zu befolgen.

NerdDinner Schritt 9: Authentifizierung und Autorisierung

Im Moment gewährt unsere NerdDinner-Anwendung jedem, der die Website besucht, die Möglichkeit, die Details eines beliebigen Abendessens zu erstellen und zu bearbeiten. Ändern Wir dies so, dass Benutzer sich registrieren und sich bei der Website anmelden müssen, um neue Abendessen zu erstellen, und eine Einschränkung hinzufügen, sodass nur der Benutzer, der ein Abendessen hostet, später bearbeiten kann.

Um dies zu aktivieren, verwenden wir Authentifizierung und Autorisierung, um unsere Anwendung zu schützen.

Grundlegendes zur Authentifizierung und Autorisierung

Bei der Authentifizierung wird die Identität eines Clients identifiziert und überprüft, der auf eine Anwendung zugreift. Vereinfacht gesagt geht es darum, zu identifizieren, wer der Endbenutzer ist, wenn er eine Website besucht. ASP.NET unterstützt mehrere Möglichkeiten zum Authentifizieren von Browserbenutzern. Für Internetwebanwendungen wird der am häufigsten verwendete Authentifizierungsansatz als "Formularauthentifizierung" bezeichnet. Mit der Formularauthentifizierung kann ein Entwickler ein HTML-Anmeldeformular in seiner Anwendung erstellen und dann den Benutzernamen/das Kennwort überprüfen, den ein Endbenutzer an eine Datenbank oder einen anderen Kennwortanmeldeinformationsspeicher übermittelt. Wenn die Kombination aus Benutzername und Kennwort korrekt ist, kann der Entwickler ASP.NET bitten, ein verschlüsseltes HTTP-Cookie auszugeben, um den Benutzer in zukünftigen Anforderungen zu identifizieren. Wir verwenden die Formularauthentifizierung mit unserer NerdDinner-Anwendung.

Bei der Autorisierung wird ermittelt, ob ein authentifizierter Benutzer über die Berechtigung zum Zugriff auf eine bestimmte URL/Ressource oder zum Ausführen einer Aktion verfügt. Beispielsweise möchten wir in unserer NerdDinner-Anwendung autorisieren, dass nur angemeldete Benutzer auf die /Dinners/Create-URL zugreifen und neue Dinners erstellen können. Wir möchten auch Autorisierungslogik hinzufügen, damit nur der Benutzer, der ein Abendessen hostet, diese bearbeiten kann – und allen anderen Benutzern den Bearbeitungszugriff verweigern kann.

Formularauthentifizierung und AccountController

Die Visual Studio-Standardprojektvorlage für ASP.NET MVC aktiviert automatisch die Formularauthentifizierung, wenn neue ASP.NET MVC-Anwendungen erstellt werden. Außerdem wird dem Projekt automatisch eine vordefinierte Implementierung einer Kontoanmeldungsseite hinzugefügt, wodurch die Integration von Sicherheit in eine Website sehr einfach ist.

Die Standardwebsite. master master Seite wird oben rechts auf der Website ein Link "Anmelden" angezeigt, wenn der Benutzer, der darauf zugreift, nicht authentifiziert ist:

Screenshot der Seite

Durch Klicken auf den Link "Anmelden" gelangen Benutzer zur URL /Account/LogOn :

Screenshot der Anmeldeseite

Besucher, die sich nicht registriert haben, können dies tun, indem Sie auf den Link "Registrieren" klicken, der sie zur /Account/Register-URL führt und ihnen die Eingabe von Kontodetails ermöglicht:

Screenshot der Seite

Durch Klicken auf die Schaltfläche "Registrieren" wird ein neuer Benutzer im ASP.NET Mitgliedschaftssystem erstellt und der Benutzer mithilfe der Formularauthentifizierung auf der Website authentifiziert.

Wenn ein Benutzer angemeldet ist, die Website. master ändert oben rechts auf der Seite die Meldung "Willkommen [Benutzername]!" und rendert einen Link "Abmelden" anstelle eines "Anmelden"-Links. Wenn Sie auf den Link "Abmelden" klicken, wird der Benutzer abgemeldet:

Screenshot der Formularseite

Die oben genannten Anmelde-, Abmelde- und Registrierungsfunktionen werden in der AccountController-Klasse implementiert, die von Visual Studio zum Projekt hinzugefügt wurde, als das Projekt erstellt wurde. Die Benutzeroberfläche für den AccountController wird mithilfe von Ansichtsvorlagen im Verzeichnis \Views\Account implementiert:

Screenshot der Navigationsstruktur

Die AccountController-Klasse verwendet das ASP.NET Formularauthentifizierungssystem, um verschlüsselte Authentifizierungscookies auszugeben, und die ASP.NET-Mitgliedschafts-API zum Speichern und Überprüfen von Benutzernamen/Kennwörtern. Die ASP.NET-Mitgliedschafts-API ist erweiterbar und ermöglicht die Verwendung aller Kennwortanmeldeinformationen. ASP.NET wird mit integrierten Mitgliedschaftsanbieterimplementierungen ausgeliefert, die Benutzernamen/Kennwörter in einer SQL-Datenbank oder in Active Directory speichern.

Wir können konfigurieren, welchen Mitgliedschaftsanbieter unsere NerdDinner-Anwendung verwenden soll, indem wir die Datei "web.config" im Stammverzeichnis des Projekts öffnen und nach dem <darin enthaltenen Mitgliedschaftsabschnitt> suchen. Die Standard-web.config hinzugefügt, wenn das Projekt erstellt wurde, registriert den SQL-Mitgliedschaftsanbieter und konfiguriert ihn für die Verwendung einer Verbindungszeichenfolge namens "ApplicationServices", um den Datenbankspeicherort anzugeben.

Die Standardverbindungszeichenfolge "ApplicationServices" (die im <Abschnitt connectionStrings> der web.config datei angegeben wird) ist für die Verwendung von SQL Express konfiguriert. Er verweist auf eine SQL Express-Datenbank mit dem Namen "ASPNETDB. MDF" im Verzeichnis "App_Data" der Anwendung. Wenn diese Datenbank bei der ersten Verwendung der Mitgliedschafts-API in der Anwendung nicht vorhanden ist, erstellt ASP.NET automatisch die Datenbank und stellt das entsprechende Mitgliedschaftsdatenbankschema darin bereit:

Screenshot der Navigationsstruktur

Wenn wir anstelle von SQL Express eine vollständige SQL Server instance (oder eine Verbindung mit einer Remotedatenbank) verwenden möchten, müssen wir lediglich die Verbindungszeichenfolge "ApplicationServices" in der web.config-Datei aktualisieren und sicherstellen, dass das entsprechende Mitgliedschaftsschema der Datenbank hinzugefügt wurde, auf die sie verweist. Sie können das Hilfsprogramm "aspnet_regsql.exe" im Verzeichnis \Windows\Microsoft.NET\Framework\v2.0.50727\ ausführen, um einer Datenbank das entsprechende Schema für die Mitgliedschaft und die anderen ASP.NET-Anwendungsdienste hinzuzufügen.

Autorisieren von /Dinners/Create URL mithilfe des [Authorize]-Filters

Wir mussten keinen Code schreiben, um eine sichere Authentifizierungs- und Kontoverwaltungsimplementierung für die NerdDinner-Anwendung zu ermöglichen. Benutzer können neue Konten bei unserer Anwendung registrieren und sich bei der Website anmelden/abmelden.

Jetzt können wir der Anwendung Autorisierungslogik hinzufügen und die Authentifizierungs-status und den Benutzernamen von Besuchern verwenden, um zu steuern, was sie innerhalb der Website tun können und was nicht. Beginnen wir mit dem Hinzufügen von Autorisierungslogik zu den Aktionsmethoden "Create" der DinnersController-Klasse. Insbesondere müssen Benutzer, die auf die /Dinners/Create-URL zugreifen, angemeldet sein. Wenn sie nicht angemeldet sind, leiten wir sie zur Anmeldeseite um, damit sie sich anmelden können.

Die Implementierung dieser Logik ist ziemlich einfach. Wir müssen lediglich unseren Create-Aktionsmethoden wie folgt ein [Authorize]-Filterattribute hinzufügen:

//
// GET: /Dinners/Create

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

//
// POST: /Dinners/Create

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

ASP.NET MVC unterstützt die Möglichkeit, "Aktionsfilter" zu erstellen, die verwendet werden können, um wiederverwendbare Logik zu implementieren, die deklarativ auf Aktionsmethoden angewendet werden kann. Der Filter [Autorisieren] ist einer der integrierten Aktionsfilter, der von ASP.NET MVC bereitgestellt wird, und ermöglicht es entwicklern, Autorisierungsregeln deklarativ auf Aktionsmethoden und Controllerklassen anzuwenden.

Wenn er ohne Parameter (wie oben) angewendet wird, erzwingt der Filter [Autorisieren], dass der Benutzer, der die Aktionsmethodenanforderung stellt, angemeldet werden muss. Andernfalls wird der Browser automatisch zur Anmelde-URL umgeleitet. Bei dieser Umleitung wird die ursprünglich angeforderte URL als Querystring-Argument übergeben (z. B. /Account/LogOn? ReturnUrl=%2fDinners%2fErstellen). Der AccountController leitet den Benutzer nach der Anmeldung zurück zur ursprünglich angeforderten URL.

Der Filter [Autorisieren] unterstützt optional die Möglichkeit, eine Eigenschaft "Benutzer" oder "Rollen" anzugeben, die verwendet werden kann, um zu verlangen, dass der Benutzer sowohl angemeldet als auch in einer Liste zulässiger Benutzer oder Mitglied einer zulässigen Sicherheitsrolle ist. Der folgende Code ermöglicht beispielsweise nur zwei bestimmten Benutzern, "scottgu" und "billg", den Zugriff auf die URL "/Dinners/Create":

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

Das Einbetten bestimmter Benutzernamen in Code ist jedoch in der Regel nicht verwaltbar. Ein besserer Ansatz besteht darin, "Rollen" auf höherer Ebene zu definieren, mit denen der Code überprüft, und dann Benutzer mithilfe einer Datenbank oder eines Active Directory-Systems der Rolle zuzuordnen (so dass die tatsächliche Benutzerzuordnungsliste extern aus dem Code gespeichert werden kann). ASP.NET enthält eine integrierte Rollenverwaltungs-API sowie eine integrierte Gruppe von Rollenanbietern (einschließlich solcher für SQL und Active Directory), die bei der Durchführung dieser Benutzer-/Rollenzuordnung helfen können. Anschließend könnten wir den Code so aktualisieren, dass nur Benutzer mit einer bestimmten Administratorrolle auf die URL /Dinners/Create zugreifen können:

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

Verwenden der User.Identity.Name-Eigenschaft beim Erstellen von Dinners

Wir können den Benutzernamen des aktuell angemeldeten Benutzers einer Anforderung mithilfe der User.Identity.Name-Eigenschaft abrufen, die für die Controller-Basisklasse verfügbar gemacht wird.

Früher als wir die HTTP-POST-Version unserer Create()-Aktionsmethode implementiert haben, hatten wir die Eigenschaft "HostedBy" des Dinner in eine statische Zeichenfolge hartcodiert. Wir können diesen Code jetzt aktualisieren, um stattdessen die eigenschaft User.Identity.Name zu verwenden und automatisch einen RSVP für den Host hinzuzufügen, der das Dinner erstellt:

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

Da wir der Create()-Methode ein [Authorize]-Attribut hinzugefügt haben, stellt ASP.NET MVC sicher, dass die Aktionsmethode nur ausgeführt wird, wenn der Benutzer, der die URL /Dinners/Create aufruft, auf der Website angemeldet ist. Daher enthält der wert der User.Identity.Name-Eigenschaft immer einen gültigen Benutzernamen.

Verwenden der User.Identity.Name-Eigenschaft beim Bearbeiten von Dinners

Fügen wir nun eine Autorisierungslogik hinzu, die Benutzer so einschränkt, dass sie nur die Eigenschaften von Abendessen bearbeiten können, die sie selbst hosten.

Um dies zu unterstützen, fügen wir zunächst eine Hilfsmethode "IsHostedBy(username)" zu unserem Dinner-Objekt hinzu (innerhalb der zuvor erstellten Teilklasse Dinner.cs). Diese Hilfsmethode gibt true oder false zurück, je nachdem, ob ein angegebener Benutzername mit der Dinner HostedBy-Eigenschaft übereinstimmt, und kapselt die Logik, die zum Durchführen eines Zeichenfolgenvergleichs ohne Beachtung der Groß-/Kleinschreibung erforderlich ist:

public partial class Dinner {

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

Anschließend fügen wir ein [Authorize]-Attribut zu den Edit()-Aktionsmethoden in unserer DinnersController-Klasse hinzu. Dadurch wird sichergestellt, dass Benutzer angemeldet sein müssen, um eine /Dinners/Edit/[id]- URL anzufordern.

Wir können dann Code zu unseren Edit-Methoden hinzufügen, die die Dinner.IsHostedBy(username)-Hilfsmethode verwenden, um zu überprüfen, ob der angemeldete Benutzer mit dem Dinner-Host übereinstimmt. Wenn der Benutzer nicht der Host ist, wird die Ansicht "InvalidOwner" angezeigt und die Anforderung beendet. Der Hierfür zu verwendende Code sieht wie folgt aus:

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

Wir können dann mit der rechten Maustaste auf das Verzeichnis \Views\Dinners klicken und den Menübefehl Add-View> auswählen, um eine neue "InvalidOwner"-Ansicht zu erstellen. Sie wird mit der folgenden Fehlermeldung aufgefüllt:

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

Wenn ein Benutzer nun versucht, ein Abendessen zu bearbeiten, das er nicht besitzt, erhält er eine Fehlermeldung:

Screenshot der Fehlermeldung auf der Nerd Dinner-Webseite.

Wir können die gleichen Schritte für die Delete()-Aktionsmethoden innerhalb unseres Controllers wiederholen, um auch die Berechtigung zum Löschen von Dinners zu sperren und sicherzustellen, dass nur der Host eines Dinners sie löschen kann.

Wir verknüpfen mit der Aktionsmethode Bearbeiten und Löschen der DinnersController-Klasse über die Details-URL:

Screenshot der Seite

Derzeit zeigen wir die Aktionslinks Bearbeiten und Löschen an, unabhängig davon, ob der Besucher der Detail-URL der Gastgeber des Abendessens ist. Ändern wir dies so, dass die Links nur angezeigt werden, wenn der besuchender Benutzer der Besitzer des Abendessens ist.

Die Detail()-Aktionsmethode in unserem DinnersController ruft ein Dinner-Objekt ab und übergibt es dann als Modellobjekt an unsere Ansichtsvorlage:

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

public ActionResult Details(int id) {

    Dinner dinner = dinnerRepository.GetDinner(id);

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

    return View(dinner);
}

Wir können unsere Ansichtsvorlage so aktualisieren, dass die Links Bearbeiten und Löschen mit der Dinner.IsHostedBy()-Hilfsmethode wie folgt bedingt ein-/ausblenden:

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

<% } %>

Nächste Schritte

Sehen wir uns nun an, wie wir authentifizierte Benutzer für RSVP für Abendessen mithilfe von AJAX aktivieren können.