Přihlašování a registrace uživatelů pomocí sessions

Thu, 28 Feb 2008 17:10:11 +1100

...snadno a jednoduše. Doufám, že tento můj článek nebude nošením dříví do lesa. K jeho napsání jsem se rozhodl po přečtení mnoha tutoriálů ohledně registrace a přihlašování uživatelů, které rozhodně nebyly z mého pohledu dostačující. Málokde je zmíněna funkce session_regenerate_id či možnost předávání SESSID pouze v cookies.

Na první záblesky lepších časů jsem narazil až na php.vrana.cz (Mimochodem doporučuji přidat do RSS čtečky), a tak jsem se vlastně dostal k napsání tohoto svého návodu. A jelikož v jednoduchosti je síla, nebudeme například potřebovat žádná zbytečná volání funkcí htmlspecialchars či nesmyslného includování souborů až po odeslání formuláře, a s pomocí pár základních pravidel nakonec snadno vytvoříme přihlašovací systém a registraci uživatelů.

Prosím berte v potaz, že článek je z roku 2008. Hodně se za tu dobu změnilo, a tak návod nemusí odpovídat dnešním standardům. Ukázka ale funguje a můžete ji využít jako první krok k lepšímu porozumnění PHP.

Ale dost řečí okolo, pojďme na to. Jako základ je samozřejmě potřeba vytvořit SQL tabulku pro uživatele.

SQL tabulka

