W tej części kursu pt. „Tworzenie gry przeglądarkowej” napiszemy klasę UserManager, która będzie realizowała rejestracje i logowanie użytkownika. Dodamy też proste zabezpieczenie przed wejściem na strony gry bez zalogowania. Zaczynajmy.

Na początku przejdźmy do bazy danych i usuńmy tabelę users, która stworzyliśmy w trzeciej lekcji. Wymaga sporo zmian i najprościej będzie stworzyć ją od nowa. Możemy to zrobić klikając na nazwę bazy w menu po lewej stronie. Wyświetli się lista tabel:

lista tabel

 

Klikamy usuń.

zniszcz table

Potwierdzamy usunięcie klikając OK.

Możemy teraz przejść do stworzenia nowej tabeli.

nowa_tabela

Możesz kliknąć na obrazek aby go powiększyć. Zmieniły się przede wszystkim dopuszczalne długości poszczególnych kolumn. Kolumna username uzyskała indeks UNIQUE.

Zanim przejdziemy do pisania klas zmieńmy jedną rzecz w pliku index.php. Interesuje nas ta linijka:

header("Location: ".SERVER_ADDRESS."home");

Chcemy aby domyślną stroną było home.library.php a nie statystyki.library.php ponieważ jest to strona na której będzie gra. Na stronie home będzie logowanie i rejestracja, więc chcemy tam kierować ruch.

 

Przejdźmy teraz do klasy ModuleLoader i dodajmy moduły, które wyświetlą formularz rejestracji i logowania. Poniżej case ranking dodaj następujący kod:

case 'rejestracja':
            echo '
                <div class="row rejestracja">
                        <div class="col-xs-12 col-sm-8 col-md-6 col-lg-6 col-lg-offset-3 col-md-offset-3 col-sm-offset-2">
                            <h2>Zarejestruj:</h2>
            				<form action="register/" method="POST" class="home_form">
                                
                                <label for="username">Nazwa użytkownika</label>
                                <input id="username" type="text" name="username"> 
                                <p class="komunikat"></p> 
                                
                                <label for="password">Hasło</label>
                                <input id="password" type="password" name="password"> 
                                <p class="komunikat"></p>
                                
                                <label for="email">Email</label>
                                <input id="email" type="email" name="email">
                                <p class="komunikat"></p>
                                
                                <input id="zarejestruj" type="submit" name="register" value="Zarejestruj">
                            </form>
                        </div>    
            
                </div>
            ';
            break; 
            
            case 'logowanie':
            echo '
                <div class="row logowanie">
                             <div class="col-xs-12 col-sm-8 col-md-6 col-lg-6 col-lg-offset-3 col-md-offset-3 col-sm-offset-2">
                            <h2>Zaloguj:</h2>
            				<form action="login/" method="POST" class="home_form">
                                
                                <label for="login">Nazwa użytkownika</label>
                                <input id="login" type="text" name="login"> <br/>
                                
                                <label for="haslo">Hasło</label>
                                <input id="haslo" type="password" name="password"> <br/>
                                
                                <input type="submit" name="zaloguj" value="Zaloguj">
                            </form>                
                    </div>
            
                </div>
            ';
            break;

Możemy zauważyć że formularz rejestracji jest całkiem inny niż logowania. Jest tak dlatego, że na formularzu rejestracji przeprowadzimy walidację a na formularzu logowania nie.

Wypełnijmy teraz plik home.library.php.

Plik home.library.php:

<?php

if(isset($_SESSION['logged'])) {
    header("Location: statystyki");
}

ModuleLoader::load('open');

echo '<div id="wrapper">';

ModuleLoader::load('navbar');

ModuleLoader::load('home');

ModuleLoader::load('rejestracja');

ModuleLoader::load('logowanie');

echo ' </div></section>';

ModuleLoader::load('footer');

echo '</div>';

ModuleLoader::load('js');

echo '</body></html>';

?>

Na samym początku sprawdzamy czy ustawiona jest sesja. Jeśli jest to znaczy że użytkownik jest zalogowany i wysyłamy go do gry – na stronę ze statystykami.

