Kihagyás

8.3. fejezet

A fejezet anyaga

Regisztráció és bejelentkezés

Ebben a részben elkészítünk egy működőképes regisztrációs és bejelentkezős rendszert. Kiegészítjük az előző fejezetben megírt regisztrációt úgy, hogy a felhasználók adatait egy szöveges fájlban tároljuk, majd megírjuk a bejelentkezést megvalósító PHP fájlt is.

Hozzuk létre a users.json fájlt, amelyben a regisztrált felhasználók adatait fogjuk tárolni! Ez a fájl kezdetben legyen az alábbi formátumban:

1
2
3
{
  "users": []
}

Mivel az előző fejezetben egy felhasználót összetett adatként, egy asszociatív tömbbel reprezentáltunk, ezért a felhasználók adatait szerializált formában fogjuk tárolni az állományban. A jelszavakat a tanult módon, hashelve tároljuk.

A functions.php fájl

Azért, hogy a kódunk logikusabban legyen szervezve, létrehozunk egy functions.php nevű fájlt, amibe olyan függvényeket írunk, amelyeket több másik PHP fájlban is felhasználunk.

Írjuk meg az alábbi két függvényt a fájlban:

  • load_users($path): array: beolvassa a $path elérési útvonalú fájlból a felhasználók szerializált adatait, deszerializálja azokat, és visszatér a deszerializált felhasználói adatokat tároló tömbbel
  • save_users($path, $users): elmenti a $path elérési útvonalú fájlba a $users tömb elemeit (az egyes felhasználók adatait) JSON formában!
Megoldás
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?php
  function save_users(string $path, array $data) {
    $users = load_users($path);

    $users["users"][] = $data;

    $json_data = json_encode($users, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);

    file_put_contents($path, $json_data);
  }

  function load_users(string $path): array {
    if (!file_exists($path))
      die("Nem sikerült a fájl megnyitása!");

    $json = file_get_contents($path);

    return json_decode($json, true);
  }
?>

A signup.php fájl

Keressük elő az előző fejezetben megírt signup.php fájlunkat! Írjuk át a regisztrációs űrlapot feldolgozó PHP kódot a következők szerint:

  • A regisztrált felhasználók adatait a users.json-ből töltsük be az előbb megírt load_users() függvény segítségével!
  • Sikeres regisztráció esetén fűzzük hozzá a felhasználókat tároló tömbhöz az újonnan regisztrált felhasználó adatait, majd írassuk ki a users.json-be a regisztrált felhasználókat a save_users() függvénnyel!
Megoldás (A módosításokat az eredeti fájlhoz képest világossárga háttérrel jelöltük)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?php
  include "functions.php";              // beágyazzuk a load_users() és save_users() függvényeket tartalmazó PHP fájlt
  $fiokok = load_users("users.json"); // betöltjük a regisztrált felhasználók adatait, és eltároljuk őket a $fiokok változóban

  $hibak = [];

  if (isset($_POST["regiszt"])) { 
    if (!isset($_POST["felhasznalonev"]) || trim($_POST["felhasznalonev"]) === "")
      $hibak[] = "A felhasználónév megadása kötelező!";

    if (!isset($_POST["jelszo"]) || trim($_POST["jelszo"]) === "" || !isset($_POST["jelszo2"]) || trim($_POST["jelszo2"]) === "")
      $hibak[] = "A jelszó és az ellenőrző jelszó megadása kötelező!";

    if (!isset($_POST["eletkor"]) || trim($_POST["eletkor"]) === "")
      $hibak[] = "Az életkor megadása kötelező!";

    if (!isset($_POST["nem"]) || trim($_POST["nem"]) === "")
      $hibak[] = "A nem megadása kötelező!";

    if (!isset($_POST["hobbik"]) || count($_POST["hobbik"]) < 2)
      $hibak[] = "Legalább 2 hobbit kötelező kiválasztani!";

    $felhasznalonev = $_POST["felhasznalonev"];
    $jelszo = $_POST["jelszo"];
    $jelszo2 = $_POST["jelszo2"];
    $eletkor = $_POST["eletkor"];
    $nem = NULL;
    $hobbik = NULL;

    if (isset($_POST["nem"]))
      $nem = $_POST["nem"];
    if (isset($_POST["hobbik"]))
      $hobbik = $_POST["hobbik"]; 

    foreach ($fiokok["users"] as $fiok) {
      if ($fiok["felhasznalonev"] === $felhasznalonev).
        $hibak[] = "A felhasználónév már foglalt!";
    }

    if (strlen($jelszo) < 5)
      $hibak[] = "A jelszónak legalább 5 karakter hosszúnak kell lennie!";

    if ($jelszo !== $jelszo2)
      $hibak[] = "A jelszó és az ellenőrző jelszó nem egyezik!";

    if ($eletkor < 18)
      $hibak[] = "Csak 18 éves kortól lehet regisztrálni!";

    if (count($hibak) === 0) {   // sikeres regisztráció
      $jelszo = password_hash($jelszo, PASSWORD_DEFAULT);       // jelszó hashelése
      // hozzáfűzzük az újonnan regisztrált felhasználó adatait a rendszer által ismert felhasználókat tároló tömbhöz
      $fiok = [
        "username" => $felhasznalonev, 
        "password" => $jelszo, 
        "age" => $eletkor,
        "gender" => $nem, 
        "hobbies" => $hobbik
      ];
      // elmentjük a kibővített $fiokok tömböt a users.json fájlba
      save_users("users.json", $fiok);
      $siker = TRUE;
    } else {                    // sikertelen regisztráció
      $siker = FALSE;
    }
  }
