Login mit Hilfe von PHP und mySQL

Dieses Tutorial soll die Grundzüge eines Login-Systems auf der Basis von PHP und mySQL unter Zuhilfenahme von Sessions klären.

Login mit Hilfe von PHP und mySQL

Dieses Tutorial soll die Grundzüge eines Login-Systems auf der Basis von PHP und mySQL unter Zuhilfenahme von Sessions klären.

Keinesfalls findet sich hier ein vollständiges und bis ins kleinste Detail ausgefeilte Benutzermanagement, weder mit noch ohne expliziter Rechtevergabe.

Ebenfalls erhebt dieses Tutorial nicht den Anspruch die ultimative Lösung zu sein, aber es ist eine einfache, leicht verständliche und recht flexible, da leicht erweiterbare Variante.

Inhaltsverzeichnis

  1. Sinn und Zweck eines Login-Systems

  2. Gründe für PHP und mySQL

  3. Die Tabelle

  4. Das Loginformular

  5. Das Einloggen

  6. Das Überprüfen

  7. Das Ausloggen

  8. Die Datei sessionhelpers.inc.php

  9. Der Schluss


Sinn und Zweck eines Login-Systems

Der Sinn und Zweck einer Benutzerverwaltung sollte wohl jedem klar sein. Grob gesagt sprechen folgende Gründe dafür:

Der Webmaster kann

  • entscheiden, wer wann welche Teile der Seite zu sehen bekommt (inkl. Downloads)

  • nachvollziehen, welcher Benutzer wann wo war

  • nachvollziehen, wer wann was gemacht hat

  • Benutzer, die sich etwas zu Schulden kommen ließen, sperren

  • ganz allgemein gesagt entscheiden, dass Herr X bestimmte Aktionen machen darf, Herr Y andere und Herr Z nur ein paar der Aktionen die Herr X machen darf

Gründe für PHP und mySQL 

Gründe für PHP

Der Login (als ein Bauteil einer Benutzerverwaltung) muss auf dem Server ablaufen, auf dem die Seiten gespeichert sind. Das impliziert die Voraussetzung, dass eine Möglichkeit gegeben sein muss, auf dem Server Benutzereingaben auszuwerten und entsprechend darauf zu reagieren.

PHP ist eine kostenlose Skriptsprache, die von vielen Webhostern zur Verfügung gestellt wird und serverseitige Aktionen erlaubt. Die Syntax ist an C angelehnt, jeder, der damit schon Erfahrung hat, wird sich sofort zu Recht finden, jeder andere wird es schnell lernen. An dieser Stelle sei kurz auf http://www.php.net verwiesen und dort insbesondere auf das Handbuch (neudeutsch Manual)

Gründe für mySQL

Die Daten für den Login, gegen die die Eingaben des Benutzers zum Einloggen abgeprüft werden, müssen natürlich auch gespeichert werden. Hierzu gibt es (wie so oft) mehrere Möglichkeiten:

  1. Speichern im Skript

    • Am einfachsten zu realisieren, aber wehe, es kommen Benutzer hinzu oder es sollen Passwörter geändert werden.

    • Ganz zu schweigen von den oben erwähnten Funktionen

  2. Speichern in Textdateien

    • Schon besser, aber auch unkomfortabel

    • Entweder für jeden Benutzer eine eigene Datei, oder eine Datei für alle auf jeden Fall schwierig zu erweitern

    • nur sequentieller Zugriff, daher langsam

  3. Speichern in einer Datenbank

    • einfacher Zugriff

    • wahlfreier Zugriff

    • leicht erweiterbar

    • übersichtlich

Die Tabelle

Theoretisch kann der Webmaster auswählen, ob er die Daten aller Benutzer in einer Tabelle haben will, oder ob er für jeden Benutzer eine separate Tabelle anlegen will.
Logisch ist es, nur eine Tabelle zu verwenden, da zusammengehörige Daten gleicher Struktur auch an einer zentralen Stelle gesammelt werden sollten. Praktisch ist es auch, da so eine spätere Modifikation oder Erweiterung der Datenstruktur unabhängig von der Anzahl an Benutzern nur für eine Tabelle vorgenommen werden muss.

