FormsAuthentication.SignOut () meldet den Benutzer nicht ab


143

Ich habe meinen Kopf etwas zu lange dagegen geschlagen. Wie verhindere ich, dass ein Benutzer die Seiten einer Site durchsucht, nachdem er mit FormsAuthentication.SignOut abgemeldet wurde? Ich würde erwarten, dass dies geschieht:

FormsAuthentication.SignOut();
Session.Abandon();
FormsAuthentication.RedirectToLoginPage();

Aber das tut es nicht. Wenn ich eine URL direkt eingebe, kann ich trotzdem zur Seite navigieren. Ich habe seit einiger Zeit keine eigene Sicherheit mehr verwendet, daher vergesse ich, warum dies nicht funktioniert.


Dieser Code ist in Ordnung, so wie er ist. Wenn Sie im Browser zurückklicken, wird die Seite auf dem Server nicht erneut aufgerufen. Es wird lediglich die lokal zwischengespeicherte Version der Seite neu geladen. Alle folgenden Lösungen scheinen diese Tatsache zu ignorieren und nicht mehr zu tun, als Sie hier tun. Kurz gesagt ... es gibt keine Antwort auf diese Frage, die den Benutzer beim Betrachten seines Caches auf dem neuesten Stand lösen könnte. Ich glaube nicht, dass es eine Möglichkeit gibt, den Cache in say ... js oder mit einer serverseitigen Anweisung zu löschen.
Krieg

Diese Antwort bietet einige Möglichkeiten zu überprüfen, insbesondere wenn Ihre
Tyler S. Loeper

Antworten:


211

Benutzer können weiterhin auf Ihrer Website surfen, da Cookies beim Anruf nicht gelöscht werden FormsAuthentication.SignOut()und bei jeder neuen Anfrage authentifiziert werden. In der MS-Dokumentation heißt es, dass Cookies gelöscht werden, aber nicht, Fehler? Es ist genau das gleiche mit Session.Abandon(), Cookie ist immer noch da.

Sie sollten Ihren Code folgendermaßen ändern:

FormsAuthentication.SignOut();
Session.Abandon();

// clear authentication cookie
HttpCookie cookie1 = new HttpCookie(FormsAuthentication.FormsCookieName, "");
cookie1.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(cookie1);

// clear session cookie (not necessary for your current problem but i would recommend you do it anyway)
SessionStateSection sessionStateSection = (SessionStateSection)WebConfigurationManager.GetSection("system.web/sessionState");
HttpCookie cookie2 = new HttpCookie(sessionStateSection.CookieName, "");
cookie2.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(cookie2);

FormsAuthentication.RedirectToLoginPage();

HttpCookieist im System.WebNamespace. MSDN-Referenz .


18
Das funktioniert bei mir. Es ist jedoch zu beachten, dass die Domain-Eigenschaft, wenn sie beim Anmelden für das FormsAuthentication-Cookie festgelegt wurde, auch festgelegt werden muss, wenn das Cookie beim Abmelden abläuft
Phil Hale,

8
Vergessen Sie
Dmitry Zaets

6
Dies scheint mir eine bessere Lösung zu sein: Response.Cookies [FormsAuthentication.FormsCookieName] .Expires = DateTime.Now.AddDays (-1);
Randy H.

7
@ RandyH. Durch Überschreiben des vorhandenen FormsAuthentication-Cookies mit einem neuen leeren Cookie wird sichergestellt, dass der Client auch dann keine Benutzerdaten aus dem Cookie abrufen kann, wenn er seine Systemuhr zurückstellt.
Tri Q Tran

9
Kann jemand all diese Kommentare in der Antwort kombinieren?
David

22

Mit zwei der oben genannten Postings von x64igor und Phil Haselden wurde dies gelöst:

