Invata PHP cu punctsivirgula.ro

I. Introducere
II. Notiuni de baza
III. Notiuni avansate
IV. Aplicatii
V. Extra
C O N T I N U T
Formular de login

Formular complet de login in PHP    0 ! 

In lectia Exemple de formulare a fost prezentat un formular de login simplificat, ce nu poate fi folosit intr-o aplicatie web reala. Scopul sau a fost de a prezenta pasii executati de interpretorul PHP in cadrul procesului de login. In cele ce urmeaza va fi prezentat un formular complet de login, cu toate operatiile necesare pentru a asigura o securitate sporita.

Acest exemplu este unul complex (nivelul de complexitate este mediu-avansat) si reuneste mai multe notiuni prezentate pe site. Daca nu stapaniti bine urmatoarele lectii, este indicat sa le recititi pentru a va asigura ca intelegeti codul prezentat.


Mecanismul de functionare

Procedeul de login este acelasi cu cel prezentat in lectia Exemple de formulare; difera doar operatiile adiacente efectuate dupa login si modul de marcare a utilizatorului ca autentificat.

Astfel, avem un formular simplu ce permite utilizatorilor transmiterea datelor de autentificare si avem o secventa de cod PHP care verifica datele trimise. Lucrurile se complica atunci cand marcam utilizatorul ca autentificat si cand verificam accesul, intrucat acest formular permite si pastrarea utilizatorului logat pentru mai multe zile. In plus, se incearca si respectarea unor norme minime de securitate.

Codul este impartit in mai multe fisiere, prezentate mai jos. Pentru moment, numele de utilizator si parola sunt aceleasi ca in exemplele precedente. Intrucat scopul acestui exemplu este de a prezenta mecanismul de functionare a unui formular de login, nu se va insista asupra modului efectiv de verificare a numelui si a parolei. De aceea, si in acest caz numele de utilizator si parola sunt verificate prin comparatie directa cu valori specificate direct in cod. Ramane ca exercitiu pentru cititori implementarea unei metode de verificare a datelor prin consultarea unei baze de date.


Formularul de login (login.php)

Asa cum am mentionat, mecanismul general de functionare este acelasi ca la orice formular simplu:

  • daca datele au fost trimise (daca exista $_POST['...']), se continua cu executarea codului PHP (din primul if), altfel se afiseaza codul HTML (formularul) de la sfarsitul fisierului;
  • se fac validari (in cazul acesta concret trebuie doar ca numele si parola sa fie nenule); daca sunt probleme cu datele trimise se seteaza un mesaj de eroare si se afiseaza codul HTML;
  • altfel, daca nu sunt probleme cu datele, se cauta un utilizator cu acel nume si acea parola (checkUserPass);
  • daca utilizatorul nu a putut fi gasit, se afiseaza codul HTML cu un mesaj de eroare; in caz contrar se marcheaza utilizatorul ca fiind autentificat (markLoggedIn).

Pe langa acest flux se mai fac alte cateva verificari specifice unei aplicatii de login: se verifica daca utilizatorul este deja autentificat, se verifica daca utilizatorul a solicitat sa fie deconectat (logout), si altele. Intrucat acestea nu prezinta interes, nu vor fi discutate, dar le puteti vedea, alaturi de comentariile aferente, in fisierele sursa ale exemplului (vedeti mai jos).

<?php
/* ... */
 
// verific daca a fost facut submit
if( isset($_POST['trimite']) ) {
 
	// validez datele
	if( empty($_POST['user']) || empty($_POST['pass']) ) {
		// setez un mesaj de eroare
		$error = getError(ERR_INVALID_DATA);
 
	} else {
		// caut user-ul si verific parola
		if( checkUserPass($_POST['user'], $_POST['pass']) == null ) {
			// setez un mesaj de eroare
			$error = getError(ERR_LOGIN_FAILED);
		} else {
			// daca s-a ajuns aici inseamna ca se poate loga
			markLoggedIn();
		}
	}
}
 
/* ... */
?>
 
<html>
<body>
 
<?php
if(!empty($error)) {
	echo '<p class="error">', $error, '</p>';
}
?>
 
<form action="" method="post" style="width: 30%">
<fieldset>
	<legend>Date de autentificare</legend>
	<input type="text" name="user" /> User<br />
	<input type="password" name="pass" /> Pass<br />
	<input type="checkbox" name="keep" id="kp" value="1" /> 
	<label for="kp">Pastreaza-ma logat</label><br />
</fieldset>
 
<fieldset>
	<legend>Actiuni</legend>
	<input type="submit" value="Login" name="trimite" value="1" />
	<input type="reset" value="Curata formular" /> 
</fieldset>
 
</form>
 
</body>
</html>