Bei der Überlegung, welche Spalten in der Tabelle enthalten sein sollen, muss unterschieden werden zwischen:

  1. Notwendige Spalten

  2. Optionale Spalten

  3. Zusatzspalten (Die Kür)

Und natürlich findet sich hier auch das SQL-Statement, um die Tabelle tatsächlich zu erstellen.

Notwendige Spalten Um sich einzuloggen, gibt der Benutzer unseres Systems seinen Namen und sein Passwort an Da wir den Benutzer also anhand dieser Daten identifizieren, müssen wir zwingend diese beiden Spalten in unserer Tabelle bereitstellen.

Da wir den Namen des Benutzers nutzen, um ihn zu identifizieren, darf es nicht mehrere Benutzer geben, die den gleichen Namen in unserem System verwenden. mySQL hilft uns hier, da es die Eigenschaft UNIQUE zur Verfügung stellt.

Daraus folgt, dass wir zwingend zwei Spalten brauchen:

  1. Name des Benutzers, mit dem er im System agieren will (vergleichbar dem Nickname in einem Forum wie dem vBulletin)

  2. Das Passwort des Benutzers

Optionale Spalten

Nachdem wir jetzt alle Felder haben, die wir unbedingt brauchen, kommen jetzt die Spalten, die empfehlenswert, weil komfortabel sind.

Wie oben bereits geschrieben, wollen wir Sessions verwenden.
Da wir die Session immer starten müssen, können wir nicht davon ausgehen, dass sich hinter jeder Session ein eingeloggter Benutzer verbirgt.
Was spricht also dagegen, die Tabelle um eine Spalte für die ID der Session des Benutzers zu erweitern?
Bei nicht eingeloggten Benutzern steht in der entsprechenden Spalte natürlich nichts.

Natürlich könnte nach erfolgreichem Anmelden eine Variable in der Session registriert werden und dann nur diese abgefragt werden, aber die Variante wie wir sie hier machen, ist meiner Meinung nach besser, weil sie flexibler im Hinblick auf Erweiterungen wie z. B. Rechtevergabe und -entzug ist.

Mit Hilfe dieser Spalte kann natürlich auch festgestellt werden, welche Benutzer momentan eingeloggt sind und welche nicht.

Um uns (auch und vor allem) im Hinblick auf eine einfache Verwendung der Tabelle mitsamt der darin enthaltenen Benutzer die Arbeit zu erleichtern, wäre es vorteilhaft, die Zahl der notwendigen Spalten, um einen Benutzer eindeutig zu identifizieren, zu minimieren und den Inhalt dieser Spalte so klein wie nur irgend möglich zu halten.
Eingeloggte Benutzer können anhand ihrer eindeutigen Session-ID identifiziert werden, aber da nicht immer alle Benutzer eingeloggt sind, scheidet die Spalte aus.
Jeder Benutzer kann über seinen eindeutigen Namen im System identifiziert werden, aber ich persönlich bevorzuge eine weitere Spalte. Diese Spalte beinhaltet die interne ID des Benutzers. Die ID ist eine eindeutige Zahl, anhand derer ein Benutzer ganz zweifelsfrei und unproblematisch identifiziert werden kann.

Ebenfalls zu empfehlen ist eine Spalte, die die Email-Adresse des Benutzers beherbergt.
Mit Hilfe der Email-Adresse können Benutzer über wichtige Änderungen informiert werden und außerdem kann die Email-Adresse zur Verifikation der Existenz des Benutzers genutzt werden.

Daraus folgt, dass wir noch die folgenden Spalten in die Tabelle aufnehmen:

  1. Benutzer-ID (diese Spalte wird als Primärschlüssel verwendet)

  2. Session-Spalte, die die Session-ID eines momentan eingeloggten Benutzers beinhaltet

  3. Mail-Spalte, die die Email-Adresse des Benutzers enthält

Zusatzspalten (Die Kür)