1. x64igor gab das Beispiel für die Abmeldung an:

  • Sie müssen zuerst das Authentifizierungs- und Sitzungscookie löschen, indem Sie leere Cookies in der Antwort auf die Abmeldung zurückgeben.

    public ActionResult LogOff()
    {
        FormsAuthentication.SignOut();
        Session.Clear();  // This may not be needed -- but can't hurt
        Session.Abandon();
    
        // Clear authentication cookie
        HttpCookie rFormsCookie = new HttpCookie( FormsAuthentication.FormsCookieName, "" );
        rFormsCookie.Expires = DateTime.Now.AddYears( -1 );
        Response.Cookies.Add( rFormsCookie );
    
        // Clear session cookie 
        HttpCookie rSessionCookie = new HttpCookie( "ASP.NET_SessionId", "" );
        rSessionCookie.Expires = DateTime.Now.AddYears( -1 );
        Response.Cookies.Add( rSessionCookie );
    

2. Phil Haselden hat das obige Beispiel gegeben, wie das Zwischenspeichern nach dem Abmelden verhindert werden kann:

  • Sie müssen den Cache auf der Clientseite über die Antwort ungültig machen .

        // Invalidate the Cache on the Client Side
        Response.Cache.SetCacheability( HttpCacheability.NoCache );
        Response.Cache.SetNoStore();
    
        // Redirect to the Home Page (that should be intercepted and redirected to the Login Page first)
        return RedirectToAction( "Index", "Home" ); 
    }
    

1
Verschwendete den ganzen Tag bei der Arbeit, um dieses Problem zu lösen. Sobald Sie angemeldet sind, wird über die Schaltfläche zum Abmelden eine falsche Aktion im Controller aufgerufen (Anmelden statt Abmelden). Vielen Dank, dies hat das Problem gelöst. Entwicklungsumgebung: ASP.NET 4.51 MVC 5.1
Ako

1
Gute Antwort! Bescheidener Vorschlag: Verwenden Sie das Formular zum Löschen von Sitzungscookies. X64igor verwendet : SessionStateSection sessionStateSection = (SessionStateSection)WebConfigurationManager.GetSection("system.web/sessionState"); HttpCookie sessionCookie = new HttpCookie(sessionStateSection.CookieName, "");. Im Allgemeinen lautet der Name des Sitzungscookies nicht "ASP.NET_SessionId".
Seebiscuit

20

Klingt für mich so, als hätten Sie Ihren autorisierungsbereich web.config nicht richtig eingerichtet. Ein Beispiel finden Sie weiter unten.

<authentication mode="Forms">
  <forms name="MyCookie" loginUrl="Login.aspx" protection="All" timeout="90" slidingExpiration="true"></forms>
</authentication>
<authorization>
  <deny users="?" />
</authorization>

Dies ist eine viel einfachere Lösung, ich würde dies als Antwort markieren. Da ich eine Codeversion auf verschiedenen Servern auf einem hatte, musste ich keine zusätzlichen Eigenschaften festlegen, die Sie hier und auf anderen hinzugefügt haben. Das Ändern von Code sollte also keine richtige Lösung sein. Das Ändern der Konfiguration ist besser.
Vladimir Bozic

Standardmäßig ist die slidingExpiration auf true gesetzt ( msdn.microsoft.com/library/1d3t3c61(v=vs.100).aspx ). Dies führt schließlich dazu, dass das Cookie nach x Minuten ungültig wird, wie im Timeout festgelegt - und nicht, wenn der Benutzer über SignOut () abgemeldet ist. Dies führt also nicht zu dem gewünschten Verhalten, um einen Benutzer mithilfe von FormsAuthentication abzumelden. Bitte korrigieren Sie mich, wenn ich falsch liege.
OlafW

12

Der Schlüssel hier ist, dass Sie sagen "Wenn ich eine URL direkt eingebe ...".

Standardmäßig speichert der Browser unter Formularauthentifizierung Seiten für den Benutzer zwischen. Wenn Sie also eine URL direkt aus der Dropdown-Liste des Adressfelds des Browsers auswählen oder eingeben, wird die Seite möglicherweise aus dem Cache des Browsers abgerufen und Sie kehren niemals zum Server zurück, um die Authentifizierung / Autorisierung zu überprüfen. Die Lösung hierfür besteht darin, das clientseitige Caching im PageLoad-Ereignis jeder Seite oder im OnLoad () Ihrer Basisseite zu verhindern:

Response.Cache.SetCacheability(HttpCacheability.NoCache);

Vielleicht möchten Sie auch anrufen:

Response.Cache.SetNoStore();

11

