Webcity.pl


  Własny mechanizm ses...

  Galeria w PHP

  SimpleXML nadchodzi!

  Obsługa MySQL'a w PH...

  Cachowanie zapytań S...

 

 02.02.06 - [new] E-video - artykuł

 30.01.06 - [update] Artykuł o sesjach

 18.12.05 - [update] PDO - artykuł

 19.08.05 - [new+upd] Aktualizacja materia...

 26.04.05 - [update] Nowy artykuł



  Pomocy WML
[php]Sesje nie działają, ...
[actionscript 2.0] [flash...
Problem z talicami w php
[ocena] llll.pl darmowe a...
Kamera na stronie interne...
Kamera na stronie interne...
[xml] Jak wyciągnąć dane
Wizualizacja w JS - POMOC
Web Developer - stała / f...

... i wiele innych wątków na forum ›

Partnerzy
› allRSS.info - katalog zasobów RSS
› iloveflyer.org - webdesign
› webserv.pl - serverpack
› skryptoteka.pl - mnóstwo skryptów
› vel.pl - hosting
› TelePraca.net - pracuj swobodnie
› PHP Solutions - magazyn PHP
› HELION - wydawnictwo informatyczne

Ksišżka dnia

CityMag
Wpisz swój e-mail, aby zaprenumerować nasz Magazyn, który zawiera najnowsze informacje ze świata i najciekawsze teksty.
 

Szukasz czegoś?
Nasza wyszukiwarka znajdzie wszystko, czego szukasz.
 

Szukanie zawansowane


  Reklama
  Redakcja
  Hosting
  Kanał

© WebCity.pl Team
 
 
  Forum Forum
Kursy Kursy
Porady Porady
Recenzje Recenzje
Newsy Newsy
Katalog stron WWW Katalog
Skrypty PHP Skrypty
Download Oferty i praca
Artykuły:
 Teoria
 Praktyka
 Promocja
 Inne
 

 Webcity.pl |

Własny mechanizm sesji w PHP

Autor: Zyx
   Nowa, poprawiona i bardziej aktualna wersja tego artykułu znajduje się tutaj (format PDF, 190 kb).

   Co jakiś czas na forum pojawiały się problemy z sesjami PHP. I zawsze w takim przypadku doradzałem stworzenie własnego mechanizmu, gdyż według mnie, ten dostępny domyślnie najlepszy nie jest, czego dowodem jest sama ilość pytań. Jednak z własnego doświadczenia wiem, iż zadanie stworzenia własnego odpowiednika nie należy do łatwych nawet dla osób znających się na rzeczy, a co dopiero dla początkujących! Dlatego postanowiłem wyjść im naprzeciw i tak oto narodził się ten tekst.

   Plany
Sesja w naszym rozumieniu to mechanizm przekazywania danych pomiędzy wywołaniami strony wychodzącymi od jednego komputera w obrębie tego samego serwisu. Za jej pomocą tworzone są rozmaite rzeczy: od koszyków internetowych (dzięki sesji towary nie "gubią się", gdy internauta zmieni podstronę), po systemy autoryzacji i logowania (phpBB, InvisionBoard i wiele innych). My zajmiemy się wykorzystaniem sesji do tego ostatniego zadania. Jednak, jak wspomniałem wyżej, nie będziemy korzystać z tego, co nam podsunęli twórcy PHP. My pójdziemy dalej i stworzymy wszystko od zera. Da to nam pełną kontrolę nad całym mechanizmem, a także możliwość jego rozbudowy o kolejne elementy, wraz z ich obsługą w samym systemie sesji.
   Szczegółowe dane sesji będą trzymane w bazie, między stronami przekazywany będzie 32-znakowy identyfikator sesji. Jego znajomość umożliwi nam załadowanie całej reszty. Oczywiście pod pewnymi warunkami. Aby upewnić się, że dane są pobierane od właściwej osoby, musimy przeprowadzić rozmaite testy. U nas będzie to tylko jeden - test adresów IP. Jeżeli adres IP zapisany w sesji będzie identyczny z tym, który podaje nam serwer, sesję można uznać za prawdziwą.
   Stare sesje musimy usuwać, dlatego będą one zawierać pole z czasem ostatniej aktywności. Przestarzałe po prostu zostaną prędzej, czy później skasowane, a w razie potrzeby na ich miejscu stworzy się nowe.
   Jako że nasz mechanizm posłuży do autoryzacji, musimy wziąć pod uwagę także roboty wyszukiwarek internetowych. Wiadomo, że nie mogą one przeglądać strony na równych zasadach z ludźmi, będą więc dla nich tworzone pseudo-sesje, nie rejestrowane w bazie danych. Taka pseudo-sesja wyklucza całkowicie możliwość logowania się, stąd też roboty nie dotrą do zasobów, które z jakiś przyczyn chcemy chronić.
   System przewiduje tylko dwa predefiniowane typy internautów: gości oraz użytkowników (zalogowani). Część zasobów będzie dostępna dla wszystkich, część tylko dla zalogowanych użytkowników. W rzeczywistości stosuje się o wiele bardziej rozbudowane systemy autoryzacji i przyznawania uprawnień, jednak jest to temat na inną bajkę. Próba jego opisania tutaj skończyłaby się rozrostem tekstu do niemiłosiernych rozmiarów.

   Baza danych
System podany poniżej wykorzystuje bazę danych MySQL, stąd też potrzebnych nam będzie parę tabelek. Oto ich kod:

CREATE TABLE `sessions` (
   `session_id` varchar(32) NOT NULL default '',
   `session_user` int(8) NOT NULL default '0',
   `session_ip` varchar(15) NOT NULL default '',
   `session_browser` varchar(128) NOT NULL default '',
   `session_time` int(11) NOT NULL default '0',
   PRIMARY KEY (`session_id`)
) TYPE=HEAP;

CREATE TABLE `users` (
   `user_id` int(10) NOT NULL auto_increment,
   `user_name` varchar(32) NOT NULL default '',
   `user_password` varchar(32) NOT NULL default '',
   PRIMARY KEY (`user_id`)
);

INSERT INTO `users` VALUES (1, 'luser', MD5('ppp'));

W tabeli sessions znajdują się dwa pola przenoszące jakieś dane o użytkowniku strony. Nie są one konieczne mechanizmowi sesji do działania, stąd też możemy je wykorzystać wedle upodobań. session_user przechowuje ID zalogowanego użytkownika. Jeśli sesja należy do gościa, znajdzie się tu wartość "0". Natomiast session_browser może posłużyć do identyfikacji przeglądarki przy wyświetlaniu listy gości w serwisie. Czas (pole session_time) tradycyjnie już zapiszemy w sekundach od 1.1.1970.
   Tabela users wygląda tradycyjnie - ot ID, nazwa użytkownika oraz jego hasło tradycyjnie hashowane MD5.

   Klasa "session"
Klasę tę zapiszemy w pliku sessions.php. W niej zawarte będą metody służące do ładowania, aktualizacji, tworzenia oraz niszczenia sesji. Jej nagłówek wygląda tak:

<?php

   define('SESSION_TIME', 3600);

   define('WEBBOT_S', 0);
   define('GUEST_S', 1);
   define('USER_S', 2);

   define('ANONYMOUS', 0);

   class session{
      var $session_id;
      var $session_user;
      var $session_ip;
      var $session_browser;
      var $session_time;

      var $sql_id;
      var $ip;
      var $browser;
      var $type;

      var $userdata;

Pierwsze pięć pól służy do gromadzenia danych z sesji. Jeżeli dodamy/usuniemy z bazy jakieś pole, powinniśmy także wprowadzić zmianę właśnie tu. Oprócz tego mamy dodatkowe pięć pól pełniące rozmaite funkcje. W $sql_id zapisany jest ID sesji przepuszczony przez funkcję mysql_real_escape_string(), która daje nam pewność, że wszystkie znaki specjalne MySQL'a staną się bezpieczne i nie będą rozwalać nam zapytań (należy tu uwzględnić ustawienia magic_quotes!). $ip i $browser to dane o adresie IP, oraz przeglądarce, z której nadeszło żądanie otwarcia strony. $type służy do określania typu sesji, a odpowiednie wartości reprezentują trzy stałe podane tuż przed klasą: WEBBOT_S (sesja bota wyszukiwarki), GUEST_S (sesja osoby niezalogowanej) oraz USER_S (sesja osoby zalogowanej). Ostatim polem jest $userdata będące tablicą, gdzie zapisujemy wszystkie przydatne informacje o osobie zalogowanej. W naszym przypadku jest to tylko ID i jego nazwa.
   Przyszedł teraz czas na zaprezentowanie pierwszej metody. Pobierze ona informacje o tym, skąd nadeszło żądanie otwarcia strony, a także sprawdzi, czy nie gości na naszej stronie przypadkiem bot jednej ze znanych wyszukiwarek. Oto stosowny kod:

      function spy_guest(){
         if(getenv('HTTP_X_FORWARDED_FOR') != ''){
            $this -> ip = (!empty($_SERVER['REMOTE_ADDR'])) ? $_SERVER['REMOTE_ADDR'] : ((!empty($_ENV['REMOTE_ADDR'])) ? $_ENV['REMOTE_ADDR'] : $REMOTE_ADDR );

            if(preg_match("/^([0-9]+.[0-9]+.[0-9]+.[0-9]+)/", getenv('HTTP_X_FORWARDED_FOR'), $ip_list)){
               $private_ip = array('/^0./', '/^127.0.0.1/', '/^192.168..*/', '/^172.16..*/', '/^10..*/', '/^224..*/', '/^240..*/');
               $this -> ip = preg_replace($private_ip, $this -> ip, $ip_list[1]);
            }
         }else{
            $this -> ip = (!empty($_SERVER['REMOTE_ADDR'])) ? $_SERVER['REMOTE_ADDR'] : ((!empty($_ENV['REMOTE_ADDR'])) ? $_ENV['REMOTE_ADDR'] : $REMOTE_ADDR );
         }

         $this -> browser = (!empty($_SERVER['HTTP_USER_AGENT'])) ? $_SERVER['HTTP_USER_AGENT'] : $_ENV['HTTP_USER_AGENT'];

         if(preg_match('/(lycos|googlebot|slurp@inktomi|ask jeeves|whatuseek|ia_archiver)/i', $this -> browser)){
            $this -> type = WEBBOT_S;
         }else{
            $this -> type = GUEST_S;
         }
      } // end spy_guest();

Jak widać w ostatnich linijkach, "autoryzacja" bota nie jest aż taka skomplikowana. Wystarczy wiedzieć, jak identyfikuje się dany robot, a następnie dopisać do wyrażenia, które dokona już reszty.
   Kolejna metoda będzie odpowiadać za wykrycie sesji oraz ewentualne wczytanie jej identyfikatora. Kod będzie wyglądał tak:

      function is_session(){
         if(strlen($_COOKIE['my_session_id']) == 32){
            $this -> session_id = $_COOKIE['my_session_id'];
            setcookie('my_session_id', $this->session_id, time() + SESSION_TIME);
            $this -> sql_id = mysql_real_escape_string($this->session_id);
            return 1;
         }
         return 0;
      } // end is_session();

Teraz opowiem, jak taki ID sesji wygląda. Otóż posłuży nam za niego połączenie aktualnego czasu oraz adresu IP gościa przepuszczone najpierw przez funkcję uniqid(), a następnie przez md5() - każdy identyfikator będzie się więc składał z 32 znaków i to nam może posłużyć do sprawdzenia, czy takowy jest już ustawiony.
   Jeżeli wykryta zostanie sesja, skrypt załaduje do pamięci jej ID, a także przedłuży ważność ciastka.
   Gdy już wykryjemy, czy sesja istnieje, czy nie, warto załadować jej dane do pamięci oraz dokonać niezbędnych kontroli. Posłuży nam do tego metoda load_session(). Zwróci ona 1, gdy nie wystąpią żadne błędy podczas kontroli i ładowania sesji.

      function load_session(){
         $r = mysql_query('SELECT s.*, u.*
                  FROM sessions s LEFT JOIN users u ON (s.session_user <> 0 AND s.session_user = u.user_id)
                  WHERE s.session_id = ''.$this -> sql_id.'' AND s.session_time > '.(time() - 3600));
         if($row = mysql_fetch_row($r)){
            $this -> session_id = $row[0];
            $this -> session_user = $row[1];
            $this -> session_ip = $row[2];
            $this -> session_browser = $row[3];
            $this -> session_time = $row[4];

            if($this -> session_user != ANONYMOUS){
               $this -> type = USER_S;
               $this -> userdata['ID'] = $row[5];
               $this -> userdata['NAME'] = $row[6];
            }else{
               $this -> type = GUEST_S;
            }

            if($this -> session_browser == $this -> browser && $this -> session_ip == $this -> ip){
               mysql_query('UPDATE sessions SET session_time = ''.time().'' WHERE session_id = ''.$this->sql_id.''');
               return 1;
            }
         }
         $this -> type = GUEST_S;
         return 0;
      } // end load_session();

Wypada mi omówić [[zapytanie]] [] użyte do pobrania danych, gdyż zapewne wiele osób nie zna użytej w nim konstrukcji. Dzięki użyciu przeze mnie konstrukcji LEFT JOIN tabela ON warunek mogę dołączyć do zbioru wyników informacje o zalogowanym człowieku, ale tylko wtedy, gdy sesja należy do kogoś takiego! Tak więc jeśli w polu session_user znajdzie się 0, rekord dotyczący użytkownika nie zostanie dołączony do wyniku, gdzie znajdą się tylko dane sesji. W przeciwnym wypadku w jednym elemencie wyniku będziemy mieli zarówno sesję, jak i użytkownika. Tym samym skrypt wykona zawsze tylko jedno zapytanie, zamiast dwóch w przypadku konieczności załadowania dodatkowych informacji.
   Jako że starych sesji nie usuwamy przy każdym żądaniu wygenerowania strony (byłoby to zbyt czasochłonne, w końcu jak często się taka sesja trafia?), musimy dołożyć do naszego zapytania odpowiednią blokadę: s.session_time > '.(time() - 3600).
   Czasem się zdarzy, że sesję trzeba będzie usunąć. Posłuży nam do tego ta oto metoda:

      function delete_session(){
         mysql_query('DELETE FROM sessions WHERE session_id=''.$this->sql_id.''');
         setcookie('my_session_id', '', 0);
      } // end delete_session();

Ustawiam czas ciastka na zero, co w praktyce oznacza jego zniszczenie, gdyż 1.1.1970 minął daaaaaaaaaawno temu.
   Kolejne trzy metody będą tworzyły różne rodzaje sesji. Najpierw zaprezentuję kod dwóch z nich: do sesji botów, oraz sesji gości, gdyż nie wymagają one specjalnego omówienia.

      function bot_session(){
         $this -> session_id = '';
         $this -> session_user = 0;
         $this -> session_ip = $this -> ip;
         $this -> session_browser = $this -> browser;
         $this -> session_time = 0;
         $this -> userdata = array('ID' => 0, 'NAME' => '');
      } // end bot_session();

      function guest_session(){
         $this -> session_id = md5(uniqid($this->ip.time()));
         $this -> session_user = 0;
         $this -> session_ip = $this -> ip;
         $this -> session_browser = $this -> browser;
         $this -> type = GUEST_S;
         setcookie('my_session_id', $this->session_id, time()+SESSION_TIME);
         mysql_query('INSERT INTO sessions VALUES(''.$this->session_id.'', '0', ''.$this->ip.'', ''.mysql_real_escape_string($this->browser).'', ''.time().'')');
      } // end guest_session();

Kod ten w praktyce tylko wypełnia pola klasy predefiniowanymi danymi, a w przypadku gości dodatkowo dokonuje ustawienia ciastka i zrobienia wpisu w bazie. Ponadto w linijce pierwszej metody guest_session() można zobaczyć, jak wygląda w praktyce generowanie ID sesji.
   Dlaczego zdecydowałem się opisać osobno trzecią metodę, user_session()? Ponieważ nie będzie można tak po prostu stworzyć sobie tego rodzaju sesji. Tu znajdą się wszystkie mechanizmy odpowiedzialne za autoryzację podczas logowania, a sesja zostanie utworzona dopiero wtedy, gdy będą one prawdziwe! Z resztą... proszę zobaczyć:

      function user_session(){
         $this -> garbage_collector();

         $r = mysql_query('SELECT user_id FROM users WHERE user_name = ''.mysql_real_escape_string($_POST['login']).'' and user_password = ''.md5($_POST['password']).''');
         if($id = mysql_fetch_row($r)){
            if(strlen($this->session_id) == 32){
               mysql_query('UPDATE sessions SET session_user = '.$id[0].', session_time = ''.time().'' WHERE session_id = ''.$this->session_id.''');
               if(mysql_affected_rows() == 1){
                  setcookie('my_session_id', $this->session_id, time()+SESSION_TIME);
               }
            }else{
               $this -> session_id = md5(uniqid($this->ip.time()));
               setcookie('my_session_id', $this->session_id, time()+SESSION_TIME);
               mysql_query('INSERT INTO sessions VALUES(''.$this->session_id.'', ''.$id[0].'', ''.$this->ip.'', ''.mysql_real_escape_string($this->browser).'', ''.time().'')');
            }
            return 1;
         }
         return 0;
      } // end user_session();

Dopiero tutaj uruchamiamy metodę garbage_collector() usuwającą stare sesje. Prawdziwość danych sprawdzamy jednym zapytaniem - jeśli zwróci ono jakiś rekord, dane są prawdziwe i można:

  • Utworzyć nową sesję, gdy takowa nie istnieje (wyświetleniu formularza do logowania wcale nie musi towarzyszyć sesja!)
  • Aktualizować sesję już istniejącą.


   Ostatnia metoda ładnie posprząta po nieużywanych sesjach:

      function garbage_collector(){
         mysql_query('DELETE FROM sessions WHERE session_time < '.(time() - SESSION_TIME));
      } // end garbage_collector();
   }
?>

Teraz mała wskazówka dla osób pragnących napisać coś w rodzaju "Ostatniej wizyty zalogowanego użytkownika na stronie". Robi się to w ten sposób: do tabeli z użytkownikami dodaje się pole w stylu user_lastvisit, natomiast w ww. metodzie przed zapytaniem DELETE dodaje się jeszcze jedno, wyglądające mniej więcej tak:

UPDATE users, sessions SET user_lastvisit = sessions.session_time WHERE sessions.session_time < '.(time() - SESSION_TIME)

I po sprawie! W niektórych bazach można to zapytanie przerzucić do triggerów, oszczędzając tym samym kod PHP :).

   Składamy wszystko do kupy
Chwila, chwila! Przecież nasz kod w takiej postaci jest praktycznie bezużyteczny! Trzeba go jeszcze poskładać w całość. Kod dokonujący autoryzacji znajdzie się w pliku auth.php. Zdefiniowałem tu jedną funkcję - do_auth(), jednak jest ona potrzebna tylko mechanizmowi i nie musisz jej znać. Stworzyłem ją, gdyż zawarta w nim część kodu powtarzała się trzy razy w mechaniźmie i po prostu zostawienie tego w wersji pierwotnej zagmatwałoby kod. A tak wiadomo, że jak jest do_auth(), to tu nastąpi autoryzacja danych z formularza do logowania i ewentualne wyświetlenie błędu.

<?php
   
   function do_auth(){
      global $session;
      if($_GET['auth'] == 'login'){
         if(!$session -> user_session()){
            error('Nieprawidłowy login i/lub hasło');
            die();
         }else{
            header('Location: index.php');
            die();
         }
      }
   } // end do_auth();

Pierwszym krokiem robionym przez mechanizm jest stworzenie obiektu klasy session oraz obsługa sesji botów wyszukiwarek:

   $session = new session;
   $session -> spy_guest();
   
   if($session -> type == WEBBOT_S){
      if(defined('LOGIN_REQUIRED')){
         die('Access denied');
      }
      $session -> bot_session();

Gdy mamy do czynienia ze zwyczajną sesją, możemy spróbować załadować jej dane. Oczywiście boty nie mają dostępu do stron, do których wymaga się logowania, co widać powyżej.

   }elseif($session -> is_session()){   
      if($session -> load_session()){
         do_auth();

         if(defined('LOGIN_REQUIRED')){
            if($session -> type != USER_S){
               form_request();
               die();
            }
         }

         if($_GET['auth'] == 'logout'){
            $session -> delete_session();
            $session -> guest_session();
            header('Location: index.php');
         }

Strona powiadamia mechanizm, że potrzeba na nią logowania poprzez stałą LOGIN_REQUIRED. Gdy jest ona ustawiona, dokonuje się sprawdzenie, czy mamy do czynienia z sesją usera, a jeśli nie - wyświetlamy delikwentowi formularz. Tu także obsługujemy akcję wylogowywania: najpierw kasujemy sesję, a później tworzymy sesję gościa.
   Gdy nie udało się załadować danych sesji, wykonujemy takie akcje:

      }else{
         $session -> session_id = '';
         do_auth();

         if(defined('LOGIN_REQUIRED')){
            form_request();
            die();
         }
         $session -> guest_session();
      }

Krótko: to, co powyżej, tyle że bez obsługi wylogowywania i domyślnym tworzeniem sesji gościa.
   To samo wykonamy też, gdy sesja w ogóle nie istnieje. Wiadomo, że skoro formularz logowania może wyświetlić się bez sesji, również w takim przypadku musimy wziąć pod uwagę możliwość autoryzacji.

      do_auth();
      
      if(defined('LOGIN_REQUIRED')){
         form_request();
         die();
      }
      $session -> guest_session();
   }
?>

I... to w zasadzie tyle.

   Testujemy nasze sesje
Do przetestowania naszego majstersztyksu potrzebujemy jeszcze trochę kodu PHP: zestawu dodatkowych funkcji (połączenie z bazą, wyświetlanie formularza logowania, oraz błędu, a także prezentacja nagłówka w stylu "Witaj gościu"/"Witaj, jesteś zalogowany jako XXX"). Oto plik functions.php:

<?php

   function form_request(){
echo <<<EOF
<html>
<head>
<title>Zaloguj się</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-2">
</head>

<body>
<center>Zaloguj się</center>
<form method="post" action="index.php?auth=login">
Twój login: <input type="text" name="login"/><br/>
Twoje hasło: <input type="text" name="password"/><br/>
<input type="submit" value="Loguj!"/>
</form>
</body>
</html>
EOF;
   } // end form_request();
   
   function error($komunikat){
echo <<<EOF
<html>
<head>
<title>Bł&#177;d autoryzacji</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-2">
</head>

<body>
<center>Bł&#177;d</center>
Podczas autoryzacji wyst&#177;pił bł&#177;d: {$komunikat}
</body>
</html>
EOF;
   } // end error();

   function db_connect(){
      mysql_connect('localhost', 'root', '');
      mysql_select_db('artykuly');
   } // end db_connect();

   function print_header(){
      global $session;
      if($session -> type == GUEST_S){
         echo 'Witaj, go&#182;ciu<br/>';
      }else{
         echo 'Witaj, jeste&#182; zalogowany jako '.$session -> userdata['NAME'].' (<a href="index.php?auth=logout">Wyloguj</a>)<br/>';
      }
   } // end print_header();

   function db_close(){
      mysql_close();
   } // end db_close();

?>

Całość natomiast przetestujemy na pięciu stronach: INDEX, STRONA1, STRONA2, STRONA3 i STRONA4. Aby wejść na dwie ostatnie, będziemy się musieli zalogować, w przeciwnym razie dostaniemy formularz do logowania, a nie ich zawartość. Na początek plik index.php:

<?php
   require('functions.php');
   
   db_connect();
   
   require('sessions.php');
   require('auth.php');
   
   print_header();

   db_close();
?>
Witaj w INDEX
<ul>
   <li><a href="s1.php">Strona 1</a></li>
   <li><a href="s2.php">Strona 2</a></li>
   <li><a href="s3.php">Strona 3 (wymaga autoryzacji)</a></li>
   <li><a href="s4.php">Strona 4 (wymaga autoryzacji)</a></li>
</ul>

s1.php:

<?php

   require('functions.php');
   
   db_connect();
   
   require('sessions.php');
   require('auth.php');
   
   print_header();

   db_close();
?>
Witaj w STRONA 1
<ul>
   <li><a href="index.php">INDEX</a></li>
   <li><a href="s2.php">Strona 2</a></li>
   <li><a href="s3.php">Strona 3 (wymaga autoryzacji)</a></li>
   <li><a href="s4.php">Strona 4 (wymaga autoryzacji)</a></li>
</ul>

s2.php:

<?php

   require('functions.php');
   
   db_connect();
   
   require('sessions.php');
   require('auth.php');
   
   print_header();

   db_close();
?>
Witaj w STRONA 2
<ul>
   <li><a href="index.php">INDEX</a></li>
   <li><a href="s1.php">Strona 1</a></li>
   <li><a href="s3.php">Strona 3 (wymaga autoryzacji)</a></li>
   <li><a href="s4.php">Strona 4 (wymaga autoryzacji)</a></li>
</ul>

s3.php:

<?php

   define('LOGIN_REQUIRED', 1);

   require('functions.php');
   
   db_connect();
   
   require('sessions.php');
   require('auth.php');
   
   print_header();

   db_close();
?>
Witaj w STRONA 3
<ul>
   <li><a href="index.php">INDEX</a></li>
   <li><a href="s1.php">Strona 1</a></li>
   <li><a href="s2.php">Strona 2</a></li>
   <li><a href="s4.php">Strona 4 (wymaga autoryzacji)</a></li>
</ul>

s4.php:

<?php

   define('LOGIN_REQUIRED', 1);

   require('functions.php');
   
   db_connect();
   
   require('sessions.php');
   require('auth.php');
   
   print_header();

   db_close();
?>
Witaj w STRONA 4
<ul>
   <li><a href="index.php">INDEX</a></li>
   <li><a href="s1.php">Strona 1</a></li>
   <li><a href="s2.php">Strona 2</a></li>
   <li><a href="s3.php">Strona 3 (wymaga autoryzacji)</a></li>
</ul>

Przypominam, że aby wymusić konieczność zalogowania się na strony 3 i 4, na samym początku plików do ich obsługi zdefiniowałem stałą LOGIN_REQUIRED.

   Gratulacje, możesz teraz uruchomić skrypt i przetestować go! W bazie jest już zdefiniowany przykładowy użytkownik: "luser", a jego hasło to "ppp".

   Co dalej?
Sesje i autoryzacja to rozległy temat. Gdyby opisać go dokładnie w tym artykule, prawdopodobnie nie starczyłoby na to miejsca w bazie :). Ponadto, zazwyczaj w tym wypadku najlepiej jest stworzyć system stosowny do naszych potrzeb. Potrzebujemy grup? Dodajemy grupy. A może każdemu z użytkowników będzie można osobno definiować uprawnienia? Również nic trudnego. Wystarczy tylko chcieć.
   Specjalnie nie rozbudowałem powyższego skryptu do rozmiarów i możliwości systemów znanych z phpBB, czy InvisionBoard, byś mógł to uczynić sam, w ramach praktyki. Jednak mogę śmiało przypuszczać, że niezbyt będziesz chciał zgłębiać tajniki kodu ww. systemów, by znaleźć coś interesującego. Dlatego też co ciekawsze pomysły opisałem poniżej.

  • Autologowanie - możliwość jego konstrukcji jest w zasadzie wynikiem rozszerzenia mechanizmu kontroli prawdziwości sesji. Otóż wraz z jej ID wysyłane jest dodatkowe ciastko niosące serializowaną tablicę z kilkoma danymi kontrolnymi: ID profilu użytkownika, czasem ostatniej aktywności sesji. Jeśli dane te będą się zgadzały z tymi w bazie, to sesja jest poprawna. Ale gdyby tak przedłużyć czas życia tego ciastka np. na pół roku, oraz dodać pola z hasłem użytkownika oraz flagą autologowania, otrzymamy ww. mechanizm! Wtedy przy tworzeniu sesji gościa wystarczy sprawdzić, czy takowe ciastko istnieje, a jeśli tak, to czy dany delikwent chce się automatycznie logować. Gdy także to będzie prawdziwe, porównujemy jego ID i hasło z tym, co ma w bazie, po czym tworzymy sesję użytkownika, zamiast sesji gościa. I kłopot z głowy.
  • Szyfrowanie - Gdy dodamy drugie ciastko, opisane powyżej, warto pokusić się o jego zaszyfrowanie. Możemy w tym celu wykorzystać jeden z modułów PHP - mcrypt bądź napisać jakiś algorytm samemu.
  • Uprawnienia - to możemy rozwiązać na kilka sposobów: poprzez tworzenie grup użytkowników; poprzez nadawanie każdemu użytkownikowi z osobna innych praw, bądź też poprzez określenie na sztywno rangi. Tak więc ci z najwyższą rangą mogą wchodzić wszędzie, ci z trochę niższą prawie wszędzie, a ci z najniższą, nie wszędzie :). Tu pole do popisu jest naprawdę duże i w praktyce bardzo łatwo wpaść na coś nowego i odkrywczego.
  • Lista odwiedzających aktualnie stronę - Tu sprawa jest prosta. Wystarczy z tabeli sessions pobrać wszystkie aktywne sesje (czyli te, których czas aktywności jest większy, niż time() - SESSION_TIME.


   Zakończenie
Mam nadzieję, iż artykuł ten rozjaśnił wszystkie sprawy związane z konstruowaniem własnych systemów sesji oraz pokazał metodę, jak to w ogóle działa. Pytania - jak zawsze, proszę kierować na forum Webcity :).

Powrót

 
 
Twój domowy serwer!