Uvod
Da bismo na što bolji način zaokružili uvodnu priču o 'osnovnim tehnikalijama u programskom jeziku PHP', o kojima smo pisali u prethodnim člancima, i takođe (što je najvažnije) - da bismo isprobali kako sve funkcionišu u praksi - kreiraćemo formular za prijavu korisnika:

Sistem za prijavu tipično funkcioniše kroz interakciju tri PHP skripte:
prijava.php
- stranica preko koje se unose podaci za prijavuobrada.php
- skripta preko koje se uneti podaci proveravaju u odnosu na podatke iz bazepovezivanje.php
- pomoćna skripta za povezivanje sa bazom
Shodno tome da li je korisnik uneo korektne podatke (ili nije), sistem će u nastavku:
- dozvoliti prijavu korisnika - ukoliko su podaci u redu (ili)
- onemogućiti dalji pristup - ukoliko nisu uneti odgovarajući podaci
Krenimo redom ....
Šta projekat obuhvata?
Kreiranje sistema za prijavu korisnika je, sa jedne strane, prilično jednostavan zadatak, ali - sa druge strane - postoje stvari o kojima se uvek mora voditi računa (pogotovo u fazi upoznavanja sa back-end programiranjem).
U pitanju je pre svega sigurnost sajta, koja može biti ugrožena na nekoliko načina:
- korisnici (namerno ili slučajno), mogu uneti podatke koji su u stanju da ugroze sajt (pa stoga moramo biti pažljivi pri obradi podataka koje su korisnici uneli)
- sa naše strane (kao back-end programeri), moramo biti korektni prema korisnicima, odnosno, moramo se potruditi da zaštitimo podatke koje korisnici predaju na obradu, što se posebno odnosi na korisničke lozinke (koje nikako ne treba čuvati u izvornom obliku!)
Navedenim temama posvetićemo posebnu pažnju (usput ćemo se naravno osvrtati i na druge tehnike koje smo ranije pominjali), a sam projekat biće podeljen na sledeće celine:
- kreiranje forme za prijavu
- objašnjenje postupka za pravilno skladištenje lozinki
- kreiranje i popunjavanje baze podataka sa korisničkim podacima (uz vođenje računa o pravilnom skladištenju lozinki)
- obrada korisničkih podataka (tokom prijave korisnika)
- sprečavanje unošenja nedozvoljenog SQL koda (SQL injection), preko polja za upis
- prosleđivanje informacije o neuspešnom pokušaju prijave (stranici za prijavu), preko superglobalne promenljive
$_SESSION
Projekat je ("zvanično") vezan za izradu stranice za prijavu korisnika, ali, osvrnućemo se i na dodatne tehnike koje se koriste pri "prečišćavanju" primljenih podataka na formama za registraciju, što će čitaocima dati sasvim dovoljno informacija da (posle izrade formulara koji opisujemo u članku), samostalno izrade formular za registraciju.
Za sam početak, obavićemo najjednostavniji zadatak - kreiraćemo formu za prijavu.
Forma za prijavu
U prigodno imenovanom folderu koji će sadržati sve skripte koje su vezane za projekat kojim se bavimo, kreirajte prvo datoteku sa nazivom prijava.php
(u pitanju je glavna stranica sa formularom za prijavu), i unesite u datoteku sledeći kod:
<?php
session_start();
?>
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'/>
<title>PHP - login</title>
<link rel='stylesheet' type='text/css' href='stil.css'/>
</head>
<body>
<main id='wrapper'>
<form id='forma_prijava' method='post' action='obrada.php'>
<input
type = 'text'
placeholder = 'Korisnicko ime'
name = 'korisnicko_ime'
/>
<input
type = 'password'
placeholder = 'Lozinka'
name='lozinka'
/>
<input
type = 'submit'
value = 'PRIJAVA'
/>
</form>
</main>
</body>
</html>
Datoteka prijava.php
povezana je sa CSS datotekom stil.css
, koja će imati sledeći sadržaj (vezu smo prethodno uspostavili preko taga <link>
):
* {
margin: 0;
padding: 0;
font: inherit;
}
::placeholder {
color: #777;
}
#wrapper {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
#forma_prijava {
display: flex;
flex-direction: column;
font-family: OpenSans-Regular, Arial, sans-serif;
font-size: 14pt;
}
#forma_prijava input {
margin: 0 0 5px 0;
padding: 2px 5px;
//border: solid 1px #bbb;
}
#forma_prijava input[type='submit'] {
margin: 0;
padding: 8px;
background: #2e7fff;
color: white;
border: none;
}
.okvir_crni {
border: solid 1px #bbb;
}
.okvir_crveni {
border: solid 2px #e33;
}
#ispis_gresaka {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
font-family: OpenSans-Regular, Arial, sans-serif;
font-size: 14pt;
margin: 12px 0 0 0;
}
Pošto smo uspostavili mehanizam za prosleđivanje podataka, spremni smo da se pozabavimo i obradom podataka koje je korisnik prosledio pri pokušaju prijave.
Međutim, pre nego što uopšte pokušamo da korisniku odobrimo pristup, moramo primetiti sledeće:
- sistem za prijavu podrazumeva pristup bazi podataka sa korisničkim podacima (međutim - baza još uvek ne postoji)
- unos lozinki u izvornom obliku predstavlja veliki sigurnosni rizik
Prvo ćemo se pozabaviti tematikom pravilnog skladištenja lozinki u izmenjenom obliku (a "usput" ćemo diskutovati i o obradi drugih podataka koji se unose u sistem preko formulara).
Obrada podataka iz formulara i skladištenje lozinki u bazama podataka
Zapisivanje lozinki u bazama podataka, u doslovnom obliku u kome ih korisnici unose, predstavlja veliki sigurnosni rizik!
Ukoliko se desi da neko neovlašćeno pristupi bazi podataka sa korisničkim podacima, u kojoj se lozinke skladište u izvornom obliku (prosto rečeno, ako neko "hakuje" bazu i preuzme sadržaj tabela), imaće direktan uvid u podatke za prijavu većeg broja korisnika i biće u stanju da se prijavi na bilo koji od naloga, bez imalo dodatnog truda, čime se potencijalno može izazvati velika šteta.