Ich habe auch schon früher damit gekämpft.

Hier ist eine Analogie zu dem, was anscheinend vor sich geht ... Ein neuer Besucher, Joe, kommt auf die Website und meldet sich über die Anmeldeseite mit FormsAuthentication an. ASP.NET generiert eine neue Identität für Joe und gibt ihm ein Cookie. Dieser Keks ist wie der Schlüssel zum Haus, und solange Joe mit diesem Schlüssel zurückkommt, kann er das Schloss öffnen. Jeder Besucher erhält einen neuen Schlüssel und ein neues Schloss.

Wenn FormsAuthentication.SignOut()es aufgerufen wird, weist das System Joe an, den Schlüssel zu verlieren. Normalerweise funktioniert dies, da Joe den Schlüssel nicht mehr hat und nicht mehr hineinkommen kann.

Wenn Joe jemals Allerdings kommt zurück und macht diese verlorenen Schlüssel haben, ist er ließ wieder in!

Nach allem, was ich sagen kann, gibt es keine Möglichkeit, ASP.NET anzuweisen, das Schloss an der Tür zu ändern!

Ich kann damit leben, indem ich mich an Joes Namen in einer Sitzungsvariablen erinnere. Wenn er sich abmeldet, verlasse ich die Sitzung, damit ich seinen Namen nicht mehr habe. Um später zu überprüfen, ob er zugelassen ist, vergleiche ich einfach seinen Identity.Name mit dem der aktuellen Sitzung. Wenn diese nicht übereinstimmen, ist er kein gültiger Besucher.

Kurz gesagt, verlassen Sie sich für eine Website NICHT darauf, User.Identity.IsAuthenticatedohne auch Ihre Sitzungsvariablen zu überprüfen!


8
+ 1, ich denke, dies wird als "Cookie-Wiederholungsangriff" bezeichnet. Es gibt einen Artikel über Einschränkungen von FormsAuthentication.SignOut: support.microsoft.com/kb/900111
Dmitry

3
Für alle, die dem obigen Link folgen möchten, ist es tot. Sie können versuchen, die WaybackMachine zu verwenden, um hier eine Kopie dieser Seite zu erhalten, aber sie versucht SOFORT, den Benutzer umzuleiten. web.archive.org/web/20171128133421/https://…
Killa-Byte

7

Nach langem Suchen hat das bei mir endlich geklappt. Ich hoffe, es hilft.

public ActionResult LogOff()
{
    AuthenticationManager.SignOut();
    HttpContext.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);
    return RedirectToAction("Index", "Home");
}

<li class="page-scroll">@Html.ActionLink("Log off", "LogOff", "Account")</li>

Ich habe jahrelang Webanwendungen in PHP entwickelt. Also bin ich neu bei MVC ... Ich gebe zu, ich liebe es, ABER wer hätte gedacht, dass etwas so Einfaches wie das Abmelden von jemandem so schwierig wäre? Ich habe jedes andere Skript auf dieser Seite ausprobiert und dies ist das einzige, das funktioniert hat. Danke fürs Schreiben!
Anthony Griggs

6

Das funktioniert bei mir

public virtual ActionResult LogOff()
    {
        FormsAuthentication.SignOut();
        foreach (var cookie in Request.Cookies.AllKeys)
        {
            Request.Cookies.Remove(cookie);
        }
        foreach (var cookie in Response.Cookies.AllKeys)
        {
            Response.Cookies.Remove(cookie);
        }
        return RedirectToAction(MVC.Home.Index());
    }

3

Der von Ihnen veröffentlichte Code sollte das Formularauthentifizierungstoken korrekt entfernen, sodass die betreffenden Ordner / Seiten möglicherweise nicht wirklich geschützt sind.

Haben Sie bestätigt, dass auf die Seiten nicht zugegriffen werden kann, bevor eine Anmeldung erfolgt ist?

Können Sie die von Ihnen verwendeten Einstellungen für web.config und den Anmeldecode veröffentlichen?


3

Ich habe für alle meine Seiten eine Basisklasse geschrieben und bin zu demselben Thema gekommen. Ich hatte Code wie den folgenden und es hat nicht funktioniert. Durch die Ablaufverfolgung geht die Steuerung von der Anweisung RedirectToLoginPage () in die nächste Zeile über, ohne umgeleitet zu werden.