?>

A login.php fájl

Miután megvalósítottuk a regisztrált felhasználók adatainak elmentését, most már van értelme egy bejelentkezési űrlap elkészítésének is. Hozzunk létre egy login.php nevű állományt, és másoljuk bele az alábbi kódot!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?php
  // ide jön az űrlapot feldolgozó PHP kód...
  $uzenet = "";
?>

<!DOCTYPE html>
<html lang="hu">
  <head>
    <title>Bejelentkezés</title>
    <meta charset="UTF-8"/>
  </head>
  <body>
    <form action="login.php" method="POST">
      <label>Felhasználónév: <input type="text" name="felhasznalonev"/></label> <br/>
      <label>Jelszó: <input type="password" name="jelszo"/></label> <br/>
      <input type="submit" name="login"/> <br/><br/>
    </form>
    <?php echo $uzenet . "<br/>"; ?>
  </body>
</html>

Írjuk meg az űrlapot feldolgozó PHP kódot! Az űrlapfeldolgozás során a következőket ellenőrizzük:

  • A felhasználónevet és a jelszót kötelező legyen megadni!
  • Az űrlapon megadott felhasználónév-jelszó párosnak meg kell egyeznie valamely regisztrált felhasználó felhasználónevével és jelszavával!

A regisztrált felhasználók adatait a users.json fájlból töltsük be (load_users() függvény)! Ha a bejelentkezés sikeres, írassuk ki a "Sikeres belépés!" szöveget, ellenkező esetben írassunk ki hibaüzenetet ($uzenet változó)!

Megoldás
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
  include "functions.php";              // a load_users() függvény ebben a fájlban van
  $fiokok = load_users("users.json"); // betöltjük a regisztrált felhasználók adatait, és eltároljuk őket a $fiokok változóban

  $uzenet = "";                     // az űrlap feldolgozása után kiírandó üzenet

  if (isset($_POST["login"])) {    // miután az űrlapot elküldték...
    if (!isset($_POST["felhasznalonev"]) || trim($_POST["felhasznalonev"]) === "" || !isset($_POST["jelszo"]) || trim($_POST["jelszo"]) === "") {
      // ha a kötelezően kitöltendő űrlapmezők valamelyike üres, akkor hibaüzenetet jelenítünk meg
      $uzenet = "<strong>Hiba:</strong> Adj meg minden adatot!";
    } else {
      // ha megfelelően kitöltötték az űrlapot, lementjük az űrlapadatokat egy-egy változóba
      $felhasznalonev = $_POST["felhasznalonev"];
      $jelszo = $_POST["jelszo"];

      // bejelentkezés sikerességének ellenőrzése
      $uzenet = "Sikertelen belépés! A belépési adatok nem megfelelők!";  // alapból azt feltételezzük, hogy a bejelentkezés sikertelen

      foreach ($fiokok["users"] as $fiok) {              // végigmegyünk a regisztrált felhasználókon
        // a bejelentkezés pontosan akkor sikeres, ha az űrlapon megadott felhasználónév-jelszó páros megegyezik egy regisztrált felhasználó belépési adataival
        // a jelszavakat hash alapján, a password_verify() függvénnyel hasonlítjuk össze
        if ($fiok["username"] === $felhasznalonev && password_verify($jelszo, $fiok["password"])) {
          $uzenet = "Sikeres belépés!";        // ekkor átírjuk a megjelenítendő üzenet szövegét
          break;                               // mivel találtunk illeszkedést, ezért a többi felhasználót nem kell megvizsgálnunk, kilépünk a ciklusból 
        }
      }
    }
  }