CREATE TABLE `uzivatele` (
    `id` int(10) unsigned NOT NULL   auto_increment,
    `jmeno` varchar(255)  COLLATE utf8_czech_ci,
    `heslo` varchar(255) NOT NULL,
    `email` varchar(255)  COLLATE utf8_czech_ci,
    `prava` int(10) unsigned  DEFAULT '1',
    `aktivni` int(10) unsigned  DEFAULT '1',
    PRIMARY KEY  (`id`),
    UNIQUE (`jmeno`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci AUTO_INCREMENT=0 ;

O uživateli budeme ukládat přihlašovací jméno, heslo, registrační e-mail a jeho práva. Sloupec`aktivni`bude sloužit k pozdějšímu využití, pro implementaci možnosti schválení registrace administrátorem.

.htaccess

Ještě před napsáním prvních řádků skriptu se pustíme do nastavení základních direktiv .htaccessu pro práci se sessions. Zde zakážeme předávání SESSID v url, nastavíme ukládání pouze do cookies a nakonec jeho platnost. Stejného efektu lze docílit i funkcí ini_set, záleží jen na nastavení hostingu, co vám povolí. Nezapomeneme ani na další pojistku proti SQL injection a zapneme escapovaní uvozovek.

php_flag session.use_only_cookies 1
php_flag session.use_trans_sid 0
php_value session.cookie_lifetime 10800

php_flag magic_quotes_gpc on
php_flag magic_quotes_sybase off
php_flag magic_quotes_runtime off

První dva řádky mimo jiné zabrání i generování invalidního inputu do formulářů. Dále máme na pořadu dne konfigurační soubor. V něm nastavíme údaje pro připojení k databázi, připojíme se k ní a nastartujeme session.

Soubor config.php

<?php

error_reporting(E_ALL);

define('DBHOST', 'localhost');		// databazovy server
define('DBNAME', 'mysql');		// jmeno databaze
define('DBUSER', 'root');		// uzivatelske jmeno
define('DBPASS', '');			// heslo k databazi

mysql_connect(DBHOST, DBUSER, DBPASS) or die(mysql_error());
mysql_select_db(DBNAME) or die(mysql_error());

session_start();

?>

Nyní už si můžeme napsat registrační skript. Soubor register.php zpracuje data odeslaná z formuláře, zkontroluje a uloží do databáze. Jméno a heslo namísto projedeme navíc i regulárním výrazem, který žádné zlobivé znaky nepustí dál. Povolíme pouze alfanumerické znaky, pomlčku, podtržítko, tečku a e-mail (pokud je někdo zvyklý uvádět e-mail jako login). Kvůli potencionálním chybám v kódování zakážeme i diakritiku.

Registrace: soubor register.php

<?php

require_once('config.php');

if(isset($_POST['submit'])){
	$message = array();

	$_jmeno = trim(htmlspecialchars($_POST['jmeno']));
	$_email = trim(htmlspecialchars($_POST['email']));
	$_heslo = trim(htmlspecialchars($_POST['heslo']));

	// kontrola jmena na prazdnotu a regularnim vyrazem
	if(empty($_jmeno)){
		$message[] = 'Pole jméno je prázdné';
	}else{
		if(!preg_match("/^[_a-z0-9\.\-@]*$/", $_POST['jmeno'])){
			$message[] = 'Neplatné znaky v poli jméno.';
		}
	}

	// kontrola hesla na prazdnotu a regularnim vyrazem
	if(empty($_email)){
		$message[] = 'Pole e-mail je prázdné';
	}else{
		if(!preg_match('/^[_a-zA-Z0-9\.\-]+@[_a-zA-Z0-9\.\-]+\.[a-zA-Z]{2,4}$/', $_POST['email'])){
			$message[] = 'Zadejte platný e-mail.';
		}
	}

	// kontrola hesla
	if(empty($_heslo)){
		$message[] = 'Pole heslo je prázdné';
	}else{
		if($_POST['heslo'] != $_POST['heslo2']) {
			$message[] = 'Hesla se neshodují';
		}
	}

	// zkontrolujeme, zda-li v databazi uz nemame stejneho uzivatele
	$select = mysql_query("SELECT `jmeno` FROM `uzivatele` WHERE `jmeno`='".mysql_real_escape_string($_jmeno)."'");
	if(mysql_num_rows($select)>0) {
		$message[] = 'Zvolte jiné uživatelské jméno.';
	}

	// ulozime udaje, kdyz neni zadna chyba
	// prava i aktivni nastavime na 1
	if(empty($message)){
		$_heslo = md5($_POST['heslo']);
		$_jmeno = mysql_real_escape_string($_jmeno);
		$_email = mysql_real_escape_string($_heslo);

		mysql_query("INSERT INTO `uzivatele` VALUES('','{$_jmeno}','{$_heslo}','{$_email}',1,1)");
		header('Location: ./register.php?ok=1');
	}
}

?>

Dále už nám zbývá jen registrační formulář, HTML část souboru register.php. Nad něj vytiskneme případné chybové hlášky, uvedeme také informace o znacích, které jsou ve jménu povoleny.





Registrace uživatele



<?php if(!empty($message)){?>

		<?php foreach((array)$message as $item){?>
<?php echo $item;?>
		<?php }?>

<?php }?>

<?php if(isset($_GET['ok'])){ ?>
Registrace proběhla úspěšně
<?php }else{ ?>

Jméno a heslo se smí skládat pouze z alfanumerických znaků, pomlčky, podtržítka, tečky a zavináče.
Nepoužívejte diakritiku.

	<form action="<?php echo $_SERVER['PHP_SELF']?>" method="post">

Registrace uživatele
<input name="jmeno" size="20" tabindex="1" type="text" value="<?php echo (!empty($_POST['jmeno']) ? htmlspecialchars($_POST['jmeno']) : ''); ?>" /> Jméno *
<input name="email" size="20" tabindex="2" type="text" value="<?php echo (!empty($_POST['email']) ? htmlspecialchars($_POST['email']) : ''); ?>" /> E-mail *
Heslo *
Heslo znovu *



<?php }?>

Přihlásit




Nyní k přihlašovacímu skriptu a ověření uživatele. Pro ošetření dat z formuláře použijeme funkci mysql_real_escape_string (mysleme na SQL injection), provedeme SQL dotaz, při kladném výsledku nastavíme sessiony a přesměrujeme do administrace, při výsledku nulovém zašleme zobrazíme chybové hlášení. Pod parametrem v GETu zde také budeme provádět odhlášení.

Přihlášení: soubor login.php

<?php

require_once('config.php');

if(isset($_POST['submit'])){
	$_jmeno = mysql_real_escape_string(trim($_POST['jmeno']));
	$_heslo = md5(trim($_POST['heslo']));

	$select = mysql_query("SELECT * FROM `uzivatele` WHERE `jmeno`='{$_jmeno}' AND `heslo`='{$_heslo}'") or die(mysql_error());
	$udaje  = mysql_fetch_assoc($select);

	// pokud je zadano platne jmeno a heslo
	if(mysql_num_rows($select)==1){
		session_regenerate_id(); // osetreni session stealing
		$_SESSION['jmeno'] = $_jmeno; // nastavime sessiony
		$_SESSION['heslo'] = $_heslo;
		$_SESSION['prava'] = $udaje['prava'];
		header("Location: admin.php");
	}else{
		$error_message = 'Chybné přihlašovací údaje';
	}
}

// odhlasime se
if(isset($_GET['logout'])){
	$_SESSION['jmeno'] = '';
	$_SESSION['heslo'] = '';
	$_SESSION['prava'] = '';
	unset($_SESSION['jmeno']);
	unset($_SESSION['heslo']);
	unset($_SESSION['prava']);
}

?>

Nakonec už jen vlastní formulář k loginu a můžeme se vesele přihlašovat.

?>




Přihlášení



<?php if(isset($_GET['logout'])){ ?>
Byli jste odhlášeni ze systému.
<?php }?>

<?php if(!empty($error_message)){?>

Chybné přihlašovací údaje

<?php }?>

<form action="<?php echo $_SERVER['PHP_SELF']?>" method="post">

Přihlášení
Jméno
Heslo




Zaregistrovat




Na závěr bych ještě krátce zmínil kontrolu uživatelů přímo v administraci. Jelikož jsme paranoidní, budeme při každém obnovení stránky tahat údaje z databáze a kontrolovat je s hodnotou SESSION proměnných. Funkci samozřejmě doporučuji includovat z externího souboru a v každém skriptu ji pouze volat. Ta se postará o kontrolu, zda-li je přihlášen skutečný uživatel, pokud ne, přesměruje na přihlašovací skript s hlavičkou 401. Potom je samozřejmě nutné myslet na to, že v případném formuláři na editaci profilu se musí přenastavit session, změní-li se heslo.

Před vlastním HTML výstupem by tedy měly být následující řádky. Samotné odhlášení bude pouze odkaz na login.php s parametrem.

Zaheslovaná sekce: soubor admin.php

<?php

require_once('config.php');

// pouze presmerovani
function unauth_header(){
    header('Location: login.php?code=401',401);
    exit;
}

// kontrola uzivatele
// na kazdy refresh probehne kontrola cloveka
function check_user(){
	if(isset($_SESSION['jmeno'],$_SESSION['heslo'],$_SESSION['prava'])){
		$select = mysql_query("SELECT `id` FROM `uzivatele` WHERE `jmeno`='{$_SESSION['jmeno']}' AND `heslo`='{$_SESSION['heslo']}'") or die(mysql_error());
		$udaje = mysql_fetch_assoc($select);
		if(mysql_num_rows($select) != 1){
			unauth_header();
		}
	}else{
		unauth_header();
	}
}

check_user();

?>

Všechny soubory si můžete stáhnout zde: prihlasovani.zip

Edit 19.3.2008: Na připomínky čtenářů jsem přidal další direktivy do souboru .htaccess a také otrimování hesla při přihlašování.

Edit 21. 1. 2016: Skripty byly upraveny tak, aby nepoužívaly deprecated funkce. Systém kontroly registračního formuláře malinko upraven tak, aby lépe vracel chybové hlášky.