Da bismo sprečili (ili makar, da bismo - koliko god je moguće - otežali), neovlašćeni pristup podacima, lozinke se ne skladište neposredno, već u kriptovanom ("haširanom") obliku koji nastaje upotrebom posebnih funkcija (koje će biti tema narednih poglavlja).

U pitanju je sigurnosni mehanizam koji, bilo koju unetu nisku znakova, svodi na nisku od 60 naizgled nasumičnih znakova (koja odgovara unetoj lozinki), čime smo postigli - "ako ništa drugo" - da lica koja su neovlašćeno pristupila podacima iz baze, ne mogu podatke za prijavu da (is)koriste neposredno.
Način kreiranja kriptovanog oblika koji smo videli, nije ni izdaleka komplikovan, ali, to (nažalost) nije jedino o čemu moramo voditi računa.
Pre nego što kriptujemo lozinku, moramo biti sigurni da sam podatak ne sadrži specijalne znakove koji se mogu umetnuti preko HTML formulara, promeniti tok izvršavanja skripte i dovesti do gubitka podataka (ili našteti sistemu i korisnicima na drugi način).
Naime, budući da se lozinke, kao i ostali podaci, unose preko HTML formulara (formulara za registraciju / prijavu / pretragu / online kupovinu / objavu komentara i slično) - pri čemu su formulari tipično dostupni javno - nije teško zaključiti da (u većini situacija), na sajtovima, "bilo ko" može da napiše "bilo šta".
Takav pristup otvara mogućnosti zloupotrebe, i stoga je zadatak back-end programera - da preduprede pokušaje unošenja neovlašćenog SQL ili HTML koda u sistem za obradu podataka (preko polja za unos podataka).
"Sanitacija" korisničkih podataka - sprečavanje ugrađivanja nedozvoljenog SQL i HTML koda (SQL i HTML injection)
Postoji pravilo koje navodi da podacima koje korisnici unose preko formulara, "ne treba verovati", i to pravilo (praksa je pokazala) - treba uvek poštovati!
Bez preduzimanja posebnih koraka, sistem za obradu korisničkih zahteva (kome se prosleđuju podaci uneti preko korisničkih formulara), znake kao što su '
(apostrof) i "
(navodnici), ne prepoznaje kao podatke iz upita, već kao deo sopstvenog SQL koda, što (kao što smo već naveli), predstavlja poveći problem i otvara mogućnost zloupotrebe.
Uzmimo da se (primera radi), podaci uneti preko <input>
polja (iz HTML forme), prosleđuju sledećem upitu:
<?php
// Podaci prosleđeni sa forme:
$korisnicko_ime = $_POST['korisnicko_ime'];
$lozinka = $_POST['lozinka'];
// Upit za pristup podacima
$upit = "SELECT * FROM korisnici WHERE korisnicko_ime='$korisnicko_ime' AND lozinka='$lozinka'";
// U nastavku, proveravamo da li je upit vratio jedan red,
// što znači da su uneti podaci korektni
?>
U normalnim okolnostima, prikazani upit izvršava se na sledeći način:
<?php
// Ako su prosleđeni sledeći podaci:
// $korisnicko_ime = "pera94";
// $lozinka = "ParadaYz14";
// .... upit za pristup podacima praktično postaje:
$upit = "SELECT * FROM korisnici WHERE korisnicko_ime='pera94'
AND lozinka='ParadaYz14'";
// .... i to je (prosto rečeno) -
// "ono što treba da se desi" ....
?>
Pod uslovom da su uneti podaci u redu (smatraćemo da jesu), sistem u nastavku dozvoljava prijavu.
Međutim, ukoliko neko prosledi sledeće podatke:
<?php
$korisnicko_ime = "pera94' OR 1=1;--";
$lozinka = "bilo šta";
?>
.... upit više nije ni najmanje bezopasan:
SELECT * FROM korisnici WHERE korisnicko_ime='pera94' OR 1=1; --' AND lozinka='ParadaYz14'";
Budući da je sistem "nekritički" prihvatio znak '
(apostrof), dotični znak je zatvorio nisku 'pera94'
i omogućio da se između korisničkog imena i "pravog" apostrofa koji je unet kao deo upita u PHP skripti, umetne proizvoljan SQL kod:
- umetnuti kod je prepravio smisao upita - "dozvolio je prijavu" preko bilo kog postojećeg korisničkog imena (u gornjem primeru,
pera94
) - takođe je poništen ostatak upita (
AND lozinka='$lozinka'
), prostim stavljanjem ostatka niske pod komentar (u različitim implementacijama SQL-a, komentar tipično započinje niskom sa dve crtice--
)
Srećom, ovakvi pokušaji 'obijanja' mogu se preduprediti prilično lako.
Dovoljno je da se unete niske 'provuku' kroz dve specijalizovane funkcije za "sanitaciju":
mysqli_real_escape_string
je funkcija koja ukida funkcionalnost znakova koji su deo SQL kodahtmlentities
je funkcija koja ukida funkcionalnost znakova koji su deo HTML koda
Preko sledećeg koda ....
<?php
// Na formi za registraciju,
// korisnik je uneo lozinku
// "ApstraKcij4~#"
// Podatke sa forme za registraciju,
// primićemo preko superglobalne
// promenljive $_POST, ....
$lozinka = $_POST['registracija_lozinka'];
// .... ali, pre nego što budemo radili bilo
// šta sa preuzetim podacima, "provući" ćemo
// ih kroz funkcije za "sanitaciju" podataka
// (popularan naziv za procedure preko kojih
// se u unetim niskama ukida funkcionalnost
// specijalnih znakova, čime se sprečava
// prosleđivanje nedozvoljenih delova koda
$lozinka = mysqli_real_escape_string($lozinka);
$lozinka = htmlentities($lozinka);
// (Tek) sada može se koristiti uneti podatak
?>
.... postiže se da uneti podatak bude tretiran kao obična niska (gornja skripta, zarad preglednosti, sadrži samo obradu vezanu za lozinku, ali, prikazani proces sanitacije potrebno je svakako obaviti i za sve ostale unete podatke).
Najprostije rečeno, od niski ....
pera94' OR 1=1;--
<a href='#'>ubačeni link</a>
.... obradom preko funkcije mysqli_real_escape_string
(za prvu nisku) i htmlentities
(za drugu), nastaju niske:
pera94\' OR 1=1\;--
<a href='#'>ubačeni link</a>
Budući da prva niska sada sadrži odgovarajuće escape sekvence za specijalne znakove (koje sugerišu SQL-u da znakove koji inače imaju specijalno značenje, treba tretirati kao obične znakove), dok su u drugoj pojave HTML znakova zamenjene odgovarajućim HTML entitetima (što neće pokrenuti kreiranje HTML/DOM objekata), može se smatrati je bezbedno koristiti obrađene niske u daljoj obradi.
Međutim, napomenućemo da ne treba smatrati da je svako unošenje apostrofa u HTML polja "pokušaj hakovanja". Recimo, sledeće niske ....
Shaquille O'Neal
Guns N' Roses
Livin’ on a Prayer
.... predstavljaju poznata imena koja se svakodnevno "provuku" kroz internet pretraživače više desetina hiljada puta.
Upravo je zato naša obaveza da, preko funkcija koje smo naveli, svedemo apostrofe na escape sekvence \'
(što će SQL prepoznati kao običan znak koji je deo unetog podatka, a ne, specijalni znak u jeziku SQL) - čime smo praktično omogućili "legitimne" vidove upotrebe apostrofa na formularima.
Provera unetog podatka u odnosu na očekivani obrazac (preg_match)
Prethodno navedeni postupak obavezno se primenjuje na svaki uneti podatak: i tokom registracije, i tokom prijave korisnika, ali, pri registraciji ćemo pažnju posvetiti i tome da podaci koje korisnik prosleđuje, obavezno budu uneti u odgovarajućem formatu (dok, u formularima za prijavu, tipično možemo pustiti korisnike da "pišu šta god žele").
U "idejnom smislu", ne želimo da dopustimo korisnicima da pišu "bilo šta", ali (sa jedne strane), praktično ne možemo "zabraniti" korisnicima da u polja za unos, unose proizvoljne podatke, a, sa druge strane, podaci svakako prolaze proces "sanitacije".
Takođe, pri obavljanju prijave, nije bitno da li se neki od unetih podataka poklapa sa predviđenim formatom, * već samo, da li uneti podaci odgovaraju konkretnim podacima iz baze podataka, ** i stoga bi proces provere formata podataka koji su prosleđeni iz forme - samo nepotrebno opteretio skriptu za obradu unetih podataka (u meri koja, ni izdaleka nije "zabrinjavajuća", ali, svejedno težimo tome da programski kod uvek bude optimalan).
Preko regularnih izraza (koji se koriste na formama za registraciju), nije teško proveriti da li podaci poštuju određene standarde:
- uneta imena i prezimena mogu sadržati samo znakove ćirilice i latinice, razmake i crtice
- korisnička imena moraju imati odgovarajuću (minimalnu) dužinu i biti sačinjena iz odgovarajućih znakova
- dužina lozinki mora biti minimalno osam znakova (tipična vrednost), i takođe, lozinke moraju sadržati sve kategorije znakova (velika i mala slova, cifre, specijalne znakove) *
- brojevi telefona mogu sadržati samo cifre, kose i ravne crte i biti formatirani na propisan način
- e-mail adrese takođe moraju imati pravilan format ("korisničko_ime@domen", bez nedozvoljenih znakova)
Pogledajmo neke od regularnih izraza (sa kojima ste se, u najvećoj meri, * već upoznali u članku o regularnim izrazima), koji odgovaraju prethodno nabrojanim zahtevima:
// Ime i prezime:
[\p{Letter}\p{Mark} ]+
// E-mail adresa:
[a-z\._]{2,}@([a-z]{2,}\.)+([a-z]{2,}){1}
// Broj telefona:
0[0-9]{2}[/-][0-9]{3,4}-?[0-9]{3,4}
Za proveru u PHP-u, koristi se funkcija preg_match
(pattern/regular expression match - funkcija koja vraća true
, ako se uneta niska poklapa sa navedenim obrascem, ili false
, ako se niska ne poklapa sa obrascem), a za primer ćemo uzeti proveru unete e-mail adrese.
Posle provere unetog podatka preko funkcija mysqli_real_escape_string
i htmlentities
, proverićemo da li je e-mail adresa formatirana pravilno.
Funkciji pregmatch
može se proslediti prethodno navedeni regularni izraz kao argument, ali, tipično se u PHP-u zadatak provere e-mail adrese obavlja uz korišćenje još jedne funkcije:
- funkcija
preg_match
proverava (samo) da li niska sadrži nedozvoljene znake - funkcija
filter_var
proverava da li se niska podudara sa određenim formatom
Sledeći kod ....
<?
if (!preg_match("/^[A-Za-z0-9@._]*$/", $forma_email) ||
!filter_var($forma_email, FILTER_VALIDATE_EMAIL))
{
// Dalji koraci u obradi greške: kreiranje
// odgovarajućeg indeksa u superglobalnoj
// promenljivoj $_SESSION, zarad prosleđivanja
// podataka stranici za prijavu/registraciju,
// i slično ....
}
?>
.... obaviće zadatak provere e-mail adrese.
Na ovom mestu, možete se zapitati (reklo bi se, sasvim opravdano), zašto je uopšte potrebno koristiti funkciju preg_match
, budući da funkcija filter_var
proverava da li se niska uklapa u obrazac za formatiranje e-mail adresa?!
Odgovor je jednostavan, ali istovremeno .... pomalo "čudan".
Funkcija filter_var
proverava da li se uneta niska poklapa sa obrascima za formatiranje email adresa, ali, pri tom se poštuju međunarodni standardi stari preko 25 godina, koji dopuštaju da se u email adresi nađu i znakovi kao što su #, !, ~ i slični.
Dakle, "što se tiče funkcije filter_var
", niska korisnik@domen.rs
formatirana je kao email adresa (što je tačno), ali je isto tako i niska k0r!$n1k#@d0m#n.r$
- takođe formatirana kao email adresa (što možda jeste u skladu sa prvobitnim standardima, ali, nikako nije nešto što se sreće u praksi).
U svakom slučaju, upotrebili smo sve mehanizme provere i zaštite koji se razumno mogu predvideti, i sada ("tek sada"), kada smo (konačno!) "prečistili" unete podatke, možemo ih uneti u tabelu (ali, pre nego što se posvetimo unosu podataka, osvrnućemo se prvo na tematiku kriptovanja lozinki).
Kreiranje kriptovanog oblika lozinke - password_hash()
Za razliku od ostalih podataka koji - posle "bezbedonosnih provera" - mogu odmah biti upisani u tabelu, lozinke se prvo podvrgavaju procesu enkripcije, po sledećoj proceduri:
- Korisnik, pri registraciji naloga, unosi lozinku (obavezno dvaput, zarad provere), i uneti podatak se prosleđuje skripti za kreiranje naloga.
- uneta lozinka se podvrgava procesu "sanitacije" (ukidanje funkcionalnosti specijalnih znakova)
- lozinka se hash-uje i skladišti u bazi
Sam proces kreiranja hash-iranog oblika lozinke je "tajnovit" (u pitanju je posebna funkcija, kao deo jezika PHP, čiji "unutrašnji" mehanizmi nisu poznati krajnjim korisnicima, iz sigurnosnih razloga), ali, procedura za kreiranje hash koda (krajnje jednostavna za implementaciju), je sledeća:
<?php
$lozinka = $_POST['forma_lozinka'];
$lozinka = mysqli_real_escape_string($lozinka);
$lozinka = htmlentities($lozinka);
$lozinka_hash = password_hash($lozinka, PASSWORD_DEFAULT);
?>
Ako unesemo sledeću lozinku ....
ParadaYz14
.... posredstvom funkcije password_hash
, od unete lozinke nastaje sledeći hash kod
$2y$10$zvsxp01IjHKVKzT15r5.Ee2sS97G.pCbnJqqd93C7XAY46t53ItEC
Drugi argument koji se predaje funkciji password_hash
, predstavlja izabrani metod kriptovanja: PASSWORD_DEFAULT
je standardni (podrazumevani) metod, dok po želji (najčešće samo zarad kompatibilnosti sa starijim, već postojećim bazama podataka), možemo koristiti i druge metode.
Obrađeni podatak se sada može skladišti u bazi, i stoga ćemo u sledećem poglavlju pisati upravo o unosu podataka, dok ćemo o proceduri provere unete lozinke (u odnosu na sačuvani hash kod), pisati u pretposlednjem poglavlju (u kome ćemo opisati celokupnu skriptu za prijavu korisnika).
Podaci za prijavu
Pošto smo se postarali da, podaci koje unosimo, budu "onakvi kakvi treba da budu", možemo podatke (zapravo) i upisati u tabelu.
U pitanju je postupak koji se tipično obavlja pri registraciji, ali, pošto članak na eklektičan način obrađuje obe teme: registraciju i prijavu, jednostavno ćemo (za sam početak), podatke uneti ručno (da bismo sistemu obezbedili funkcionalnost).
Kreiranje baze podataka sa korisnicima
Prema uputstvima iz članka o povezivanju PHP skripti sa MySql bazama podataka, kreirajte skriptu za povezivanje sa serverom (za početak, nemojte unositi ime baze - iz očiglednih razloga), i potom pokrenite sledeći upit za kreiranje baze podataka (još jednom da se podsetimo: potrebno je smestiti skripte u zajednički folder i dodeliti im prepoznatljiva imena):
<?php
require_once('povezivanje.php');
$upit = "CREATE DATABASE korisnici";
$rezultat = mysqli_query($veza, $upit);
if (!$rezultat) {
echo "Greška pri kreiranju baze podataka.";
exit();
}
?>
Posle kreiranja baze, možemo kreirati i popuniti tabelu sa korisnicima.
Kreiranje tabele sa korisničkim podacima
Prepravite sada skriptu povezivanje.php
, tako da se pri povezivanju koristi i naziv baze ("korisnici"), i kreirajte zatim novu skriptu, preko koje ćemo kreirati tabelu korisnici
:
<?php
require_once('povezivanje.php');
$upit = "CREATE TABLE korisnici
(
id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
korisnicko_ime varchar(255) NOT NULL,
lozinka_hash varchar(255) NOT NULL,
email varchar(255) NOT NULL
)";
$rezultat = mysqli_query($veza, $upit);
if (!$rezultat) {
echo "Greška pri kreiranju tabele.";
exit();
}
?>
Sada se tabela može popuniti korisničkim podacima.
Unos korisničkih podataka u tabelu
Podaci preuzeti iz forme za registraciju mogu se uneti u tabelu, korišćenjem odgovarajuće skripte:
<?php
require_once('povezivanje.php');
// Preuzimanje podataka iz forme;
// provera indeksa (preko funkcije isset),
// izostavljena je zarad preglednosti
// (i treba je dodati)
$korisnicko_ime = $_POST['korisnicko_ime'];
$lozinka = $_POST['lozinka'];
$email = $_POST['email'];
// Sanitacija:
$korisnicko_ime = mysqli_real_escape_string($veza, $korisnicko_ime);
$korisnicko_ime = htmlentities($veza, $korisnicko_ime);
$lozinka = mysqli_real_escape_string($veza, $lozinka);
$lozinka = htmlentities($veza, $lozinka);
$email = mysqli_real_escape_string($veza, $email);
$email = htmlentities($veza, $email);
// Kreiranje hash-irane verzije lozinke ....
$lozinka_hash = password_hash($lozinka, PASSWORD_DEFAULT);
// Upis podataka u tabelu:
$upit = "INSERT INTO korisnici
(korisnicko_ime, lozinka_hash, email)
VALUES
('$korisnicko_ime', '$lozinka_hash', '$email')";
$rezultat = mysqli_query($veza, $upit);
if (!$rezultat)
{
echo "Greška pri unosu podataka u tabelu";
exit();
}
?>
U pitanju je skripta kakvu bismo koristili za obradu podataka koji su prosleđeni preko formulara za registraciju, ali, budući da to (zvanično) nije tema ovog članka, koristićemo pojednostavljenu skriptu sa "hardkodiranim" podacima i nećemo obavljati provere (mi nećemo sami sebe "hakovati", a nemojte ni vi):
<?php
require_once('povezivanje.php');
// Smatraćemo da su iz forme forme za
// registraciju preuzeti sledeći podaci:
$korisnicko_ime = "petar_011";
$lozinka = "ApstraKcij4~#";
$email = "petar_m@proba.rs";
// Kreiraćemo hash kod unete lozinke ....
$lozinka_hash = password_hash($lozinka, PASSWORD_DEFAULT);
// .... i zatim upisati podatke u tabelu:
$upit = "INSERT INTO korisnici
(korisnicko_ime, lozinka_hash, email)
VALUES
('$korisnicko_ime', '$lozinka_hash', '$email')";
$rezultat = mysqli_query($veza, $upit);
if (!$rezultat)
{
echo "Greška pri unosu podataka u tabelu";
exit();
}
?>
Sada, kada postoji (bar jedan) korisnički nalog u tabeli sa korisnicima (bez čega mehanizam za prijavu praktično ne može da funkcioniše), možemo se posvetiti izradi skripte za prijavu (što je - ako smo usput možda i zaboravili - "zvanično" i dalje glavna tema članka). :)
Obrada podataka za prijavu
Kreirajte datoteku sa nazivom obrada.php
i unesite u datoteku sledeći kod (objašnjenja su u komentarima):
<?php
require_once('povezivanje.php');
// Prijem podataka;
// (dodajte proveru indeksa preko funkcije isset)
$korisnicko_ime = $_POST['korisnicko_ime'];
$lozinka = $_POST['lozinka'];
// Sanitacija:
$korisnicko_ime = mysqli_real_escape_string($veza, $korisnicko_ime);
$korisnicko_ime = htmlentities($korisnicko_ime);
$lozinka = mysqli_real_escape_string($veza, $lozinka);
$lozinka = htmlentities($lozinka);
// Prijava:
$upit = "SELECT * FROM korisnici WHERE
korisnicko_ime='$korisnicko_ime'";
$rezultat = mysqli_query($veza, $upit);
// Ukoliko je korisničko ime pravilno uneto, tabela
// rezultat sadržaće tačno jedan red (u suprotnom
// prekinućemo dalje izvršavanje skripte)
if (mysqli_num_rows($rezultat) != 1) {
echo "Uneto korisničko ime nije prepoznato.<br>";
echo "Vratite se nazad i pokušajte ponovo.<br>";
exit();
}
// Ukoliko je do sada bilo sve u redu,
// proverićemo i lozinku.
// Prvo ćemo pročitati prvi (i jedini)
// red tabele (ne treba nam if, jer smo
// prethodno ustanovili da postoji
// tačno jedan red u tabeli) ....
$red = mysqli_fetch_assoc($rezultat);
// .... a onda možemo proveriti i lozinku
if (!password_verify($lozinka, $red['lozinka_hash'])) {
echo "Pogrešno uneta lozinka.<br>";
echo "Vratite se nazad i pokušajte ponovo.<br>";
exit();
}
// Dakle, ako funkcija password_verify ustanovi
// da uneta lozinka ne vraća odgovarajući hash kod,
// prekinućemo sa izvršavanjem skripte, dok,
// u suprotnom, prelazimo na ....
// OSTATAK SKRIPTE
echo "Uspešno ste se prijavili!<br>";
// Prikazivanje pozdravne poruke, učitavanje
// sadržaja shodno korisničkom nalogu
// i slično ....
?>
Za kraj, pogledaćemo kako korisnika možemo obavestiti o neuspešnom pokušaju prijave, vraćanjem na stranicu za prijavu i označavanjem polja sa pogrešno unetim podacima.
Vraćanje podataka stranici za prijavu, preko superglobalne promenljive $_SESSION
Sa jedne strane, može se reći da je krajnje adekvatno da se skripta za obradu unetih podataka (u našem slučaju, skripta obrada.php
), koristi i za ispis poruka o greškama pri prijavljivanju.
Međutim, nepisana pravila modernog web dizajna nalažu da korisnike treba obaveštavati o neuspešnim pokušajima prijavljivanja - preko istog formulara preko koga su prvobitno pokušali da obave prijavu (pri čemu, zbog brzine izvršavanja, praktično deluje da se korisnik nije ni udaljio sa stranice za prijavu).
Sa ovakvim pristupom već smo se upoznali u članku o superglobalnoj promenljivoj $_SESSION, pa ovoga puta neće biti teško da ideje koje smo ranije izneli, implementiramo u okviru formulara za prijavu.
Skriptu za obradu prijave dopunićemo na sledeći način (princip po kome sve funkcioniše opisan je u prethodno navedenom članku, a detalji implementacije zapisani su u komentarima):
<?php
// Da se podsetimo, komanda session_start()
// poziva se na samom početku:
session_start();
// .... a provera indeksa preko funkcije isset,
// i ovoga puta je izostavljena zarad preglednosti
// (i treba je dodati)
require_once('povezivanje.php');
$korisnicko_ime = $_POST['korisnicko_ime'];
$lozinka = $_POST['lozinka'];
$korisnicko_ime = mysqli_real_escape_string($veza, $korisnicko_ime);
$korisnicko_ime = htmlentities($korisnicko_ime);
$korisnicko_ime = trim($korisnicko_ime);
$lozinka = mysqli_real_escape_string($veza, $lozinka);
$lozinka = htmlentities($lozinka);
$lozinka = trim($lozinka);
// Ovoga puta, proverićemo da li je korisničko ime uopšte uneto:
if (strlen($korisnicko_ime) == 0)
{
$_SESSION['greska_korisnicko_ime'] = "Unesite korisničko ime";
header("location: index.php");
exit();
}
unset($_SESSION['greska_korisnicko_ime']);
$upit = "SELECT * FROM korisnici WHERE
korisnicko_ime='$korisnicko_ime'";
$rezultat = mysqli_query($veza, $upit);
if (mysqli_num_rows($rezultat) != 1)
{
// Ovoga puta, poruku o grešci ćemo proslediti 'unazad'
// preko superglobalne promenljive $_SESSION
$_SESSION['greska_korisnicko_ime'] = "Korisničko ime nije prepoznato.";
header("location: index.php");
exit();
}
// Ako smo do ovde došli, smatraćemo da sa unosom
// korisničkog imena nije bilo grešaka, pa možemo
// ukloniti indeks 'greska_korisnicko_ime'
unset($_SESSION['greska_korisnicko_ime']);
$red = mysqli_fetch_assoc($rezultat);
if (!password_verify($lozinka, $red['lozinka_hash']))
{
// Kao i za korisničko ime, ovoga puta ćemo
// poruku o grešci proslediti unazad
// preko superglobalne promenljive $_SESSION
$_SESSION['greska_lozinka'] = "Pogrešno uneta lozinka.<br>Pokušajte ponovo.<br>";
header("location: index.php");
exit();
}
unset($_SESSION['greska_lozinka']);
echo "Uspešno ste se prijavili!<br>";
?>
Sada možemo prepraviti i formu za prijavu (zarad preglednosti nećemo prikazivati celu skriptu):
<form method='post' action='prijava.php'>
<input
<?php
if (isset($_SESSION['greska_korisnicko_ime']))
{
echo "class = 'okvir_crveni'";
}
else
{
echo "class = 'okvir_crni'";
}
?>
type = 'text'
placeholder = 'Korisničko ime'
name = 'korisnicko_ime'
/>
<input
<?php
if (isset($_SESSION['greska_lozinka']))
{
echo "class = 'okvir_crveni'";
}
else
{
echo "class = 'okvir_crni'";
}
?>
type = 'password'
placeholder = 'Lozinka'
name='lozinka'
/>
<input
type = 'submit'
value = 'PRIJAVA'
/>
</form>
// Ispod forme, dodaćemo div u kome ćemo ispisivati greške:
<div id='ispis_gresaka'>
<?php
if (isset($_SESSION['greska_korisnicko_ime'])) {
echo $_SESSION['greska_korisnicko_ime'];
}
else {
echo "";
}
if (isset($_SESSION['greska_lozinka'])) {
echo $_SESSION['greska_lozinka'];
}
else {
echo "";
}
?>
</div>
Osvrnimo se na nekoliko detalja:
- budući da su atributi
<input>
polja zapisani pedantno (jedni ispod drugih), lako je bilo zaključiti gde treba "uglaviti" blokove PHP koda - informacije o pojavi grešaka, prikazane su uključivanjem crvenih okvira na
<input>
poljima i ispisom grešaka u pomoćnom<div>
elementu (ispod forme)
Za kraj ....
Za kraj nam ostaje, da vam ponovo predložimo da isprobate tehnike koje smo opisali, kroz sledeće primere:
- probajte da (ponovo) kreirate formular za prijavu korisnika - ali ovoga puta samostalno
- probajte da samostalno kreirate formular za registraciju korisnika
Takođe, ukoliko želite da isprobate formular koji smo izrađivali u članku, možete ispratiti sledeći link (ukoliko već niste):
Formular za prijavu korisnikaPodaci za prijavu su oni koje smo koristili u članku.