?>

Átirányítás

PHP-ban a header() beépített függvénnyel manuálisan is beállíthatjuk a szerver által elküldött HTTP válaszüzenet fejlécinformációit.

A gyakorlaton ezt a függvényt arra fogjuk használni, hogy a felhasználót átirányítsuk egy adott URL-re. Ezt a "Location:" fejlécinformáció beállításával tehetjük meg.

1
2
3
<?php
  header("Location: home.php");   // átirányítás a home.php weboldalra
?>

Fontos, hogy az elküldött fejlécinformációknak meg kell előzniük bármilyen más elküldött tartalmat! Probléma lehet ugyanis abból, ha a header()-t azután hívjuk meg, miután valamilyen kimenet (pl. HTML tagek, üres sorok vagy PHP által generált output) már el lett küldve.

Feladatok

  1. Írjuk át az imént elkészített signup.php állományt úgy, hogy a sikeres regisztrációt követően a felhasználó a login.php oldalra legyen átirányítva!
  2. Írjuk át a login.php-t úgy, hogy a sikeres bejelentkezést követően a felhasználó az index.php nevű főoldalra legyen átirányítva! (Ha még nem létezik ez a fájl, akkor hozzuk létre!)
Megoldás

A signup.php kiegészítése:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<?php
  // ...

  if (count($hibak) === 0) {   // sikeres regisztráció
    $jelszo = password_hash($jelszo, PASSWORD_DEFAULT);
      $fiok = [
        "username" => $felhasznalonev, 
        "password" => $jelszo, 
        "age" => $eletkor,
        "gender" => $nem, 
        "hobbies" => $hobbik
      ];
    save_users("users.json", $fiok);
    $siker = TRUE;
    header("Location: login.php");            // átirányítás a login.php oldalra
  }

  // ...
?>

A login.php kiegészítése:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<?php
  // ...

  foreach ($fiokok["users"] as $fiok) {              
    if ($fiok["username"] === $felhasznalonev && password_verify($jelszo, $fiok["password"])) {
      $uzenet = "Sikeres belépés!";        
      header("Location: index.php");          // átirányítás az index.php oldalra
    }
  }

  // ...
?>

Menetkövetés

A webes kommunikáció során használatos HTTP egy állapotmentes protokoll. Ez azt jelenti, hogy a szerver a klienstől kapott kéréseket egymástól függetlenül kezeli, és alapból nem emlékezik az adott klienstől kapott korábbi kérések adataira.

Ez problémás lehet, ugyanis a webalkalmazásoknak folyamatos kapcsolatban kell lenniük a felhasználókkal, és a különböző kérések között is emlékezniük kell az addigi adatokra, műveletekre. Például, ha a webalkalmazásunk egyik oldalán bejelentkezik a felhasználó, egy másik oldalon pedig felhasználói beállításokat eszközöl (pl. az alkalmazás megjelenésével vagy nyelvével kapcsolatban), akkor fontos, hogy tudjunk olyan adatokat is tárolni a felhasználóról, amelyek az alkalmazásunk több oldala számára is elérhetők.

Ennek megvalósítására szolgál a menetkövetés.

A menetkövetés megvalósítási módjai

A menetkövetést megvalósíthatjuk:

  • kliensoldalon
    • rejtett űrlapmezőkkel
    • URL újraírás segítségével
    • böngészősütik (cookie-k) használatával
  • szerveroldalon
    • session segítségével.