Zanim zajmiemy się walidacją formularza, dodajmy kilka reguł CSS, które ostylują formularze. W pliku style.css dodaj następujące reguły:

.home_form input {
    width: 80%;
    height: 30px;
    background: silver;
    color: #fff;
}

.home_form input:last-child {
    width: 40%;
    height: 50px;
}

.home_form h2 {
    margin-top: 220px;
}

.rejestracja > div {
    text-align: center;
}

.logowanie {
    margin-top: 50px;
}

.logowanie .home_form input:nth-child(2) {
    margin-bottom: 10px;
}

.logowanie .home_form input:last-child {
    margin-top: 10px;
}

.rejestracja input:first-child {
    margin-top: 10px;
}
 
label {
    width: 100%;
}

input.invalid, textarea.invalid{
    border: 3px solid red;
}
 
input.valid, textarea.valid{
    border: 3px solid green;
}
 
.blad {
	color: red;	
}
 
.ok {
	color: green;	
}

Są to bardzo proste reguły i myślę, że nie trzeba ich tłumaczyć. Nie ma żadnego znaczenia jakie te reguły będą – możesz dodać jakie chcesz. 🙂

Szczególnie ważne są cztery ostatnie reguły. Skorzystamy z nich przy walidacji formularza, którą teraz napiszemy. Wypełnijmy plik wlasny.js.

Plik wlasny.js:

$(document).ready(function() {
 
 //walidacja formularzy
   
 function walidacjaFormularza(id, ilosc_znakow, komunikat_ok, komunikat_blad, wzor){
    $(id).on('blur', function() {
		var input = $(this);
        
        if (typeof wzor != 'undefined') { //uznajemy że jest to email i nie sprawdzamy innych warunków
           var email = wzor.test(input.val()); 
           
           if(email){
    			input.removeClass("invalid").addClass("valid");
    			input.next('.komunikat').text(komunikat_ok).removeClass("blad").addClass("ok");
		   }
    		else {
    			input.removeClass("valid").addClass("invalid");
    			input.next('.komunikat').text(komunikat_blad).removeClass("ok").addClass("blad");
    		}
           return;
        } 
        
        var input_length = input.val().length;
        
		if(input_length >= ilosc_znakow[0] && input_length <= ilosc_znakow[1]){
			input.removeClass("invalid").addClass("valid");
			input.next('.komunikat').text(komunikat_ok).removeClass("blad").addClass("ok");
		}
		else{
			input.removeClass("valid").addClass("invalid");
			input.next('.komunikat').text(komunikat_blad).removeClass("ok").addClass("blad");
			
		}
});   
 }
 
walidacjaFormularza('#username', [5,20], "Wprowadzono poprawną nazwę użytkownika.", "Nazwa użytkownika musi mieć od 5 do 20 znaków.");
walidacjaFormularza('#password', [5,25], "Wprowadzono poprawne hasło!", "Hasło musi mieć od 5  do 25 znaków.");
walidacjaFormularza('#email', [1,255], "Wprowadzono poprawny email!", "Podany email jest nieprawidłowy.", /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i);


$('#zarejestruj').click(function(event){
			var username = $('#username');
			var password = $('#password');
            var email = $('#email');
			
			
			if(username.hasClass('valid') && password.hasClass('valid') && email.hasClass('valid')){
				alert("Wprowadzono poprawne dane!");	
			}
			else {
				event.preventDefault();
				alert("Uzupełnij wszystkie pola!");	
			}
});
 
});

Jest to bardzo prosta walidacja formularza. Tłumaczyłem ją w tym poście.

Możemy przejść teraz do napisania klasy UserManager. W folderze Managers utwórz plik UserManager.class.php.

Plik UserManager.class.php:

<?php


class UserManager {
    
    //utworzenie zmiennych
    protected $login;
    protected $password;
	protected $mail;
    protected $id;
    
    public function LogIn($LOGIN, $PASSWORD) { //przyjmujemy w formularza login i hasło
        
        $this->login = $LOGIN; //przypisanie loginu
        $this->password = $PASSWORD; //przypisanie hasła
        
        
        
        if(self::isExist() && count(self::isExist()) > 0) { //sprawdzenie czy metoda isExist wyszukała użytkownika - wyszukała, logujemy
             $id = self::getIdByUsername(); //pobranie id użytkownika
             $this->id = $id; //przypisanie id
           
            
            self::log_in(); //ustawienie sesji
            return $this->login;
            
        } else {
            
            return false; //nie znaleziono takiego użytkownika
            
        }
        
    }
    