if (_requiresAuthentication)
{
    if (!User.Identity.IsAuthenticated)
        FormsAuthentication.RedirectToLoginPage();

    // check authorization for restricted pages only
    if (_isRestrictedPage) AuthorizePageAndButtons();
}

Ich fand heraus, dass es zwei Lösungen gibt. Entweder um FormsAuthentication.RedirectToLoginPage () zu ändern; sein

if (!User.Identity.IsAuthenticated)
    Response.Redirect(FormsAuthentication.LoginUrl);

ODER, um die web.config durch Hinzufügen zu ändern

<authorization>
  <deny users="?" />
</authorization>

Im zweiten Fall erreichte die Steuerung während der Ablaufverfolgung nicht die angeforderte Seite. Es wurde sofort zur Anmelde-URL umgeleitet, bevor der Haltepunkt erreicht wurde. Daher ist die SignOut () -Methode nicht das Problem, sondern die Redirect-Methode.

Ich hoffe das kann jemandem helfen

Grüße


2
Sie können Response.End () auch direkt nach dem Aufruf von FormsAuthentication.RedirectToLoginPage () aufrufen
murki

Ich denke, es gibt ein bisschen Missverständnisse seitens der MS. Sie müssen Personen sperren, wenn Sie möchten, dass sie zur Anmeldeseite zurückkehren. Andernfalls ermöglicht Ihnen das Framework den Zugriff. Sie müssen also Lösung 2 in diesem Beitrag sagen.
Josh Robinson

3

Ich habe gerade einige der Vorschläge hier ausprobiert und während ich die Zurück-Schaltfläche des Browsers verwenden konnte, schickte mich das [Autorisieren] -Token für dieses [ActionResult] direkt zum Anmeldebildschirm zurück, als ich auf eine Menüauswahl klickte.

Hier ist mein Abmeldecode:

        FormsAuthentication.SignOut();
        Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
        Response.Cache.SetExpires(DateTime.Now.AddSeconds(-1));
        HttpCookie cookie = HttpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
        if (cookie != null)
        {
            cookie.Expires = DateTime.Now.AddDays(-1);
            Response.Cookies.Add(cookie);
        }

Obwohl mich die Zurück-Funktion im Browser zurückgenommen und das gesicherte Menü angezeigt hat (daran arbeite ich noch), konnte ich nichts tun, was in der App gesichert war.

Hoffe das hilft


Vielen Dank. Dies ist die Lösung, die für mich funktioniert hat (keine Notwendigkeit <deny users="?" />in web.config)
Alexei

3

Ich habe die meisten Antworten in diesem Thread ausprobiert, kein Glück. Endete damit:

protected void btnLogout_Click(object sender, EventArgs e)
{
    FormsAuthentication.Initialize();
    var fat = new FormsAuthenticationTicket(1, "", DateTime.Now, DateTime.Now.AddMinutes(-30), false, string.Empty, FormsAuthentication.FormsCookiePath);
    Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(fat)));
    FormsAuthentication.RedirectToLoginPage();
}

Fand es hier: http://forums.asp.net/t/1306526.aspx/1


3

Diese Antwort ist technisch identisch mit Khosro.Pakmanesh. Ich poste es, um zu verdeutlichen, wie sich seine Antwort von anderen Antworten in diesem Thread unterscheidet und in welchem ​​Anwendungsfall sie verwendet werden kann.

Im Allgemeinen, um eine Benutzersitzung zu löschen, tun

HttpContext.Session.Abandon();
FormsAuthentication.SignOut();

wird den Benutzer effektiv abmelden. Jedoch , wenn in der gleichen Anfrage Sie überprüfen müssen Request.isAuthenticated(wie oft in einem Authorization Filter passieren kann, zum Beispiel), dann werden Sie feststellen , dass

Request.isAuthenticated == true

sogar nachdem du es getan hast HttpContext.Session.Abandon()und FormsAuthentication.SignOut().

Das einzige, was funktionierte, war zu tun

AuthenticationManager.SignOut();
HttpContext.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);

Das setzt effektiv ein Request.isAuthenticated = false.


