asp.net - Wie kann ich "Benutzer" in mein DDD-Modell mit authentifizierenden Benutzern integrieren?



asp.net-mvc authentication (2)

Ich erstelle meine erste ASP.NET MVC-Site und habe versucht, der domänenbasierten Entwicklung zu folgen. Meine Website ist eine Website für die Projektzusammenarbeit, bei der Benutzer einem oder mehreren Projekten auf der Website zugewiesen werden können. Aufgaben werden dann zu Projekten hinzugefügt und Benutzer mit einem Projekt können Aufgaben zugewiesen werden. Ein "Benutzer" ist also ein grundlegendes Konzept meines Domänenmodells.

Mein Plan ist, ein "Benutzer" -Modellobjekt zu haben, das alle Informationen über einen Benutzer enthält und über ein IUserRepository zugänglich ist. Jeder Benutzer kann durch eine UserId identifiziert werden. Obwohl ich mir zu diesem Zeitpunkt nicht sicher bin, ob die UserId eine Zeichenfolge oder Ganzzahl sein soll.

Wie sollten meine Domänenobjekte User und IUserRepository mit den administrativen Funktionen meiner Site in Beziehung stehen, wie das Autorisieren von Benutzern und das Anmelden von Benutzern? Wie würde ich mein Domänenmodell mit anderen Aspekten von ASP.NET integrieren, z. B. HttpContext.User, HttpContext.Profile, einem benutzerdefinierten MemberShipProvider, einem benutzerdefinierten ProfileProvider oder einem benutzerdefinierten AuthorizeAttribute?

Soll ich einen benutzerdefinierten MembershipProvider und / oder ProfilProvider erstellen, der mein IUserRepository umschließt? Ich kann jedoch auch voraussehen, warum ich die Benutzerinformationen in meinem Domänenmodell von der Autorisierung eines Benutzers auf meiner Website trennen möchte. Zum Beispiel möchte ich in Zukunft vielleicht von der Formularauthentifizierung zur Windows-Authentifizierung wechseln.

Wäre es besser, das Rad nicht neu zu erfinden und mit dem in ASP.NET integrierten SqlMembershipProvider zu bleiben? Die Profilinformationen jedes Benutzers würden im Domänenmodell (User / IUserRepository) gespeichert, aber das Passwort würde nicht enthalten sein. Ich würde dann die standardmäßigen ASP.NET-Mitgliedschaften verwenden, um mit der Erstellung und Autorisierung von Benutzern umzugehen? Daher müsste irgendwo Code vorhanden sein, der ein Profil für neue Benutzer im IUserRepository erstellen kann, wenn ihr Konto erstellt wird oder wenn sie sich das erste Mal anmelden.

https://src-bin.com


Answer #1

Ich weiß, meine Antwort kommt ein bisschen spät, aber für zukünftige Hinweise auf andere Kollegen mit der gleichen Frage.

Im Folgenden finden Sie ein Beispiel für die benutzerdefinierte Authentifizierung und Autorisierung mithilfe von Rollen. http://www.codeproject.com/Articles/408306/Understanding-and-Implementing-ASP-NET-Custom-Form . Es ist ein sehr guter Artikel, sehr frisch und aktuell.

Meiner Meinung nach sollten Sie diese Implementierung als Teil der Infrastruktur haben (Erstellen Sie einfach ein neues Projekt Sicherheit oder wie immer Sie es nennen wollen) und implementieren Sie dieses Beispiel oben. Rufen Sie diesen Mechanismus dann von Ihrer Anwendungsschicht aus auf. Denken Sie daran, dass die Anwendungsschicht den gesamten Vorgang in Ihrer Anwendung steuert und orchestriert. Domain-Ebene sollte ausschließlich über Geschäftsvorgänge, nicht über den Zugriff oder Datenpersistenz, usw. betreffen. Es ist unwissend, wie Sie Menschen in Ihrem System authentifizieren.

Denken Sie an eine Ziegelei-Firma. Das implementierte Fingerabdruck-Zugangssystem hat nichts mit dem Betrieb dieses Unternehmens zu tun, ist jedoch Teil der Infrastruktur (Gebäude). In der Tat kontrolliert es, wer Zugang zum Unternehmen hat, damit sie ihre jeweiligen Aufgaben erfüllen können. Sie haben nicht zwei Angestellte, einen, um seinen Fingerabdruck zu scannen, damit der andere hineingehen und seine Arbeit machen kann. Sie haben nur einen Mitarbeiter mit einem Zeigefinger. Für "Zugriff" braucht man nur seinen Finger ... Also sollte Ihr Repository, wenn Sie dasselbe UserRepository für die Authentifizierung verwenden, eine Authentifizierungsmethode enthalten. Wenn Sie sich stattdessen für einen AccessService entschieden haben (dies ist ein Anwendungsdienst, kein Domänen-Service), müssen Sie UserRepository einbeziehen, damit Sie auf diese Benutzerdaten zugreifen, seine Fingerinformationen (Benutzername und Passwort) abrufen und diese vergleichen die Form (Fingerscan). Habe ich mir das richtig erklärt?

Die meisten DDD-Situationen treffen auf reale Situationen zu ... wenn es um die Architektur der Software geht.


Answer #2

Ja - sehr gute Frage. Wie bei Andrew Cooper hat auch unser Team all das durchgemacht.

Wir gingen mit den folgenden Ansätzen (richtig oder falsch):

Benutzerdefinierter Mitgliedschaftsanbieter