Portiunea de cod cea mai importanta din acest proces este apelul functiei markLoggedIn care se executa dupa ce utilizatorul este validat.


Functia markLoggedIn (functii.php)

Aceasta functie este esentiala intrucat ea salveaza pe sesiune informatii despre utilizator: user-ul, IP-ul, timpul la care s-a logat, ultima accesare a unei pagini, etc. Folosind mecanismul sesiunilor oferit de PHP, datele raman persistente si disponibile altor pagini, astfel ca se poate verifica la orice accesare daca utilizatorul este autentificat sau nu.

Mai mult decat atat, este posibila si salvarea unui cookie care sa permita autentificarea automata chiar si dupa incheierea unei sesiuni PHP (dupa inchiderea browser-ului). Cookie-ul este salvat doar daca utilizatorul solicita acest lucru.

De mentionat este ca se poate spune in orice moment cum s-a logat un utilizator, in functie de valoarea elementului 'via' ($_SESSION['LOGIN']['via']). Initial acesta are valoarea 'form', intrucat prima logare va fi prin intermediul formularului, dar la autentificarea automata prin cookie acesta va avea o alta valoare. De ce este util acest lucru? In functie de specificul aplicatiei, se pot implementa masuri de siguranta aditionale, cum ar fi solicitarea utilizatorilor logati prin cookie sa introduca parola atunci cand fac operatiuni senzitive.

Corpul functiei este prezentat mai jos.

# definesc functia care marcheaza utilizatorul ca autentificat
function markLoggedIn() {
	$username = $_POST['user']; // user-ul din formular
	$keep = $_POST['keep']; // checkbox-ul din formular
	$ip = $_SERVER[ 'REMOTE_ADDR' ]; // ip-ul vizitatorului
	$via = 'form'; // s-a logat prin formular, nu prin cookie
 
	// creez o structura de date
	$data = array();
	$data[ 'loggedIn' ] = true;
	$data[ 'username' ] = $username;
	$data[ 'loginDate' ] = time();
	$data[ 'lastAccess' ] = time();
	$data[ 'keepLoggedIn' ] = $keep;
	$data[ 'ip' ] = $ip;
	$data[ 'via' ] = $via;
 
	// pastrez in sesiune
	$_SESSION[ 'LOGIN' ] = $data;
 
	// daca trebuie sa tin minte loginul, creez un cookie
	if( $keep == 1 ) {
		/* setez un cookie ce contine structura creata mai sus si care
		 * va expira in 30 de zile; structura de date este serializata
		 * adica transformata intr-un format ce poate fi stocat ca text
		 * dupa serializare, textul returnat este encodat cu algoritmul
		 * base64 la care se adauga caracterul '1' pentru a ingreuna
		 * decodificarea continutului */
		setcookie( 'logindata', '1' . base64_encode( serialize( $data ) ), 
				time() + 2592000, '/' );
 
	} else {
		// sterg cookie-ul prin setarea valabilitatii la o data din trecut
		setcookie( 'logindata', "", time() - 36000, '/' );
	}
 
	// acum ca am salvat datele pe sesiune (si posibil in cookies), redirectionez
	header('Location: index.php');
 
	// opresc executia scriptului curent
	exit;
}

Se observa ca, odata ce datele au fost puse pe sesiune, se face o redirectionare catre o alta pagina. Acest lucru este lesne de inteles - logarea tocmai s-a realizat cu succes, deci nu are nici un rost sa mai fie afisat formularul de login. Redirectionarea se face prin transmiterea unui header si oprirea executiei scriptului PHP.


Pagina protejata (index.php)

Fisierul index.php, cel care este afisat imediat dupa login, ca de altfel orice alta pagina ce poate fi accesata doar dupa autentificare, trebuie sa verifice starea utilizatorului. Astfel, o pagina protejata va verifica daca utilizatorul este autentificat, inainte de a afisa orice informatie. In exemplul nostru, verificarea se face apeland functia checkLogin, ca mai jos.

<?php
# aceasta este o pagina protejata, deci inainte sa afisez orice, vom 
# verifica daca vizita provine de la un utilizator logat
checkLogin();
?>
 
<!-- continutul paginii trebuie pus dupa verificare -->

Functia checkLogin (functii.php)

Aceasta functie verifica daca utilizatorul care acceseaza pagina este deja autentificat sau se poate loga automat prin intermediul cookie-urilor. Verificarea se face cu ajutorul unei functii utilitare, getAuthCode, iar in cazul in care rezultatul este negativ se va redirectiona catre pagina de login. In acest mod, utilizatorii neautentificati nu vor putea accesa o pagina protejata.

Functia getAuthCode este cea care face verificarea propriu-zisa. Aceasta cauta mai intai date pe sesiune pentru a vedea daca utilizatorul este deja logat, apoi verifica daca exista un cookie valid ce poate permite autentificarea. Codul complet este prezentat mai jos.