    protected function isExist() { //sprawdzenie czy użytkownik o podanej kombinacji nazwy i hasła istnieje
        
        $arr = DatabaseManager::selectBySQL("SELECT * FROM users WHERE username='".$this->login."' AND password='".md5($this->password)."' LIMIT 1");
        return $arr; //zwrócenie tablicy
        
    }
    
    protected function getIdByUsername() { //pobieranie id użytkownika mając jego nick
        
        $array = DatabaseManager::selectBySQL("SELECT * FROM users WHERE username='".$this->login."' AND password='".md5($this->password)."' LIMIT 1");
        foreach($array as $key) {
            $id = $key['id'];
        }
        return $id; //zwracamy id
        
    }
    
    protected function log_in() { //utworzenie sesji
        
        $_SESSION['uid'] = $this->id;
        $_SESSION['logged'] = true;
        
    }
    
    public function LogOut() { //wylogowanie
        
        $_SESSION['uid'] = false; //ustwienie sesji na false
        $_SESSION['logged'] = false; //ustwienie sesji na false
        
        session_destroy(); // zniszczenie sesji
        
    }
    
    public function CreateUser($POST) { //rejestracja użytkownika
        
        if(isset($POST) && is_array($POST)) { //sprawdzenie czy została przesłana tablica i czy jest tablicą
            
            
            $res = DatabaseManager::insertInto("users", array("username"=>addslashes($POST['username']), 
                                               "password"=>md5($POST['password']), "email"=>addslashes($POST['email']))); //dodanie użytkownika do bazy danych
                                               
            if($res) { 
                return true; //powodzenie, zwracamy true
            } else {
                return false; //niepowoedzenie, zwracamy false
            }
            
        } else {
            
            return false; // zwracamy false
            
        }       
    }  
}

?>

To cała klasa obsługująca logowanie i rejestrację. Całość zawiera komentarze, które tłumaczą co się dzieje. Za rejestrację odpowiada metoda CreateUser. Reszta metod dotyczy logowania.

Przejdźmy teraz do zastosowania. W klasie ModuleLoader możemy zobaczyć że formularz dotyczący rejestracji zostaje wysłany pod adres register a formularz logowanie pod adres login. Musimy więc utworzyć dwa pliki – register.library.php oraz login.library.php. Stwórz te pliki w folderze LIBRARY.

Plik register.library.php:

<?php

if((strlen($_POST['username']) >= 5 && strlen($_POST['username']) <= 20) &&
   (strlen($_POST['password']) >= 5 && strlen($_POST['password']) <= 25) &&
    (preg_match('/^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i', $_POST['email']))) {
    
    $um = new UserManager;
    
    $res = $um->CreateUser($_POST); //przesłanie tablicy do metody CreateUser w klasie UserManager
    
    if($res) {
         $um->LogIn($_POST['username'], $_POST['password']); //zarejestrowano, logujemy użytkownika
         header("Location: ".$_SERVER['HTTP_REFERER']); //przekierowanie
    } else {
        
        die("Utworzenie użytkownika nie było możliwe!"); //przekierowanie na stronę błędu
        
    }
    
} else {
    die("DOSTĘP DO TEJ STRONY ZOSTAŁ ZABLOKOWANY!"); //wejście poza formularzem
}

?>

W pierwszym if przeprowadzamy podstawową walidację dotyczącą długości pól oraz poprawności emaila. Zawsze powinno się przeprowadzać walidację również po stronie serwera, ponieważ walidację w javascript można łatwo ominąć (np. wyłączając javascript). Następnie przesyłamy tablicę $_POST do metody CreateUser. Jeśli metoda zwróci true  to logujemy użytkownika i następuje przekierowanie do gry. Jeśli nie to kończymy działanie skryptu. W ramach ćwiczeń, możesz zrobić w tym miejscu przekierowanie na prostą stronę błędu.