Hier ein kleiner Ausblick auf mögliche Erweiterungen:

  1. Account-Freischaltung anhand eines Aktivierungslinks an die gespeicherte Email-Adresse

  2. Anzahl fehlerhafter Login-Versuche (ggf. inklusive Sperren des Accounts und Zusenden eines neuen Passwortes bei Überschreitung eines Limits)

  3. Prüfen, ob ein Benutzer gesperrt ist (ggf. auch warum)

  4. Prüfen, ob ein Benutzer seinen Account aktiviert hat (kann zur Prüfung der Email-Adresse verwendet werden)

  5. Zeitpunkt des Einloggens

  6. Zeitpunkt der letzten Aktion

  7. Zeitpunkt des Ausloggens

  8. Spalte mit Benutzerlevel

  9. Benutzer in verschiedene Gruppen einteilen

  10. ...

Das SQL-Statement

Folgendes SQL-Statement erstellt die Tabelle inklusive der notwendigen und optionalen Felder:

CREATE TABLE users ( UserID int(11) PRIMARY KEY auto_increment, UserName varchar(30) NOT NULL default '', UserPass varchar(32) NOT NULL default '', UserSession varchar(32), UserMail varchar(150) NOT NULL default '', UNIQUE KEY NickName (UserName), UNIQUE KEY UserMail (UserMail) )

PRIMARY KEY besagt, dass die Spalte UserID der Primärschlüssel der Tabelle ist, also jeden Datensatz eindeutig identifiziert. auto_increment besagt, dass der Wert dieser Spalte automatisch um 1 erhöht wird,wenn ein neuer Benutzer angelegt wird, d. h. die Datenbank kümmert sich selbst darum, dass die Eindeutigkeit gewährleistet bleibt. Toll, oder?

UNIQUE KEY besagt, dass die Datenbank auch von alleine darauf achtet, dass in der Spalte, die zwischen den Klammern steht, kein Wert doppelt eingetragen wird.

Und so sieht die Tabelle aus:

Field Type Null Key Default Extra
UserID int(11)   PRI NULL auto_increment
UserName varchar(30)   UNI    
UserPass varchar(32)        
UserSession varchar(32) YES   NULL  
UserMail varchar(150)   UNI    

Das Passwort

Hier kommt jetzt kein Hinweis darauf, dass Passwörter wie geheim, 007, passwort, der Geburtstag des Kanarienvogels rückwärts nicht empfehlenswert sind.

Ganz allgemein sollte man aber sagen:

  • je länger das Passwort, desto sicherer.

  • Groß- und Kleinbuchstaben, Sonderzeichen und Ziffern sollten alle verwendet werden.

Akzeptabel wäre zum Beispiel ein Passwort mit 6 Zeichen Läge, dass aus Buchstaben, Ziffern und Sonderzeichen besteht, aber eine dahingehende Prüfung hat mit dem eigentlichen Thema nicht direkt zu tun. Diese kleine Ausführung ist mehr als Anregung und Gedankenanstoß zu sehen.

Worauf ich eigentlich hinaus wollte, war:

Das Passwort wird in der Tabelle verschlüsselt

So kann niemand (wirklich niemand) das Passwort eines Benutzers auslesen.
Selbstverständlich muss das vom Benutzer eingegebene Passwort ebenfalls verschlüsselt werden, bevor wir es mit dem in der Tabelle abgleichen können.

Zum verschlüsselten Speichern verwenden wir eine Funktion, die uns SQL zur Verfügung stellt.
Ihr Name lautet MD5.
Die Funktion werden wir beim Anlegen des Beispielbenutzers und beim Einloggen in Aktion sehen.

Der Beispielbenutzer

Damit wir später auch kontrollieren können, ob unser Login funktioniert, legen wir jetzt einen Benutzer in unserem System an.
Der Benutzer wird folgende Daten haben:

Tabellenspalte Wert
UserID ?
irrelevant, da die Spalte von der Datenbank
automatisch befüllt wird
UserName admin
UserPass tutorial
UserSession NULL
wird nicht angegeben, da NULL laut unserer
Tabellendefinition der Standardwert ist
UserMail mrhappiness@inter.net

Daraus ergibt sich folgendes SQL-Statement:

INSERT INTO users SET UserName='admin', UserPass=MD5('tutorial'), UserMail='mrhappiness@inter.net'

Danach sieht unsere Tabelle so aus:

UserID UserName UserPass UserSession UserMail
1 admin 0575c8d592fb7b088226750aceec2b4e NULL mrhappiness@inter.net