2

Dies passierte mir, als ich die Eigenschaft Authentifizierung> Formulare> Pfad in festlegteWeb.config . Durch das Entfernen wurde das Problem behoben, und durch einfaches FormsAuthentication.SignOut();Entfernen wurde das Cookie erneut entfernt.


1

Möglicherweise melden Sie sich von einer Subdomain (sub1.domain.com) aus an und versuchen dann, sich von einer anderen Subdomain (www.domain.com) abzumelden.


1

Ich hatte gerade das gleiche Problem, bei dem SignOut () das Ticket anscheinend nicht richtig entfernen konnte. Aber nur in einem bestimmten Fall, in dem eine andere Logik eine Umleitung verursachte. Nachdem ich diese zweite Weiterleitung entfernt (durch eine Fehlermeldung ersetzt) ​​hatte, verschwand das Problem.

Das Problem muss gewesen sein, dass die Seite zum falschen Zeitpunkt umgeleitet wurde und somit keine Authentifizierung auslöste.


1

Ich habe jetzt ein ähnliches Problem und ich glaube, dass das Problem in meinem Fall sowie im Originalplakat auf die Weiterleitung zurückzuführen ist. Standardmäßig verursacht eine Response.Redirect eine Ausnahme, die sofort auftritt, bis sie abgefangen wird und die Umleitung sofort ausgeführt wird. Ich vermute, dass dies verhindert, dass die geänderte Cookie-Sammlung an den Client weitergegeben wird. Wenn Sie Ihren Code zur Verwendung ändern:

Response.Redirect("url", false);

Dies verhindert die Ausnahme und scheint es zu ermöglichen, dass das Cookie ordnungsgemäß an den Client zurückgesendet wird.


1

Versuchen Sie einfach, eine Sitzungsvariable zu senden, wenn Sie auf Anmelden klicken. Überprüfen Sie auf der Begrüßungsseite zunächst, ob diese Sitzung beim Laden der Seite oder beim Init-Ereignis wie folgt leer ist:

if(Session["UserID"] == null || Session["UserID"] == "")
{
    Response.Redirect("Login.aspx");
}

1

Für mich funktioniert der folgende Ansatz. Ich denke, wenn nach der Anweisung "FormsAuthentication.SignOut ()" ein Fehler auftritt, funktioniert SingOut nicht.

public ActionResult SignOut()
    {
        if (Request.IsAuthenticated)
        {
            FormsAuthentication.SignOut();

            return Redirect("~/");
        }
        return View();
     }

0

Testen / sehen Sie dieses Verhalten mit IE? Es ist möglich, dass der IE diese Seiten aus dem Cache bereitstellt. Es ist bekanntermaßen schwierig, den IE dazu zu bringen, den Cache zu leeren. Wenn Sie also die URL einer der "gesicherten" Seiten eingeben, wird in vielen Fällen der zwischengespeicherte Inhalt von zuvor angezeigt, selbst wenn Sie sich abmelden.

(Ich habe dieses Verhalten auch dann gesehen, wenn Sie sich als ein anderer Benutzer anmelden und der IE die Begrüßungsleiste oben auf Ihrer Seite mit dem Benutzernamen des alten Benutzers anzeigt. Heutzutage wird es normalerweise durch ein erneutes Laden aktualisiert, aber wenn es dauerhaft ist könnte es immer noch ein Caching-Problem sein.)


0

Session.abandon () zu machen und den Cookie zu zerstören funktioniert ziemlich gut. Ich verwende mvc3 und es sieht so aus, als ob das Problem auftritt, wenn Sie zu einer geschützten Seite gehen, sich abmelden und über Ihren Browserverlauf gehen. Keine große Sache, aber trotzdem nervig.

Der Versuch, Links in meiner Web-App zu durchsuchen, funktioniert jedoch richtig.

Wenn Sie festlegen, dass kein Browser-Caching ausgeführt wird, ist dies möglicherweise der richtige Weg.


0

Für MVC funktioniert das bei mir:

        public ActionResult LogOff()
        {
            FormsAuthentication.SignOut();
            return Redirect(FormsAuthentication.GetRedirectUrl(User.Identity.Name, true));
        }