Megjegyzés

A fenti módszerek közül a gyakorlaton a sessionnel és böngészősütikkel történő menetkövetést tárgyaljuk részletesen. A rejtett űrlapmezők használata és az URL újraírás előadáson kerül ismertetésre.

A kliensoldali technológiák esetén az adatokat a kliensgépen tároljuk. Ennek egyik nagy hátránya, hogy a kliensoldalon tárolt adatokhoz könnyen hozzá lehet férni és egyszerűen át lehet őket írni. Ha ezt meg akarjuk akadályozni, akkor tároljuk az adatokat a szerveren!

Session

A menetkövetés egyik lehetséges megvalósítása a session. Ennek a lényege, hogy a weboldal megnyitásakor a klienshez egy egyedi munkamenet-azonosítót (session id) rendelünk. Ezután a kliens minden HTTP kérés esetén elküldi a munkamenet-azonosítót a szervernek, a szerver pedig különféle adatokat rendelhet a felhasználóhoz az azonosító alapján.

Egy munkamenet lényegében a kliens és szerver között zajló különféle műveletek sorozata, amely általában a böngésző bezárásáig él. A munkamenet-azonosító egyértelműen azonosítja a munkamenetet és az ahhoz tartozó szerveroldali adatokat.

A munkamenet-azonosító kliensoldalon általában böngészősütiben (cookie-ban) tárolódik, viszont az URL végére írva, GET paraméterként is átadható (például ha a sütik le vannak tiltva).

Munkamenet-kezelő függvények

A PHP számos beépített függvényt biztosít a munkamenetek kezelésére.

A session_start() függvény először megnézi, hogy létezik-e már egy érvényes munkamenet-azonosító (amit a szerver sütiben vagy GET/POST paraméterben megkapott). Ha talál ilyet, akkor betölti a hozzá tartozó adatokat, egyébként pedig létrehoz egy új munkamenetet.

Miután a session_start() függvényt meghívtuk, elérhetővé válik a $_SESSION szuperglobális tömb, amiben kulcs-érték párokként tároljuk a munkamenet-változók neveit és értékeit. Ebben természetesen új adatokat is elhelyezhetünk, például eltárolhatunk különféle információkat a bejelentkezett felhasználóról.

A session_id() függvénnyel lekérdezhetjük a munkamenet-azonosítót.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<?php
  session_start();              // meglévő munkamenet betöltése vagy új munkamenet létrehozása
  echo session_id() . "<br/>";  // munkamenet-azonosító kiíratása

  $_SESSION["username"] = "guest";    // egyszerű adatok tárolása a $_SESSION szuperglobális tömbben
  $_SESSION["age"] = 42;

  $_SESSION["user"] = ["username" => "guest", "age" => 42]; // természetesen összetett adatok is tárolhatók a $_SESSION-ben

  if (isset($_SESSION["user"])) {     // ha be lett állítva a "user" munkamenet-változó értéke...
    if (isset($_SESSION["user"]["username"])) {
      echo "<p>Üdvözöllek, " . $_SESSION["user"]["username"] . "! </p>";
    }
  }
?>

A session_unset() függvénnyel kiüríthetjük a munkamenet-változókat, a session_destroy() függvénnyel pedig törölhetjük a munkamenetet. Ezek segítségével megvalósítható a felhasználó manuális kijelentkeztetése.

1
2
3
4
5
6
<?php
  session_start();

  session_unset();          // munkamenet-változók kiürítése ($_SESSION egy üres tömb lesz)
  session_destroy();        // munkamenet törlése
?>
A példaprojekt kiegészítése menetkövetéssel

Egészítsük ki a korábban elkezdett példaprojektünket menetkövetéssel!

  • A bejelentkezést követően tároljuk el a felhasználó adatait tartalmazó asszociatív tömböt egy munkamenet-változóban!
  • Miután a felhasználó sikeresen bejelentkezett a weboldalunkon:
    • a "Bejelentkezés" oldal helyett jelenítsünk meg egy "Profilom" oldalt, ahol kilistázzuk a felhasználó adatait
    • a "Regisztráció" oldalt cseréljük le egy "Kijelentkezés" opcióra!