Przejdźmy teraz do logowania.

Plik login.library.php:

<?php

if(isset($_POST['login']) && isset($_POST['password'])) {
    
    $um = new UserManager;
    
    if($um->LogIn($_POST['login'], $_POST['password'])) { //przekazanie do metody LogIn w klasie UserManager loginu i hasła
        
        header("Location: ".$_SERVER['HTTP_REFERER']); //przekierowanie do gry
        
    } else {
        
        die ("Nieprawidłowa nazwa użytkownika lub hasło."); //nieprawidłowe dane
        
    }
    
} else { 
    die("DOSTĘP DO TEJ STRONY ZOSTAŁ ZABLOKOWANY!"); //wejście bez formularza  
}

?>

Na początku sprawdzamy że login i hasło zostały w ogóle wysłane. Jeśli tak to wysyłamy te dane do metody LogIn. Jeśli ta metoda zwróci true to wysyłamy użytkownika na stronę gry. Jeśli nie to kończymy pracę skryptu.

Możemy teraz napisać plik odpowiadający za wylogowanie użytkownika. W folderze LIBRARY utwórz plik logout.library.php.

Plik logout.library.php:

<?php

if(isset($_SESSION['logged']) && isset($_SESSION['uid'])) {
    
    $um = new UserManager;
    $um->LogOut();

         header("Location: ".SERVER_ADDRESS."home");
    
} else {
    
    die("DOSTĘP DO TEJ STRONY ZOSTAŁ ZABLOKOWANY!");
    
}

?>

 

Jest to bardzo prosty plik, który sprawdza czy sesja jest ustawiona. Jeśli tak to zastosowana zostaje motoda LogOut, która niszczy sesję. Musimy teraz dodać te strony do klasy MainPage. Przejdź do pliku MainPage.class.php i pod ostatnim case dodaj poniższy kod:

case 'login':
            require_once $this->active_page.".library.php";
            break;
            
            case 'logout':
            require_once $this->active_page.".library.php";
            break;
            
            case 'register':
            require_once $this->active_page.".library.php";
            break;

Teraz logowanie i rejestracja działają 🙂

Musimy teraz zabezpieczyć strony gry (statystyki,walka,sklep,ranking ranking) przed niezalogowanymi użytkownikami. W plikach wewnątrz folderu LIBRARY, które odpowiadają za ładowanie tych stron dodaj na początku tą instrukcję:

if(!isset($_SESSION['logged'])){
    die("Musisz się zalogować!");
}

Nie pozwoli to niezalogowanym użytkownikom wejść na strony gry. Zablokujmy teraz wyświetlanie menu dla niezalogowanych użytkowników (na stronie home). Zmieńmy case navbar w klasie ModuleLoader.

case 'navbar':
            if(isset($_SESSION['uid'])){
                $button = '<button type="button" class="navbar-toggle button_nav" data-toggle="collapse" data-target="#moje-menu">
                                            <span class="sr-only">Nawigacja</span>
                                            <span class="icon-bar"></span>
                                            <span class="icon-bar"></span>
                                            <span class="icon-bar"></span>
                                        </button>';
                
                $menu = '<ul class="nav navbar-nav navbar-right" id="ul_nawigacja">
                                            <li class="active"><a href="statystyki">Statystyki</a></li>
                                            <li><a href="praca">Praca</a></li>
                                            <li><a href="sklep">Sklep</a></li>
                                            <li><a href="walka">Walka</a></li>
                                            <li><a href="ranking">Ranking</a></li>
                                            <li><a href="logout">Wyloguj</a></li>
                                        </ul>';                        
            } else {
                $button = '';
                $menu = '';  
            }
            
            echo '
            
              <header id="menu" class="navbar-fixed-top">
                    <div class="container">
                        <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
                            <nav class="navbar navbar-inverse" role="navigation" id="pasek_nawi">
                                <div class="container-fluid">
                                    <div class="navbar-header">
                                        '.$button.'
                                        <div id="logo">
                                            <h1>Gra</h1>
                                        </div>
                                        
                                    </div>
                                    <div class="collapse navbar-collapse" id="moje-menu">
                                        '.$menu.'
                                    </div>
                                </div>
                            </nav>
                        </div>
                    </div>
                </header>
            
            ';
            break;

