Deze website maakt gebruik van diensten van Google voor het tonen van advertenties en het bijhouden van bezoekersstatistieken. Google kan hiermee uw surfgedrag volgen. Zie voor meer informatie het privacybeleid van Google. Via Your Online Choices kunt u zich afmelden voor gepersonaliseerde advertenties. Deze melding verbergen.

22 MySQL Loginssyteem Beheerdeel

In hoofdstuk 21 hebben we een loginsysteem gemaakt dat gebruik maakt van een MySQL database. Wat daar nog aan ontbreekt is een beheerdeel om gebruikersaccounts te kunnen maken en verwijderen. Nu je zo ver bent gekomen in PHPBoek(); kun je jezelf geen beginner meer noemen. We geven nog wat uitleg over de onderdelen waaruit het beheerdeel bestaat en wat je daarvoor moet doen; daarna is het aan jou om dit te maken. Uiteraard is er een uitwerking, volledig voorzien van commentaar.

Oefening: beheerdeel

Gebruikersoverzicht

Het gebruikersoverzicht is een tabel met alle gebruikersaccounts. Van iedere rij in de MySQL tabel gebruikers wordt de gebruikersnaam, e-mailadres en tijdstempel getoond. Achter ieder gebruikersaccount staat een link naar een script om het account te verwijderen. De query-string wordt gebruikt om het gebruikers-id door te geven aan het verwijderscript (verwijder.php?id=1). Boven de tabel staat een link naar een script om een nieuw gebruikersaccount toe te voegen.

Voor het weergeven van de gebruikerslijst wordt de volgende SQL-query gebruikt:

SELECT * FROM `gebruikers`

Om alle rijen weer te geven wordt mysqli_fetch_assoc() gebruikt in combinatie met een while-loop:

$result = mysqli_query($link, $sql);
while ($data = mysqli_fetch_assoc($result)) {

}

Gebruikers toevoegen

Voor het toevoegen van nieuwe gebruikers wordt een gebruikersnaam en wachtwoord opgegeven in een formulier. Het script controleert vervolgens of de gebruikersnaam nog niet bestaat. Als dat zo is, wordt een e-mail met de gebruikersnaam en een gegenereerd willekeurig wachtwoord naar het opgegeven e-mailadres gestuurd en wordt de informatie in de database opgeslagen. De scripts voor het aanmaken van het admin-account en het opvragen van een nieuw wachtwoord uit hoofdstuk 21 kunnen hier hergebruikt worden. De SQL-query zal er als volgt uit zien:

INSERT INTO `gebruikers`
SET
    `gebruikersnaam` = 'GEBRUIKERSNAAM',
    `wachtwoord` = 'HASH_VAN_WACHTWOORD',
    `email` = 'EMAILADRES'

Gebruikers verwijderen

Het script voor het verwijderen van een gebruiker vraag eerst om een bevestiging. Daarna kan het SQL-statement DELETE gebruikt worden om een rij in de tabel te verwijderen:

DELETE FROM table_name
    [WHERE where_condition [, ...]
    [ORDER BY col_name [DESC] [, ...]]
    [LIMIT [offset,] row_count]

In de DELETE-query wordt eerst aangegeven uit welke tabel iets verwijderd moet worden. Daarnaast kunnen er via WHERE aanvullende voorwaarden worden opgegeven, zoals ook bij een SELECT-query. Eventueel mogen ook nog ORDER BY en LIMIT worden opgegeven, maar in de praktijk is daar niet zo vaak een nuttige toepassing voor. Als er geen enkele voorwaarde wordt opgegeven, dan worden alle rijen in de tabel verwijderd. De SQL-query voor het verwijderen van een gebruikersaccount ziet er dan als volgt uit:

DELETE FROM `gebruikers` WHERE `id` = 'ID_NUMMER'

Beveiliging

De scripts van het beheerdeel moeten beveiligd worden zodat niet iedereen zomaar gebruikersaccounts kan aanmaken en verwijderen. Als we dit doen met logincheck.inc.php uit hoofdstuk 21, dan kunnen nog steeds alle ingelogde gebruikers bij het beheerdeel. Er moet dus een aangepaste beveiliging komen voor het beheerdeel.

Als er niet veel beheerders zijn en deze niet vaak wijzigen, volstaat het om voor een eenvoudige oplossing te kiezen zoals de id's vastleggen in het script van gebruikers die beheerder zijn. Als er dan een beheerder bij komt moet even het script worden aangepast om deze toegang te geven tot het beheerdeel. Omdat gebruikers-id's niet wijzigen en niet hergebruikt worden is dit een adequate oplossing.

Een elegantere oplossing is in de database-tabel een extra kolom op te nemen om daarin aan te geven of een gebruiker ook beheerder is. Hiervoor moet dan ook het script om een nieuwe gebruiker aan te maken worden aangepast, om een vinkje op te nemen om iemand beheerder te kunnen maken. De gebruikers tabel kan via phpMyAdmin worden aangepast.

Opdracht

Maak het beheerdeel zoals hierboven beschreven. Om de uitwerking niet te complex te laten zijn, zijn een aantal controles of formulieren correct zijn ingevuld weggelaten.

Uitwerking opdracht

Uitwerking

config.inc.php

<?php
//bestand met databasegegevens
$db['server'] = 'localhost';
$db['user'] = 'root';
$db['password'] = '';
$db['database'] = 'phpboek';
//array met gebruikerid's die beheerder zijn
$beheerder_ids = array(1);
?>

genereer_wachtwoord.fct.php

<?php 
//functie om nieuw wachtwoord te genereren
function genereer_wachtwoord($len) {
    $tekens = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    $wachtwoord = '';
    for ($i = 0; $i < $len; $i++) {
        $start = mt_rand(0, strlen($tekens) - 1);
        $wachtwoord = $wachtwoord . substr($tekens, $start, 1);
    }
    return $wachtwoord;
}
?>

logincheck_beheer.inc.php

<?php
//haal gegevens uit cookie
$cookie = unserialize($_COOKIE['login']);
$user_id = $cookie['id'];
$wachtwoord = $cookie['password'];
//include database gegevens en array met beheerder-id's
include('config.inc.php');
//controleer of waarden in het cookie zinnig zijn
if (is_numeric($user_id) && (strlen($wachtwoord) == 64)) {
    //controleer of gebruikers-id uit cookie beheerder is
    if (in_array($user_id, $beheerder_ids)) {
        //verbind met server
        $link = mysqli_connect($db['server'], $db['user'], $db['password'], $db['database']);
        //stel karakterset in voor mysqli_real_escape_string
        mysqli_set_charset($link, 'latin1');
        //query om tabel te maken
        $sql = "SELECT
        `id`
        FROM `gebruikers`
        WHERE `id` = '" . mysqli_real_escape_string($link, $user_id) . "'
        AND `wachtwoord` = '" . mysqli_real_escape_string($link, $wachtwoord) . "'
        LIMIT 1";
        //voer query uit
        $result = mysqli_query($link, $sql);
        if (mysqli_num_rows($result) != 1) {
            //geef foutmelding en stop verder uitvoering van pagina.
            echo '<p>Ongeldige aanmelding. <a href="login.php">login</a>';
            exit;
        }
    }
    //geen beheerder
    else {
        //geef foutmelding en stop verder uitvoering van pagina.
        echo '<p>Je bent geen beheerder!</p>';
        exit;
    }
}
//cookie bevat ongeldige informatie
else {
    //geef foutmelding en stop verder uitvoering van pagina.
    echo '<p>Je bent niet aangemeld. <a href="login.php">login</a>';
    exit;
}
?>

nieuw.php

<?php 
//controleer of beheerder
include('logincheck_beheer.inc.php');
//functie om wachtwoorden te genereren
include_once('genereer_wachtwoord.fct.php');

//controleer of formulier verzonden
if (!empty($_POST)) {
    //genereer nieuw wachtwoord
    $wachtwoord = genereer_wachtwoord(8);
    //stel afzender en bericht op
    $afzender = 'From: ' . $_SERVER['SERVER_NAME'] . ' <noreply@' . $_SERVER['SERVER_NAME'] . '>';
    $onderwerp = 'Nieuw account';
    $bericht = 'Je gebruikersnaam is: 
    ' . $_POST['gebruikersnaam'] . '
    Je wachtwoord is: 
    ' . $wachtwoord;
    //genereer hash
    $wachtwoord = hash('sha256', $wachtwoord);
    //query om nieuwe gebruiker in te voegen
    //als gebruikersnaam al bestaat zal query vanzelf mislukken, omdat `gebruikersnaam` een unieke sleutel is
    $sql = "INSERT INTO `gebruikers`
    SET
        `gebruikersnaam` = '" . mysqli_real_escape_string($link, $_POST['gebruikersnaam']) . "',
        `wachtwoord` = '" . mysqli_real_escape_string($link, $wachtwoord) . "',
        `email` = '" . mysqli_real_escape_string($link, $_POST['email']) . "' ";
    //voer query uit
    if (mysqli_query($link, $sql)) {
        //stuur email
        $verzonden = mail($_POST['email'], $onderwerp, $bericht, $afzender);
    }
    else {
        //er is geen gebruiker
        $gebruikersnaam_fout = TRUE;
    }
}

?>
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Nieuwe gebruiker</title>
</head>
<body>

<h1>Nieuwe gebruiker</h1>

<?php
if ($gebruikersnaam_fout === TRUE) {
    echo '<p class="error">De ingevulde gebruikersnaam bestaat al.</p>';
}
if ($verzonden === TRUE) {
    echo '<p class="succes">Er is een nieuw account gemaakt en de gegevens zijn naar het opgegeven e-mailadres gestuurd.</p>';
}
?>

<form method="post">
<table>
<tr><td>Gebruikersnaam:</td><td><input type="text" name="gebruikersnaam"></td></tr>
<tr><td>E-mailadres:</td><td><input type="text" name="email"></td></tr>
<tr><td></td><td><input type="submit" value="Maak account"></td></tr>
</table>
</form>

</body>
</html> 

overzicht.php

<?php
//controleer of beheerder
include('logincheck_beheer.inc.php');

//als formulier verzonden
if (!empty($_POST['username']) && !empty($_POST['password'])) {
    //bereken hash van wachtwoord
    $wachtwoord = hash('sha256', $_POST['password']);
    //query om rij te selecteren
    $sql = "SELECT
    `id`
    FROM `gebruikers`
    WHERE `gebruikersnaam` = '" . mysqli_real_escape_string($link, $_POST['username']) . "'
    AND `wachtwoord` = '" . mysqli_real_escape_string($link, $wachtwoord) . "'
    LIMIT 1";
    //voer query uit
    $result = mysqli_query($link, $sql);
    if (mysqli_num_rows($result) == 1) {
        $row = mysqli_fetch_row($result);
        $cookie['id'] = $row[0];
        $cookie['password'] = $wachtwoord; 
        //zet cookie
        setcookie('login', serialize($cookie), time() + 60*60*24*7*2, '/');
        //login is gelukt
        $login_correct = TRUE;
    }
    //wachtwoord niet correct
    else {
        $login_error = TRUE;
    }
}
?>

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Overzicht gebruikers</title>
</head>
<body>

<h1>Overzicht gebruikers</h1>

<p><a href="nieuw.php">Nieuwe gebruiker</a></p>

<?php
//verbind met server
$link = mysqli_connect($db['server'], $db['user'], $db['password'], $db['database']);
//query om inhoud van tabel te selecteren
$sql = "SELECT * FROM `gebruikers`";
//voer query uit
$result = mysqli_query($link, $sql);
//als er een of meer rijen zijn
if (mysqli_num_rows($result) > 0) {
    //start html tabel
    echo '<table>';
    echo '<tr><th>Gebruikersnaam</th><th>E-mailadres</th><th></th></tr>';
    //lus om alle rijen weer te geven
    while ($data = mysqli_fetch_assoc($result)) {
        echo '<tr>';
        echo '<td>' . htmlspecialchars($data['gebruikersnaam']) . '</td>';
        echo '<td>' . htmlspecialchars($data['email'])  . '</td>';
        echo '<td><a href="verwijder.php?id=' . $data['id'] . '">Verwijder</a></td>';
        echo '</tr>';
    }
    //eind html tabel
    echo '</table>';
}
//geen resultaten
else {
    echo '<p>Er zijn geen gebruikers.</p>';
}

?>

</body>
</html> 

verwijder.php

<?php 
//controleer of beheerder
include('logincheck_beheer.inc.php');
?>
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Gebruiker verwijderen</title>
</head>
<body>

<h1>Gebruiker verwijderen</h1>

<?php
//controleer of formulier verzonden
if (!empty($_POST)) {
    //verwijder gebruiker
    $sql = "DELETE FROM `gebruikers`
    WHERE `id` = '" . mysqli_real_escape_string($link, $_POST['id']) . "'";
    if (mysqli_query($link, $sql)) {
        echo '<p>Gebruiker is verwijderd.</p>';
    }
    else {
        echo '<p>Gebruiker kan niet worden verwijderd. ' . mysqli_error($link) . '</p>';
    }
}
else {
    //formulier nog niet verzonden
    ?>
    
    <form method="post">
    <p>Weet je zeker dat je gebruiker met id <?php echo htmlspecialchars($_GET['id']); ?> wilt verwijderen</p>
    <input type="hidden" name="id" value="<?php echo htmlspecialchars($_GET['id']); ?>">
    <input type="submit" value="Gebruiker verwijderen">
    </form>
    
    <?php
}
?>

</body>
</html>