Wie man unschwer erkennen kann, haben wir als Passwort tutorial verwendet, aber stattdessen steht auf einmal 0575c8d592fb7b088226750aceec2b4e in der Spalte UserMail. Diese Umwandlung hat die bereits erwähnte SQL-Funktion MD5 erledigt.

Das Loginformular

Natürlich wollen wir nicht, dass die Benutzer unseres Systems direkten Kontakt zur Datenbank mit unserer Benutzertabelle haben. Dagegen sprechen Gründe wie Datensicherheit, Datenschutz und nicht zuletzt natürlich auch der Komfort.
Es ist schließlich nicht nur bequemer, sondern auch allgemein üblich die Anmeldedaten in ein Formular einzugeben und dieses dann abzuschicken.

Solch ein Formular wollen wir jetzt (ganz rudimentär) erstellen:

<form method="post" action="login.php">
  <label>Benutzername:</label><input name="username" type="text">
  <br>
  <label>Passwort: </label><input name="userpass" type="password" id="userpass">
  <br>
  <input name="login" type="submit" id="login" value="Einloggen">
</form>

Zur kurzen Erläuterung:

  • method="post" bedeutet, dass die Daten des Formulares versteckt versendet werden, also nicht in der Adressleiste des Browser auftauchen.

  • action="login.php" bedeutet, dass die Daten des Formulares an die Datei login.php gesendet werden. Diese Datei ist für die Auswertung der Daten zuständig.

  • Es gibt zwei Textfelder mit den Namen username und userpass.
    Der Inhalt der Felder steht uns in PHP in einer Variablen mit gleichem Namen zur Verfügung.

Diese Datei speichern wir unter dem Namen login.php ab. Das heißt, dass wir die Daten sozusagen an uns selbst schicken. So können wir die Prüfung der Daten, das Einloggen und das erneute Anzeigen des Formulares im Fehlerfall am bequemsten durchführen.

Das Einloggen

Nachdem wir jetzt ein fast bildhübsches Formular haben, fehlt uns nur noch die serverseitige Logik, mit Hilfe derer wir die eingegebenen Daten verifizieren und angemessen reagieren.
Daher laden wir die Datei login.php und erweitern sie wie folgt:
Wie gehabt, kommt erst der Code und danach die Erklärung:

<?php
session_start
();
include 
'sessionhelpers.inc.php';
if (isset(
$_POST['login']))
{
    
$userid=check_user($_POST['username'], $_POST['userpass']);
    if (
$userid!=false)
        
login($userid);
    else
        echo 
'Ihre Anmeldedaten waren nicht korrekt!';
}
if (!
logged_in())
    echo 
'<form method="post" action="login.php">
        <label>Benutzername:</label><input name="username" type="text"><br>
        <label>Passwort: </label><input name="userpass" type="password" id="userpass"><br>
        <input name="login" type="submit" id="login" value="Einloggen">
    </form>'
;
else
    echo 
'<a href="logout.php">Ausloggen</a>';
echo 
'<p /><a href="logged_in.php">Check</a>';
?>

Die erste Zeile startet die Session. Dieser Befehl muss auf allen Seiten stehen, und zwar überall als erster Befehl, da es sonst unter Umständen zu unschönen Fehlermeldungen kommt.
Die zweite Zeile bindet die Datei sessionhelpers.inc.php ein, in der ein paar Funktionen zum Ein- und Ausloggen stehen.
Die dritte Zeile prüft, ob unser Formular abgeschickt wurde.
Ist das der Fall wird mit der Funktion check_user die UserID des Benutzers erfragt, zu dem die eingegebenen Daten passen.

Es gibt hier zwei Möglichkeiten:

  • Es wird eine UserID zurückgeliefert. Daraus folgt, dass die Anmeldedaten korrekt waren und der Benutzer wird mit der Funktion login eingeloggt.
  • Es wird false zurückgeliefert. Daraus folgt, dass die Anmeldedaten nicht korrekt waren und es wird eine entsprechende Meldung ausgegeben.