Zmienne button oraz menu przyjmują treść tylko wtedy gdy użytkownik jest zalogowany. Zmienna menu przechowuje wcześniejsze odnośniki i jeden nowy – Wylogowanie, który niszczy sesję. Zmienna button to przycisk, który pojawiał się przy niższych rozdzielczościach.

Wszystko gotowe. Już od kolejnej lekcji zaczniemy pisać komponenty gry.

Przerabiany przykład możesz zobaczyć tutaj.

Paczkę możesz pobrać tutaj.

  • KorniXPL

    Wiem, że to dość stary temat, ale mam problem. Zarówno po napisaniu kodu samemu jak i skopiowaniu paczki i podmianie danych w config.php mam problem z rejestracją i logowaniem. Rejestracja odpowiada „Utworzenie użytkownika nie było możliwe!”, mimo że użytkownik dodał się do bazy poprawnie, niestety logowanie na niego skutkuje „Nieprawidłowa nazwa użytkownika lub hasło.”

  • Vikerian

    Zabezpieczenie w metodzie selectBySQL()

    $SQL = $conn->real_escape_string($SQL);

    z DatabaseManager.class.php wywala błąd przy logowaniu.

    Zostało dodane w 3 lekcji i należy je usunąć. Autor już to zrobił w swojej paczce, ale jeśli piszecie kod samodzielnie to możecie mieć ten problem.

  • Patryk Iksde

    Mam nadzieję, że mimo iż zostało to dodane bardzo dawno temu to nadal tu zaglądasz i pomożesz 😀
    Chcę wejść i zobaczyć jak wygląda moja strona. Niestety nie mogę zalogować się ( pomyślałem- być może konto phpadmin to nie to samo co tutaj) chciałem założyć konto, ale niestety nawet zarejestrować się nie mogę. Co mam zrobić? :/

  • Lukasz Kowalski

    Cześć. Podmieniłem tylko w config.php db_username na root oraz ustawilem brak hasla i rejestracja dziala, dodalem kilku uzytkownikow bez problemu na localhost;p Natomiast od okolo 2h mam problem z logowaniem… „Nieprawidłowa nazwa użytkownika lub hasło.” Mimo iz ten wpis byl umieszczony kilka msc temu to moze jest jeszcze szansa na odp? 😛

    • Bartłomiej Mąkina

      Cześć,

      dodajesz użytkowników przez formularz rejestracyjny czy bezpośrednio w bazie danych? Hasło jest szyfrowane podczas rejestracji i odszyfrowane podczas logowania. Jeśli dodasz użytkowników bezpośrednio w bazie danych, to hasło nigdy nie będzie prawidłowe 🙂

  • Piotr

    Przy rejestracji wywala błąd w userManager.class.php w 83linijce

  • Piotr

    uporałem się z podawaniem hasla do localhost zamieniając w komentarz w userManager.class.php od 38 do 46 linijki ale teraz próbuje się zalogować do konta które jest w phpmyadmin zrobione i wyświetla „Nieprawidłowa nazwa użytkownika lub hasło.” …

  • Piotr

    Używam XAMP-a i tam jak kiedyś robiłem system rejestracji tylko na localhost to nie używam hasła, tak też teraz usunąłem hasło z fragmentu „define(‚DB_PW’, ”);” ale wyskakuje http://scr.hu/2lq6/iei47

  • Arkadiusz

    Bardzo ciekawy kurs, doszedłem do tego momentu i crash. Prawdopodobnie metoda lub jej prywatność sypie system logowania . Myślałem że to wina iż przepisywalem kod ale pobrałam paczkę i dalej to samo. Cały czas wyskakuje błąd z Niepoprawnym użytkownikiem lub haslem. Czyli nie może pobrać dokładnie danych prawdopodobnie z protected od isExist lub getIdByUsername . Pozdrawiam

    • Bartłomiej Mąkina

      Cześć 🙂

      Po pobraniu paczki podmieniłeś dane dostępu do bazy danych w pliku config.php?

      Pozdrawiam.