1. Wat willen we maken ?
Hieronder leggen we uit hoe je een login script maakt. We zullen een heleboel aspecten bestuderen. We maken een login-form, een scriptdeel dat de post-data verzameld en controleerd, een scriptdeel dat een sessie aanmaakt, een scriptdeel dat voorziet in inloggen met cookies en een scriptdeel dat we gebruiken voor het uitloggen. Het hele script moet bovendien makkelijk te integreren zijn in de hele site en herbruikbaar voor andere projecten.
2. Wat hebben we nodig ?
- Daar het om een php-script gaat natuurlijk een server die php kan parsen. Verder wordt er verondersteld dat je een mysql database hebt met een tabel in met de logingegevens van de leden. (dit mag van een bestaand forum zijn) - Jij, een scripter die wat voorkennis heeft van werken met classes (zie tutorial: OOP) en voorkennis van werken met een database en voorkennis van html, vooral forms.
3. De ledentabel en paswoord encryptie.
Met phpAdmin kun je makkelijk jouw database verkennen en een ledentabel aanmaken of zoeken naar de naam van de ledentabel van jouw forum. (mijn voorbeeldscript is voor een invisionforum) Voor dit script zijn slechts drie kolommen nodig: - id: kolom waar de id van het lid in staat (unieke kolom, primary key) - name: kolom waar de naam van het lid in staat - password: kolom waar het passwoord in staat (van mij, en bij een forumtable, staan deze paswoorden normaal gezien in hash-vorm. Paswoorden worden meestal geëncrypteerd in een md5-hash code. Dit kan je door gewoon de functie md5() te gebruiken:
<? $pass_hash = md5($pass); ?>
Koos je bijvoorbeeld als passwoord "sitemasters", dan wordt de hash-code "03b750a5cac94509b4c8ab11e67d9e9b". Uit deze hashcode kan je het originele paswoord niet meer destilleren. (Dit is ook de reden waarom veel sites bij het aanvragen van een vergeten paswoord, een nieuw aanmaken en niet jouw oud teruggeven, omdat ze je paswoord moeilijk kunnen achterhalen) Je kan enkel alle mogelijke woorden aflopen en kijken of het dezelfde hash-code opleverd, maar daarvoor mag je jouw computer enkele uren laten rekenen. De kans dat een ander willekeurig woord dezelfde hashcode oplevert is naar verluidt 1 op een miljard, dus heel klein. In ons script zullen we dus het ingetypte paswoord eerst moeten omzetten naar de hashcode en pas dan kunnen we controleren of het paswoord juist is.
4. De klasse-layout.
Ons login-script schrijven we mbv een klasse, omdat we het makkelijk in onze site willen kunnen implementeren en omdat we met cookies zullen werken. Cookies moeten namelijk aangemaakt worden voor we de headers versturen (dus voordat we enige echo doen) We zullen dus op twee tijdstippen werken, bij het begin van het parsen van een pagina (cookies verzenden/ontvangen) en later voor het echoën van de login-informatie. We delen onze klasse op in verschillende deelscripts, door middel van de klassefuncties:
<? class login { var $ingelogd; function login() { }//end function function controleer_paswoord($a) { }//end function function is_ingelogd() { }//end function function get_output() { }//end function function loguit() { }//end function }//end class $bezoeker = new login(); ?>
5. De klassefunctie is_ingelogd.
De klasse functie is_inlogd() is de gemakkelijkste functie van ons script, het geeft een boolean terug (true of false). Daar we binnen de klasse zelf met een (privé) variable zullen werken die deze waarde bevat, kunnen we gewoon die variable retourneren.
<? function is_ingelogd() { return $this->ingelogd; }//end function ?>
Doordat we bij het uiterste begin van al onze scripts deze klasse hebben geïncluded en de klasse gerund hebben (in $bezoeker) kunnen we in all onze scriptpagina's makkelijk met deze functie controleren of de bezoeker is ingelogd. Dit doen we zo:
<? if( $bezoeker->is_ingelogd() ) doeiets(); //we kunnen dus makkelijk iets koppelen aan het feit ingelogd zijn ?>
6. De klassefunctie get_output.
De klasse-functie get_output() zullen we ook overal in onze scriptpagina's kunnen aanroepen. We zullen dus gewoon een echo kunnen doen op de plaats waar we de output op de pagina willen:
<? echo $bezoeker->get_output(); ?>
De output zal afhangen van het feit of de bezoeker is ingelogd of niet:
<? function get_output() { if($this->is_ingelogd) return 'string met login informatie en een button om in te loggen'; else return 'string met een form om naam en passwoord in te vullen'; }//end function ?>
De html in de string moet je zelf invullen, opties en layout aanpassen aan eigen wensen van onderstaande voorbeeld. Voor meer info over hoe forms werken zie ander tutorial. - voor ingelogde bezoekers: (login-info en button om uit te loggen) De login-info halen we uit de sessie, die we bij het initialiseren hebben aangemaakt (script in punt 7)
<? '<div>Je bent ingelogd als: '.$_SESSION['member_name'].'. Je hebt al '.$_SESSION['count'].' pagina's bezocht. <form action="'.$_SERVER['REQUEST_URI'].'" method="post"><input type="submit" name="loguit" value="Log me uit" /></form></div>' ?>
- voor niet-ingelogde bezoekers: (form om in te loggen)
<? '<div><form action="'.$_SERVER['REQUEST_URI'].'" method="post"><input type="text" name="member_name" /><input type="password" name="pass" /><input type="submit" name="submit" value="Log me in" /></form></div>' ?>
Merk op dat we voor action-url van het form de url van de gevraagde pagina zelf kiezen (m.b.v. $_SERVER['REQUEST_URI'] ), zo zal de bezoeker na in- of uitloggen op dezelfde pagina blijven.
7. De klasse constructor login.
Nu beginnen we aan het echte scripten, de kern van ons script is de loginfunctie. Deze functie moet bij iedere pagina die geladen wordt door een bezoeker worden uitgevoerd. Er zijn 4 mogelijkheden: 1. De bezoeker wil uitloggen (hij heeft op de "log uit"-button geklikt) 2. De bezoeker is al ingelogd in de sessie, tijdens een van de vorige pagina's, hij moet dus ingelogd blijven 3. De bezoeker komt juist toe, en we proberen hem automatisch te laten inloggen (daarvoor halen we zijn id en pass_hash uit de cookie opgeslagen op zijn computer) 4. De bezoeker logt in via het form (er werd dus geen cookie gevonden met login-info) Laten we van start gaan A: We zetten de default waarde voor ingelogd zijn op false. We starten eveneens een sessie met session_start(); dit moet altijd, op iedere pagina waarin we sessies willen gebruiken. Pas daarna kunnen we gegevens opslaan of opvragen aan de sessie en zo cross-pagina gegevensoverdracht doen.
<? function login() { $this->ingelogd = false; //default uitgelogd session_start(); //start een sessie ?>
B1: De bezoeker wil uitloggen. We kijken of in de post-global de variable loguit zit (dat is namelijk de naam van die loguit button, zie hiervoor), dan heeft de bezoeker immers op de loguit button geklikt. Als dit het geval is, dan moet de loguit functie worden uitgevoerd (dat scripten we later).
<? if(isset($_POST['loguit'])) $this->loguit(); ?>
B2: De bezoeker is al ingelogd in de sessie, dit controleren door te kijken of enkele sleutel-sessie variabelen al bestaan. We gebruiken hier de elseif statement omdat dit niet meer hoeft worden gedaan als de bezoeker wilde uitloggen. (Voor wie sessies nog niet goed kent nog even opmerken dat je met session gegevens server-side kunt opslaan over verschillende pagina's heen. Echter je kan dit niet vergelijken met een database, want sessie-gegevens gaan verloren na het sluiten van de browser van de bezoeker of na bep. tijd van inactiviteit, meestal 1 of 2 uur. Sessies bieden dan weer het voordeel op query's naar de database dat ze sneller werken, dus minder server load.) Daarna tellen we een pagina bij, in de sessie variable 'count', waar we bijhouden hoeveel pagina's de gebruiker al deze sessie heeft bezocht.
<? elseif(isset($_SESSION['count'])&isset($_SESSION['member_id'])&isset($_SESSION['member_name'])) { $_SESSION['count']++; //als sessie al bestaat, tel aantal pagina's +1 $this->ingelogd = true; //ingelogd } ?>
B3: De bezoeker komt juist toe en we proberen hem automatisch in te loggen gebruik makend van informatie die in de cookies staat opgeslagen van de bezoeker. Concreet betekent dit dat we ons script enkel moeten uitvoeren als de variabelen 'member_id' en 'pass_hash' in de cookies zijn opgeslagen. (lijn 1) Die waarden vinden we terug in de $_COOKIE superglobal. Is dit het geval, dan zullen we die informatie controleren in onze database (gedeeltelijk opbouwen van query lijn 2), via de functie controleer_paswoord(). (lijn 3) We zullen die functie straks scripten. Deze functie retourneert een boolean, true als paswoord overeenkomt met dat van het lid. Deze waarde kunnen we dus opslaan in de privé klasse variable '$this->ingelogd'. (lijn 4) (Opgelet, werk je met een forum dan moet je zelfde variabelennamen nemen als jouw forum, surf je met firefox en installeer je de web_developper plug-in dan kan je makkelijk kijken welke variabelennaam jouw forum gebruikt. De hier gebruikte zijn voor invision. Let op: het phpbb-forum werkt anders.)
<? 1. elseif(isset($_COOKIE['member_id'])&isset($_COOKIE['pass_hash'])) { 2. $sql = "id = ".(int)$_COOKIE['member_id']." AND password = '".addslashes($_COOKIE['pass_hash'])."'"; 3. $controle = $this->controleer_paswoord($sql); 4. $this->ingelogd = $controle; 5. } ?>
Let op bij lijn 2, bemerk dat we de 'member_id' waarde converteren naar een integer en het paswoord addslashen (magic_quotes ligt niet aan). Je mag nooit een input van gebruikers vertrouwen, er kan immers altijd een hacker tussen zitten. Ook data opgeslagen in cookies, zijn makkelijk te wijzigen (want cookie is txt-file). (Bijgevolg ook: weet iemand jouw member_id en pass_hash, dan kan hij die waarden in een cookie bij hem/haar zetten en in jouw account inloggen, dus je mag dus nooit jouw paswoord publiekelijk maken, maar ook niet jouw pass_hash) B4: De bezoeker logt in via het form. We vinden dus de data in $_POST superglobal terug. Met die data voeren we opnieuw de functie controleer_paswoord() uit, echter met een licht gewijzigde $sql, nu beschikken we over member_name (in B3 member_id) en pass (in B3 pass_hash).
<? if(isset($_POST['member_name'])&isset($_POST['pass'])&!$this->ingelogd) { //geen elseif, wordt dan sobieso uitgevoerd, ook als je last hebt met verkeerde info in cookies en via form wilt inloggen $sql = "name = '".addslashes($_POST['member_name'])."' AND password = '".md5(addslashes($_POST['pass']))."'"; $controle = $this->controleer_paswoord($sql); $this->ingelogd = $controle;
Als de gebruiker succesvol is ingelogd (if-statement), (boolean wordt weer doorgegeven aan $this->ingelogd, dan zetten we cookies zodat de bezoeker de volgende keer automatisch zal ingelogd zijn.
<? if($this->ingelogd)) { setcookie ("member_id",$_SESSION['member_id'],time() + 3600*24*30,'/','jouw_domein.be'); setcookie ("pass_hash",md5($_POST['pass']),time() + 3600*24*30,'/','jouw_domein.be'); } } ?>
setcookie(variablenaam,tijd,pad,domein); - tijd: wanneer de cookie automatisch wordt gedelete bij de bezoeker, hier: time() nu dus + 3600 * 24 *0 aantal seconden, doe de bewerking maar eens na: 1 uur * 24 * 30 = 30 dagen. - pad en domein: let goed op bij jouw_domein.be, heb je een forum, die bijvoorbeeld cookies zet met domein forum.jouw_domein.be, dan krijg je comptabiliteitsproblemen. Hoewel pad en domein optioneel zijn, zet je ze beter standaard in de syntax, zo vermijd je problemen! C: We hebben de 4 mogelijkheden afgewerkt, we zijn klaar met deze functie.
<? } //end function ?>
8. De klassefunctie controleer_paswoord.
We connecten met de database (hier via extern script). We bouwen de volledige query op, waarvan we dus al de helft gemaakt hebben en doorgegeven aan deze functie. En sturen de query door naar onze database.
<? function controleer_paswoord($sql2) { require('db_connect.php'); //script voor databaseconnect ( mysql_connect en mysql_select_db) $sql = "SELECT name,id FROM members WHERE ".$sql2; //nu volledige query $result = mysql_query($sql) or die (mysql_error()); ?>
Als de query een resultaat opleverde, dan heeft de bezoeker bewezen dat hij is wie hij beweerd te zijn, dus steken we zijn gegevens in de sessie. Die sessie gegevens kunnen we dan overal oproepen (zonder deze klasse nog nodig te hebben). We mogen ook de retourneerwaarde niet vergeten, die onze login-functie verwacht.
<? if($row = mysql_fetch_object($result)) { $_SESSION['count']=1; $_SESSION['member_id']=$row->id; $_SESSION['member_name']=$row->name; return true; //ingelogd } else return false; //niet ingelogd }//end function ?>
Hier zou je nog wat code kunnen aanbreien indien de bezoeker verkeerd naam/paswoord heeft opgegeven. Je kan het else geval makkelijk uitbreiden en de fout opslaan in $this->foutcode, daarna kan je er een gepaste output aan koppelen in de get_output()-functie.
9. De klassefunctie loguit.
We moeten nog de loguit-functie maken, wees gerust, het is een makkie Gewoon, de sessie variablen vernietigen (lijn 1) (eventueel ook de sessie zelf: lijn 2), daarna de cookies wissen, door voor houdbaarheidsdatum een negative tijd te kiezen. (lijn 3+4) En ook niet vergeten de privé klasse variable aan te passen. (lijn 5)
<? function loguit() { 1. session_unset(); 2. session_destroy(); 3. setcookie ("member_id", "", time() - 3600,"/", "jouw_domein.be"); 4. setcookie ("pass_hash", "", time() - 3600,"/", "jouw_domein.be"); 5. $this->ingelogd = false; } //end function ?>
10. Het script implementeren in de website.
Nu de klasse af is kunnen we het implementeren in onze website. We moeten dus op alle pagina's van onze website deze klasse includen. Temeer moeten we de klasse aanroepen, met $bezoeker = new login; Dan kunnen we gelijk waar in onze scripts de functie $bezoeker->is_ingelogd() gebruiken. Of de forms echoën door $bezoeker->get_output(); aan te roepen. Bovendien, als de functie $bezoeker->is_ingelogd() een true retourneerde, dan kunnen we de sessie variabelen $_SESSION['member_id'], $_SESSION['member_name'] en $_SESSION['count'] raadplegen. We kunnen eveneens overal nieuwe sessie variablen aanmaken om gegevens op te slaan. Dit is dus geen plug-and-play script, het is een walkthrough-tutorial. Het script is nog vatbaar voor verbetering, zowel wat beveiliging als gebruikersinfo betreft. Het is een startpunt en moet aan de persoonlijke wensen aangepast worden.
$bezoeker = new login;
$bezoeker->is_ingelogd()
$bezoeker->get_output();
$_SESSION['member_id']
$_SESSION['member_name']
$_SESSION['count']
« Ga terug