A "Profilom" oldal csak a sikeres bejelentkezést követően legyen elérhető! Amennyiben egy nem bejelentkezett felhasználó szeretne erre az oldalra navigálni, akkor irányítsuk át a bejelentkezés oldalra!

A megoldás menete

Először kiegészítjük a login.php-t menetkövetéssel. A bejelentkezést követően a $_SESSION["user"] fogja tárolni a bejelentkezett felhasználó adatait.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
  session_start(); 
  include "functions.php";
  $fiokok = load_users("users.json");

  $uzenet = "";

  if (isset($_POST["login"])) {
    if (!isset($_POST["felhasznalonev"]) || trim($_POST["felhasznalonev"]) === "" || !isset($_POST["jelszo"]) || trim($_POST["jelszo"]) === "") {
      $uzenet = "<strong>Hiba:</strong> Adj meg minden adatot!";
    } else {
      $felhasznalonev = $_POST["felhasznalonev"];
      $jelszo = $_POST["jelszo"];

      $uzenet = "Sikertelen belépés! A belépési adatok nem megfelelők!"; 

      foreach ($fiokok["users"] as $fiok) {              
        if ($fiok["username"] === $felhasznalonev && password_verify($jelszo, $fiok["password"])) {
          $uzenet = "Sikeres belépés!";
          $_SESSION["user"] = $fiok;              // a "user" nevű munkamenet-változó a bejelentkezett felhasználót reprezentáló tömböt fogja tárolni
          header("Location: index.php");          // átirányítás
        }
      }
    }
  }
?>

Mivel a sessionben tárolt adatokhoz a weboldal többi lapján is hozzá szeretnénk férni, ezért helyezzük el a session_start() utasítást az index.php, login.php, logout.php és profile.php fájlok elején is!

1
2
3
<?php
  session_start();
?>

Alakítsuk át a navigációt úgy, hogy a "Bejelentkezés" és "Regisztráció" opciók a nem bejelentkezett felhasználók számára legyenek láthatók! A bejelentkezett felhasználók ezek helyett a "Profilom" és a "Kijelentkezés" menüpontokat lássák!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<nav>
  <ul>
    <li><a href="index.php">Főoldal</a></li>
    <?php if (isset($_SESSION["user"])) { ?>
      <li><a href="profile.php">Profilom</a></li>
      <li><a href="logout.php">Kijelentkezés</a></li>
    <?php } else { ?>
      <li><a href="login.php">Bejelentkezés</a></li>
      <li><a href="signup.php">Regisztráció</a></li>
    <?php } ?>
  </ul>
</nav>

Valósítsuk meg a felhasználó kijelentkeztetését a logout.php állományban! A sikeres kijelentkezés után irányítsuk át a felhasználót a login.php oldalra!

1
2
3
4
5
6
7
<?php
  session_start();
  session_unset();
  session_destroy();

  header("Location: login.php");    // átirányítás
?>

Végezetül készítsük el a profile.php oldalt, ahol a bejelentkezett felhasználó adatait fogjuk kilistázni! Ezt az oldalt csak a sikeres bejelentkezést követően szeretnénk elérhetővé tenni, ezért még a fájl elején ellenőrizzük, hogy van-e bejelentkezett felhasználó. Amennyiben nincs, a login.php-ra navigálunk.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?php
  session_start();

  if (!isset($_SESSION["user"])) {
    // ha a felhasználó nincs belépve (azaz a "user" munkamenet-változó értéke nem került korábban beállításra), akkor a login.php-ra navigálunk
    header("Location: login.php");
  }
?>

...

<?php
  // profiladatok kilistázása
  echo "<ul>";
  echo "<li>Felhasználónév: " . $_SESSION["user"]["felhasznalonev"] . "</li>";
  echo "<li>Életkor: " . $_SESSION["user"]["eletkor"] . "</li>";
  echo "<li>Nem: " . $_SESSION["user"]["nem"] . "</li>";
  echo "<li>Hobbik: " . implode(", ", $_SESSION["user"]["hobbik"]) . "</li>";
  echo "</ul>";
?>

Utolsó frissítés: 2024-04-18 12:30:49