0

Ich wollte einige Informationen hinzufügen, um das Problem besser zu verstehen. Die Formularauthentifizierung ermöglicht das Speichern von Benutzerdaten entweder in einem Cookie oder in der Abfragezeichenfolge der URL. Die von Ihrer Site unterstützte Methode kann in der Datei web.config konfiguriert werden.

Laut Microsoft :

Die SignOut-Methode entfernt die Informationen zum Formularauthentifizierungsticket aus dem Cookie oder der URL, wenn CookiesSupported false ist .

Gleichzeitig sagen sie :

Einer der HttpCookieMode-Werte, der angibt, ob die Anwendung für die Authentifizierung ohne Cookies konfiguriert ist. Der Standardwert ist UseDeviceProfile .

In Bezug auf UseDeviceProfile heißt es schließlich :

Wenn die CookieMode-Eigenschaft auf UseDeviceProfile festgelegt ist, gibt die CookiesSupported-Eigenschaft true zurück, wenn der Browser für die aktuelle Anforderung sowohl Cookies als auch das Umleiten mit Cookies unterstützt . Andernfalls gibt die CookiesSupported-Eigenschaft false zurück.

Wenn Sie dies alles zusammenfügen, kann die Standardkonfiguration je nach Browser des Benutzers dazu führen, dass CookiesSupported true ist. Dies bedeutet, dass die SignOut-Methode das Ticket nicht aus dem Cookie löscht. Dies scheint nicht intuitiv zu sein und ich weiß nicht, warum es so funktioniert. Ich würde erwarten, dass SignOut den Benutzer unter keinen Umständen tatsächlich abmeldet.

Eine Möglichkeit, das SignOut selbstständig zu machen, besteht darin, den Cookie-Modus in der Datei web.config in "UseCookies" (dh Cookies sind erforderlich) zu ändern:

<authentication mode="Forms">
  <forms loginUrl="~/Account/SignIn" cookieless="UseCookies"/>
</authentication>

Nach meinen Tests funktioniert SignOut auf Kosten Ihrer Website von selbst, sodass Cookies nun ordnungsgemäß funktionieren müssen.


Ich denke, du liest das falsch. In Bezug auf SignOut () bin ich mir ziemlich sicher, dass es bedeutet, dass es aus der URL gelöscht wird, wenn CookiesSupported falsch ist, andernfalls aus dem Cookie. Das heißt, sie hätten schreiben sollen: "Die SignOut-Methode entfernt die Ticketinformationen für die Formularauthentifizierung aus dem Cookie oder, wenn CookiesSupported falsch ist, aus der URL."
Oskar Berggren

-1

Beachten Sie, dass WIF es ablehnt , den Browser anzuweisen, die Cookies zu bereinigen, wenn die wsignoutcleanup-Nachricht von STS nicht mit der URL mit dem Namen der Anwendung von IIS übereinstimmt , und ich meine CASE SENSITIVE . WIF antwortet mit der grünen OK-Prüfung, sendet jedoch nicht den Befehl zum Löschen von Cookies an den Browser.

Sie müssen also auf die Groß- und Kleinschreibung Ihrer URLs achten.

Beispielsweise speichert ThinkTecture Identity Server die URLs der besuchenden RPs in einem Cookie, macht sie jedoch alle in Kleinbuchstaben. WIF empfängt die Nachricht wsignoutcleanup in Kleinbuchstaben und vergleicht sie mit dem Anwendungsnamen in IIS. Wenn es nicht übereinstimmt, werden keine Cookies gelöscht, aber dem Browser wird OK gemeldet. Für diesen Identity Server musste ich also alle URLs in web.config und alle Anwendungsnamen in IIS in Kleinbuchstaben schreiben, um solche Probleme zu vermeiden.

Vergessen Sie auch nicht, Cookies von Drittanbietern im Browser zuzulassen, wenn Sie die Anwendungen außerhalb der Subdomain von STS haben. Andernfalls löscht der Browser die Cookies nicht, selbst wenn WIF dies ihm mitteilt.


1
WIF? STS? ThinkTecture Identity Server? Was sind all diese Dinge und wie hängen sie mit dieser Frage zusammen?
Oskar Berggren
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.