...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.

<!DOCTYPE html> 
<html lang="cs"> 
<head>
	<meta charset="utf-8" />
	<title>Registrace uživatele</title>
</head>
<body>

<?php if(!empty($message)){?>
	<ul style="color:red;">
		<?php foreach((array)$message as $item){?>
		<li><?php echo $item;?></li>
		<?php }?>
	</ul>
<?php }?>

<?php if(isset($_GET['ok'])){ ?>
	<h2>Registrace proběhla úspěšně</h2>
<?php }else{ ?>
	<ul>
		<li>Jméno a heslo se smí skládat pouze z alfanumerických znaků, pomlčky, podtržítka, tečky a zavináče.</li> 
		<li>Nepoužívejte diakritiku.</li>
	</ul>
	<form action="<?php echo $_SERVER['PHP_SELF']?>" method="post">
		<fieldset>
			<legend>Registrace uživatele</legend>
			<p><input name="jmeno" size="20" tabindex="1" type="text" value="<?php echo (!empty($_POST['jmeno']) ? htmlspecialchars($_POST['jmeno']) : ''); ?>" /> <label>Jméno *</label></p>
			<p><input name="email" size="20" tabindex="2" type="text" value="<?php echo (!empty($_POST['email']) ? htmlspecialchars($_POST['email']) : ''); ?>" /> <label>E-mail *</label></p>
			<p><input name="heslo" size="20" tabindex="3" type="password" /> <label>Heslo *</label></p>
			<p><input name="heslo2" size="20" tabindex="4" type="password" /> <label>Heslo znovu *</label></p>
			<input name="submit" type="submit" tabindex="5" value=" registrovat " /><br />
		</fieldset>
	</form>
<?php }?>

<p><a href="login.php">Přihlásit</a></p>
  
</body>
</html>

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.

?>
<!DOCTYPE html> 
<html lang="cs"> 
<head>
	<meta charset="utf-8" />
	<title>Přihlášení</title>
</head>
<body>

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

<?php if(!empty($error_message)){?>
	<ul style="color:red;">
		<li>Chybné přihlašovací údaje</li>
	</ul>
<?php }?>

<form action="<?php echo $_SERVER['PHP_SELF']?>" method="post">
	<fieldset>
		<legend>Přihlášení</legend>
		<p><input name="jmeno" size="20" tabindex="1" type="text" /> <label>Jméno</label></p>
		<p><input name="heslo" size="20" tabindex="2" type="password" /> <label>Heslo</label></p>
		<p><input name="submit" type="submit" tabindex="3" value=" přihlásit " /></p>
	</fieldset> 
</form>
  
<p><a href="register.php">Zaregistrovat</a></p>
  
</body>
</html>

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();

?>

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.