Mehr von Jürgen Gutsch

Mehr von Jürgen Gutsch

Empfehlungen von Jürgen Gutsch

Blog-Empfehlungen von Jürgen Gutsch

Willkommen bei ASP.NET Zone. Anmelden | Registrieren | Hilfe

Jürgen Gutsch

ASP.NET und mehr...
Einfache Authentifizierung mit ASP.NET

Ich möchte heute mal eine einfache Authentifizierung mit ASP.NET vorstellen, welche ohne die herkömmlichen ASP.NET Login Controls und deren Provider auskommt. Der Vorteil ist, dass es für kleine Anwendungen wesentlich schneller und einfacher umzusetzen ist. Möchte man dagegen noch Dinge nutzen wie Rollen und Profile sind eher die herkömmlichen Controls und Membership-, Role- und ProfileProvider schneller zu implementieren. Der größte Nachteil bei der herkömmlichen Methode ist der, dass es recht aufwendig wird, sobald die Benutzer und Rollen aus einer Benutzerdefinierten und/oder bereits vorhandenen Datenbank kommen sollen, denn dann müssen in der Regel eigene Provider geschrieben werden.

Was wird benötigt?

1) Eine Datenquelle welche die Benutzerinformationen enthält
2) Eine öffentliche Loginseite oder eine öffentliche Seite mit einer Loginmöglichkeit
3) Mehrere geschützte Seiten

Die Datenquelle kann jede beliebige sein (XML, CSV, Access, SQL Server, etc...). In den Code-Beispielen gehe ich nicht auf diese ein, sondern verwende eine fiktive Klasse mit dem Namen "UserLib", welche mir dem Zugriff auf die DatenQuelle abnimmt.

Das Loginformular enthält ein Feld für die Benutzernamen, ein Feld für das Passwort, eine Checkbox um anzugeben, ob ein Cookie gesetzt werden soll und natürlich einen Button:

<asp:Panel ID="pnlLogin" runat="server"
        GroupingText="Login" Width="300" BackColor="#FFFFFF">
    <asp:Label AssociatedControlID="txtUsername" ID="lblUsername"  
        runat="server" Text="Benutzername" />
    <br ID="txtUsername" runat="server" CssClass="text" />
    <asp:Label AssociatedControlID="txtPassword" ID="lblPassword"  
        runat="server" Text="Passwort" />
    <br ID="txtPassword" runat="server" CssClass="text" />
    <br ID="chbRememberLogin" runat="server"  
        Text="Login Merken" />
    <asp:Button ID="btnLogin" runat="server" Text="Anmelden"  
        OnClick="btnLogin_Click" />
    <p>(Benutzername und Passwort: maxm)</p>
</asp:Panel>

Was passiert bei Klick auf den Button?

Wenn beide Felder ausgefüllt sind (zur Prüfung können selbstverständlich die Validator Controls verwendet werden) werden die Daten mit der Datenquelle gegen geprüft. Im Optimalfall ist das Passwort in der Datenquelle verschlüsselt. Das eingegebene Passwort wird als ebenfalls verschlüsselt und dem in der Datenquelle verglichen.

Sind die eingegebenen Daten falsch wird eine erneute Eingabe gefordert. Im anderen Fall wird jetzt erst mal die BenutzerID in einer Sessionvariablen gespeichert. Anschließend wird geprüft, ob die CheckBox für das Cookie angehakt ist. Wenn ja, wird ein Cookie mit der BenutzerID gesetzt. Das Cookie könnte eine Gültigkeit von ca. 14 Tagen haben.

Das war schon alles für den Loginvorgang. Man könnte jetzt auch schon auf eine geschützte Seite weiterleiten, je nach dem wie und wo das Loginformular eingebaut ist.

protected void btnLogin_Click(object sender, EventArgs e)
{
    Guid UserID = UserLib.CheckUser(this.txtUsername.Text,
          this.txtPassword.Text);
    if (UserID == Guid.Empty)
    {
        // Fehlermeldung: Daten stimmen nicht
    }
    else
    {
        Session["UserID"] = UserID;
        if (this.chbRememberLogin.Checked)
        {
            HttpCookie MyCookie = new HttpCookie("UserID");
            MyCookie.Value = UserID.ToString();
            MyCookie.Expires = DateTime.Now.AddDays(14);
            Response.Cookies.Add(MyCookie);
        }
        Response.Redirect("Secure.aspx");
    }
}

Wie wird der User erkannt?

Um mir die Arbeit nicht auf allen Seiten machen zu müsse, schreibe ich mir eine neue Basisklasse für diese Seiten. In meinem Beitrag über die Basisklasse ist die Lösung bereits teilweise umgesetzt. Was fehlte, ist die Prüfung des Cookies, wenn keine Sessionvariable existiert:

// Aktuelle Benutzer ID
private Guid userId = Guid.Empty;
public Guid UserId
{
    get
    {
        if (userId == Guid.Empty)
        {
            object obj = Session["UserID"];
            if (obj != null)
            {
                userId = new Guid(obj.ToString());
            }
            else
            {
                HttpCookie cookie = Request.Cookies["UserID"];
                if (cookie != null)
                {
                    string value = cookie.Value;
                    if (!string.IsNullOrEmpty(value))
                    {
                        userId = new Guid(value);
                        Session["UserID"] = userId;
                    }
                }
            }
        }
        return userId;
    }
}

Theoretisch kann ich mir die Arbeit noch mehr vereinfachen, wenn ich mir die Ermittlung des aktuellen Users ebenfalls in diese Klasse holen (User ist hier eine einfache Klasse welche die Eigenschaften eines Benutzer enthält):

// Aktueller Benutzer
private User currenUser = null;
public User CurrentUser
{
    get
    {
        if (currenUser == null)
            currenUser = UserLib.LoadUser(this.UserId);
        return currenUser;
    }
}

In der Page_Init muss ich nur noch prüfen ob der aktuelle Benutzer nicht existiert und entsprechend z. B. auf eine Loginseite umleiten:

// Muss von öffentlichen Seiten überschrieben werden
protected virtual void Page_Init(object sender, EventArgs e)
{
    if (this.CurrentUser == null)
        Response.Redirect("Default.aspx", true);
}

Wenn der Benutzer als auf eine geschützte Seite trifft, wird erst geprüft, ob eine gültige Session existiert. Wenn nein, wird geprüft, ob ein Cookie existiert. Ist das auch nicht der Fall, kann die zweite Eigenschaft keinen Benutzer ermitteln, gibt Null zurück und es wird zum Login weitergeleitet. Existiert ein Cookie, wird die BenutzerID in die Session gespeichert und der Benutzer ist eingeloggt.

In öffentlichen Seiten muss die Page_Init der Basisklasse überschrieben werden, sonst entsteht eine Endlosschleife. Die Login Seite wird sich immer wieder selbst aufrufen, wenn "CurrentUser" null ist.
Die Loginseite könnte z. B: auf die erste geschützte Seite umleiten, wenn der Benutzer erkannt wurde:

protected override void Page_Init(object sender, EventArgs e)
{
    if (this.CurrentUser != null)
        Response.Redirect("Secure.aspx", true);
}

Anwendung

Von jetzt an kann ich in allen geschützten Seiten, welche von dieser Basisklasse ableiten, auf die Eigenschaften des aktuellen Benutzers zugreifen:

this.lblFullName.Text = String.Format("{0} {1}",
        this.CurrenUser.Name, this.CurrenUser.LastName);

Demoprojekt

Ein kleines Demoprojekt kann hier heruntergeladen werden:
http://www.aspnetzone.de/files/folders/198986/download.aspx
(Es wird das .NET Framework 3.5 verwendet)

Posted: Montag, 7. Juli 2008 21:30 von Jürgen Gutsch

Kommentare

Peter Bucher sagte:

Sally Jürgen

Sieht interessant aus!

Gibt es die Klassen bzw. ein Beispielprojekt zum Download?

Ggf. könnte auch ein eigenes Authentifizierungsmodul eingesetzt werden, um die Geschichte ein wenig von den Seiten zu entkoppeln.

Das ist aber auch wieder mehr Aufwand und ein wenig undurchsichtiger.

# Juli 8, 2008 00:55

Jürgen Gutsch sagte:

Sally Peter,

das ich die Codes mehr oder weniger "freihändig" geschrieben habe, gibt es kein Beispielprojekt. Wenn aber eines gewünscht wird, mach ich das natürlich gerne.

Stimmt, man könnte ein HttpModule nutzen. Theoretisch sollte das nicht sehr viel mehr Aufwand machen. Mal sehen, ob sich dazu was schreiben lässt.

# Juli 8, 2008 08:21

Peter Bucher sagte:

Ein Beispielprojekt wäre sicher wünschenswert :)

# Juli 8, 2008 21:39

Jürgen Gutsch sagte:

Na gut, wenn es unbedingt sein muss... *stöhn*

Ich hab ein kleines Demoprojekt angehängt, bzw. den Link zum Download gepostet. :-)

# Juli 9, 2008 23:14

Peter Bucher sagte:

Danke :)

# Juli 21, 2008 08:12

Peter Bucher sagte:

M&#246;glichkeiten der Authentifizierung mit ASP.NET Mit ASP.NET gibt es verschiedene M&#246;glichkeiten,

# Oktober 17, 2008 14:55
Anonyme Kommentare sind nicht zugelassen