Weder ich noch der andere Entwickler sind Fans des integrierten ASP.NET-Mitgliedschaftsanbieters. Es ist viel zu aufgeblasen für das, worum es auf unserer Website geht (einfache, von der UGC betriebene soziale Website). Wir haben ein sehr einfaches erstellt, das genau das tut, was unsere Anwendung benötigt, und nicht mehr. Während der integrierte Mitgliedschaftsanbieter alles tut, was Sie benötigen, aber höchstwahrscheinlich nicht.

Authentifizierungsticket / Authentifizierung für benutzerdefinierte Formulare

Alles in unserer Anwendung verwendet eine Schnittstellen-gesteuerte Abhängigkeitsinjektion (StructureMap). Dies beinhaltet die Formularauthentifizierung. Wir haben eine sehr dünne Schnittstelle geschaffen:

public interface IAuthenticationService
{
   void SignIn(User user, HttpResponseBase httpResponseBase);
   void SignOut();
}

Diese einfache Schnittstelle ermöglicht einfaches Mocking / Testen. Mit der Implementierung erstellen wir ein benutzerdefiniertes Formular-Authentifizierungs-Ticket, das Folgendes enthält: Dinge wie die Benutzer-ID und die Rollen, die bei jeder HTTP-Anforderung erforderlich sind, ändern sich nicht häufig und sollten daher nicht bei jeder Anforderung abgerufen werden.

Wir verwenden dann einen Aktionsfilter, um das Formularauthentifizierungsticket (einschließlich der Rollen) zu entschlüsseln und es in die HttpContext.Current.User.Identity (für die unser Principal-Objekt ebenfalls interface-basiert ist).

Verwendung von [Authorize] und [AdminOnly]

Wir können weiterhin die Autorisierungsattribute in MVC verwenden. Und wir haben für jede Rolle auch einen erstellt. [AdminOnly] überprüft einfach die Rolle für den aktuellen Benutzer und gibt 401 (verboten) aus.

Einfache, einzelne Tabelle für Benutzer, einfache POCO

Alle Benutzerinformationen werden in einer einzigen Tabelle gespeichert (mit Ausnahme von "optionalen" Benutzerinformationen wie Profilinteressen). Dies wird einem einfachen POCO (Entity Framework) zugeordnet, in dem auch Domänenlogik in das Objekt eingebaut ist.

Benutzer-Repository / Service

Einfaches Benutzer-Repository, das domänenspezifisch ist . Dinge wie das Ändern des Passworts, das Aktualisieren des Profils, das Abrufen von Benutzern usw. Das Repository ruft die Domänenlogik für das oben erwähnte Benutzerobjekt auf. Der Dienst ist ein dünner Wrapper über dem Repository, der einzelne Repository-Methoden (zB Suchen) in spezialisiertere (FindById, FindByNickname) trennt.

Domäne getrennt von Sicherheit

Unsere "Domäne" der Benutzer und seine / ihre Assoziationsinformationen. Dazu gehören Name, Profil, Facebook / soziale Integration usw.

Dinge wie "Login", "Logout" beschäftigen sich mit Authentifizierung und Dinge wie "User.IsInRole" befassen sich mit Autorisierung und gehören daher nicht in die Domäne.

Unsere Controller arbeiten also sowohl mit dem IAuthenticationService als auch mit dem IUserService .

Das Erstellen eines Profils ist ein perfektes Beispiel für Domänenlogik, die auch mit Authentifizierungslogik gemischt ist.

So sieht unser Aussehen aus:

[HttpPost]
[ActionName("Signup")]
public ActionResult Signup(SignupViewModel model)
{
    if (ModelState.IsValid)
    {
        try
        {
            // Map to Domain Model.
            var user = Mapper.Map<SignupViewModel, Core.Entities.Users.User>(model);

            // Create salt and hash password.           
            user.Password = _authenticationService.SaltAndHashPassword();

            // Signup User.
            _userService.Save(user);

            // Save Changes.
            _unitOfWork.Commit();

            // Forms Authenticate this user.
            _authenticationService.SignIn(user, Response);

            // Redirect to homepage.
            return RedirectToAction("Index", "Home", new { area = "" });
        }
        catch (Exception exception)
        {
            ModelState.AddModelError("SignupError", "Sorry, an error occured during Signup. Please try again later.");
            _loggingService.Error(exception);
        }
    }

    return View(model);
}

Zusammenfassung

Das oben genannte hat für uns gut funktioniert. Ich liebe es , eine einfache Benutzertabelle zu haben, und nicht diesen aufgeblähten Wahnsinn, der der ASP.NET-Mitgliedschaftsanbieter ist. Es ist einfach und stellt unsere Domäne dar , nicht die Darstellung von ASP.NET.

Wie gesagt, wir haben eine einfache Website. Wenn Sie auf einer Banking-Website arbeiten, würde ich vorsichtig sein, das Rad neu zu erfinden.

Mein Ratschlag ist, zuerst Ihre Domain / Ihr Modell zu erstellen, bevor Sie überhaupt über die Authentifizierung nachdenken. (DDD ist natürlich das, worum es geht).

Ermitteln Sie dann Ihre Sicherheitsanforderungen und wählen Sie einen Authentifizierungsanbieter (von der Stange oder benutzerdefiniert) entsprechend aus.

Lassen Sie ASP.NET nicht diktieren, wie Ihre Domain gestaltet sein soll. Das ist die Falle, in die die meisten Leute geraten (einschließlich mir, bei einem früheren Projekt).

Viel Glück!





domain-driven-design