Die folgenden Schritte werden immer durchgeführt, also auch dann, wenn der Benutzer kein Formular abgeschickt hat:
Es wird geprüft, ob der Benutzer eingeloggt ist.
Ist dies der Fall wird ein Link ausgegeben, der ihm das Ausloggen ermöglicht, anderenfalls wird das Login-Formular angezeigt.

Das Überprüfen

Natürlich reicht es nicht, den Benutzer einmalig einzuloggen.

Es muss auch geprüft werden, ob der Mensch, der gerade eine Seite anklickt, berechtigt ist eben diese zu sehen, ob er also eingeloggt ist. Die Prüfung läuft folgendermaßen:

<?php
session_start
();
include 
'sessionhelpers.inc.php';
echo 
'Sie sind ';
if (!
logged_in())
    echo 
'nicht ';
echo 
'eingeloggt.<p />';
echo 
'<a href="login.php">Start</a>';
?>

Wie man sieht, ist die Prüfung recht kurz:
Die erste Zeile startet wie gehabt die Session. Danach wird wieder die Datei sessionhelpers.inc.php eingebunden, da sie die Funktion logged_in() beinhaltet.

if (!logged_in()) prüft, ob der Benutzer eingeloggt ist. Das Ergebnis wird durch das vorangestellte Ausrufezeichen »!« negiert.
Abhängig vom Ergebnis dieser Abfrage wird die Ausgabe angepasst. Das speichern wir jetzt als logged_in.php.

Das Ausloggen

Das Ausloggen eines Benutzers wird noch kürzer. Den unten stehenden Code speichern wir unter logout.php ab.

<?php
session_start
();
include 
'sessionhelpers.inc.php';
echo 
'Sie sind ';
if (!
logged_in())
    echo 
'nicht ';
echo 
'eingeloggt.<p />';
logout();
echo 
'Sie sind ';
if (!
logged_in())
    echo 
'nicht ';
echo 
'eingeloggt.< p/>';
echo 
'<a href="login.php">Einloggen</a>';
?>

Wie gehabt wird die Session gestartet. Danach wird wieder die Datei sessionhelpers.inc.php eingebunden, da sie die Funktion logout() beinhaltet.

Die Datei sessionhelpers.inc.php

Jetzt kommen wir endlich zu der Datei, die die Funktionalität, die wir schon mehrmals genutzt haben, beinhaltet.

Natürlich könnte man die oben verwendeten Abfragen auf jeder Seite verwenden, vor allem wenn wir sehen, wie wenig Codezeilen sie umfassen, wird sich das auch der ein oder andere fragen, daher hier gleich die Antwort:

Wenn ich die verwendeten Routinen nicht an jeder Stelle, an der sie verwendet werden, sondern nur einmal an einer zentralen Stelle einbinde, hast das mindestens diese 3 Vorteile:

  1. Sollte sich etwas ändern, muss die Änderung nur an der einen zentralen Stelle vorgenommen werden.
    So wird die Gefahr des Übersehens und des Vertippens bei einem Auftreten minimiert.
  2. Die Funktionalität ist so leichter erweiterbar, da nur eine Stelle geändert werden muss.
  3. Wird die Login-Funktionalität auch an anderer Stelle benötigt, müssen die Routinen nicht mühsam aus mehreren Skripten zusammengesucht werden.
<?php
function connect()
{
    
$con=mysql_connect('servername','benutzer','passwort') or die(mysql_error());
    
mysql_select_db('datenbank, die die Benutzertabelle enthält',$con) or die(mysql_error());
}

function 
check_user($name$pass)
{
    
$sql="SELECT UserId
    FROM users
    WHERE UserName='"
.$name."' AND UserPass=MD5('".$pass."')
    LIMIT 1"
;
    
$result=mysql_query($sql) or die(mysql_error());
    if (
mysql_num_rows($result)==1)
    {
        
$user=mysql_fetch_assoc($result);
        return 
$user['UserId'];
    }
    else
        return 
false;
}

function 
login($userid)
{
    
$sql="UPDATE users
    SET UserSession='"
.session_id()."'
    WHERE UserId="
.$userid;
    
mysql_query($sql);
}

function 
logged_in()
{
    
$sql="SELECT UserId
    FROM users
    WHERE UserSession='"
.session_id()."'
    LIMIT 1"
;
    
$result=mysql_query($sql);
      return (
mysql_num_rows($result)==1);
}