# definesc o functie ajutatoare care face redirect la pagina de login
function checkLogin() {
	// daca nu e logat il redirectionez la pagina de login
	if(getAuthCode() != 0) {
		// nu este autentificat, fac redirect specificand codul erorii
		header('Location: login.php?error=' . ERR_MUST_LOGIN);
 
		// opresc executia scriptului curent
		exit;
	}
}
 
 
# definesc functia care verifica daca utilizatorul este autentificat
function getAuthCode() {
	$error = 0;
 
	// mai intai verific daca in sesiunea curenta utilizatorul e logat
	if( isset($_SESSION[ 'LOGIN' ]) ) {
		$data = $_SESSION[ 'LOGIN' ];
 
		// exista date pe sesiune, le verific
		if( empty($data) || empty($data['loggedIn']) ) {
			// datele sunt goale
			$error = ERR_MUST_LOGIN;
 
		} else {
			// verific ip-ul
			if( $data['ip'] != $_SERVER[ 'REMOTE_ADDR' ] ) {
				// ip-ul e diferit, probabil e un atac
				// in orice caz, marchez ca nelogat
				$error = ERR_MUST_LOGIN;
 
			} else {
				// daca nu este marcat ca 'pastreaza-ma logat' verific timpul
				// ultimului acces si il deloghez daca a fost inactiv prea mult
				$elapsed = time() - $data['lastAccess'];
 
				if( empty($data['keepLoggedIn']) && $elapsed > LOGIN_TIMEOUT ) {
					// il deloghez cu un mesaj de eroare
					markLoggedOut(ERR_TIMEOUT);
				}
 
				// daca am ajuns aici inseamna ca este logat
				$error = 0;
 
			}
		}
 
	} else if( isset($_COOKIE['logindata']) ) {
 
		// nu exista date pe sesiune, dar exista un cookie
		// din textul cookie-ului elimin primul caracter de control
		// apoi decodific folosind algoritmul base64
		$formaSerializata = base64_decode(substr($_COOKIE['logindata'], 1));
 
		if( $formaSerializata === false ) {
			// continutul cookie-ului e invalid
			$error = ERR_MUST_LOGIN;
 
		} else {
			// reconstruiesc structura de date plecand de la forma serializata
			$data = unserialize( $formaSerializata );
 
			if( empty($data) || !is_array($data)) {
				// datele sunt invalide
				$error = ERR_MUST_LOGIN;
 
			} else if( $data['ip'] != $_SERVER[ 'REMOTE_ADDR' ] ) {
				// ip-ul e diferit, probabil e un atac
				// in orice caz, marchez ca nelogat
				$error = ERR_MUST_LOGIN;
 
			} else {
				// daca am ajuns aici inseamna ca este logat
				$error = 0;
 
				// marchez ca este logat prin cookie
				$data['via'] = 'cookie';
 
				// pun datele pe sesiune
				$_SESSION[ 'LOGIN' ] = $data;
			}
		}
 
	} else {
		// nu sunt logat, marchez sesiunea ca fiind not-logged-in
		$_SESSION[ 'LOGIN' ] = array('loggedIn' => false);
		$error = ERR_MUST_LOGIN;
	}
 
	// daca este logat, actualizez data ultimei accesari
	if( $error == 0 ) {
		$_SESSION[ 'LOGIN' ]['lastAccess'] = time();
	}
 
	return $error;
}

Este foarte important ca toate paginile protejate sa apeleze functia checkLogin (si, implicit, getAuthCode), atat penru a proteja continutul de utilizatori neautentificati, cat si pentru a actualiza componenta 'lastAccess' salvata pe sesiune. Atunci cand un utilizator logat nu solicita sa ramana autentificat (deci nu se salveaza cookie-ul), la fiecare noua accesare a unei pagini, se verifica daca intervalul de timp dintre ultimele doua accesari a depasit o limita stabilita, caz in care este deconectat pentru inactivitate. Trebuie, asadar, ca ora ultimei accesari sa fie actualizata de-a lungul navigarii utilizatorului, iar acest lucru se face in functia getAuthCode.

Functia markLoggedOut este care care face deconectarea; concret, sterge toate datele de pe sesiune si cookie-ul de login (continutul functiei se gaseste in fisierul functii.php disponibil pentru download mai jos).


Testarea formularului de login

Aplicatia este disponibila pentru testare aici (click pentru a accesa). Datele corecte de login sunt aceleasi ca la celelalte exemple de pe site: admin / ghiceste-Ma.

Daca doriti sa incercati fisierele pe serverul vostru local, le puteti descarca de mai jos.

Descarcati o arhiva cu toate fisierele

Nimic de afisat.


Adauga un comentariu la aceasta sectiune.