function 
logout()
{
    
$sql="UPDATE users
    SET UserSession=NULL
    WHERE UserSession='"
.session_id()."'";
    
mysql_query($sql);
}

connect();
?>

Jetzt fehlt nur noch die Erklärung:

Es gibt, wie man sieht, fünf Funktionen in der Datei.

connect()

Diese Funktion stellt eine Verbindung zum SQL-Server her.
Die Parameter servername, benutzer und passwort sind natürlich anzupassen.

check_user($name, $pass)

Diese Funktion prüft die im Formular (oder ggf. im Cookie) eingetragenen Werte und liefert entweder die eindeutige ID des Benutzers zurück, oder false, falls es keinen Datensatz gibt, der die Bedingung erfüllt.

Diese Abfrage heißt übersetzt soviel wie:

Liefere (SELECT) den Wert der Spalte UserID
aus der Tabelle (FROM) users bei der (WHERE) der Wert in der Spalt UserName und der Wert in der Spalte UserMail mit den Formulareingaben übereinstimmen.

Um die Geschwindigkeit etwas zu steigern (es ist aber auch so schon schnell), sagen wir, dass nach einem Datensatz (LIMIT 1) aufgehört werden soll mit der Suche.

login($userid)

Diese Funktion loggt einen Benutzer in unser System ein. Welcher Benutzer eingeloggt werden soll, wird über den Parameter $userid angegeben.
Die ganze Hexerei wird erledigt, indem schlicht und ergreifend die Session-ID (die eindeutig ist) in die Spalte UserSession des Benutzers geschrieben wird.

logged_in()

Diese Funktion prüft, ob der Benutzer eingeloggt ist.
Zu diesem Zweck wird abgefragt, wie viele Einträge in der Benutzertabelle mit der aktuellen Session-ID in der Spalte UserSession vorhanden sind. Wird ein Eintrag gefunden, ist der Benutzer eingeloggt.

logout()

Diese Funktion loggt den Benutzer wieder aus, indem in die Spalte UserSession der Wert NULL geschrieben wird.

Der Schluss

So, das war's auch schon.

Ich hoffe, es war einigermaßen verständlich geschrieben und hat euch weitergeholfen.

Bei Fragen zu dem Tutorial sehen wir uns im Forum.

mrhappiness

Erfahrungen


Super Tutorial, vielen Dank für die Mühe!

Könnte man den Teil mit dem Verschlüsseln des Passwortes eventuell um "salted hashes" erweitern, da ja die reine MD5-Verschlüsselung inzwischen durch Brute-Force-Angriffe geknackt werden kann?

Mehr Informationen dazu: http://heise.de/-1253931
Geschrieben von fmg Am 08.09.2012 10:11:10

Hey superklasse dein Tutorial!

Da steht alles drin was man über PHP als Einsteiger mit programmiererfahrung wissen muss:

Mit PHP kann man einen Linuxrechner (hier: einer mit echt krasser Hardware und Server genannt)
Fernbedienen (hier: befehle ausführen lassen, Datenbanken abfragen)

PHP ist die Schnittstelle zwischen Fernbediener/Nutzer und Linuxrechner

einzigst einen Link zum Fortsetzungskapitel "Rechteverwaltung mit PHP" hätte mir noch gefehlt (oder fällt diese Aufgabe eher dem Linuxsystem zu? also die rechteverwaltung mein ich )


Also mir hats SEHR GEHOLFEN

DANKE+Gruß,

joseppe
Geschrieben von joseppe Am 15.03.2010 06:39:54

Hier Kannst Du einen Kommentar verfassen


Bitte gib mindestens 10 Zeichen ein.
Wird geladen... Bitte warte.
* Pflichtangabe

Verwandte Beiträge

Wird geladen... Bitte warte.
Wird geladen... Bitte warte.

Tutorial veröffentlichen

Tutorial veröffentlichen

Teile Dein Wissen mit anderen Entwicklern weltweit

Du bist Profi in deinem Bereich und möchtest dein Wissen teilen, dann melde dich jetzt an und teile es mit unserer PHP-Community

mehr erfahren

Tutorial veröffentlichen