Strona używa cookies (ciasteczek). Dowiedz się więcej o celu ich używania i zmianach ustawień. Korzystając ze strony wyrażasz zgodę na używanie cookies, zgodnie z aktualnymi ustawieniami przeglądarki.    X

CakePHP w praktyce. Logowanie z funkcją 'zapamiętaj mnie na...'

Jako, że jest to mój pierwszy post - przedstawię się.
Kamil Falgowski, lat 22, pracuję w agencji reklamowej we Wrocławiu jako człowiek od PHP.

Niestety w pracy nie jest mi dane korzystać z różnych dobrodziejstw oddanych zupełnie za darmo dla ludu przez zespoły programistów pracujących nad aplikacjami przez wiele lat.
Jednak w swoich projektach nikt mi tego nie zabrania. I tu właśnie pojawia się tytułowe CakePHP.

Jest wiele innych framework'ów PHP, podobno lepszych, wygodniejszych i tak dalej, i tak dalej... Ale po co? CakePHP - podręcznikowe podejście do MVC, wygoda, jasne konwencje nazewnictwa. Czego chcieć więcej? No może jeszcze tego, że jak wpiszę coś w google to znajdę dziesiątki stron i forów na dane zagadnienie. Tak, to jest dostajemy w zestawie.

No to zaczynamy.
Co będzie nam potrzebne?

  • Apache - osobiście polecam xampp
  • Najnowsze wydanie CakePHP do ściągnięcia tutaj
  • Środowisko pracy, polecam Sublime Text 2
  • Chwilka wolnego czasu i trochę chęci.

Nie będę się tutaj rozpisywał nad założeniami modelu MVC, tylko w skrócie: model, view controller.
Model - część aplikacji odpowiadająca całej logice, np połączenia z bazą danych,
View - widok, czyli wszystko to co chcemy aby widział przeglądający,
Controller - swoisty klej, który ma za zadanie połączenie modelu z widokiem.

Konfiguracja CakePHP

Po skopiowaniu plików ściągniętych ze strony framework'a i wpisaniu w pasek adresu

localhost/nazwafolderuzcakephp
pojawi się powitalna strona, gdzie będą wypunktowane wszystkie sprawy którymi należy się zająć przed przystąpieniem do pracy.

Najważniejsza się konfiguracja bazy danych. Zrobimy to edytując plik

/app/Config/database.php
Powinien mieć on mniej więcej taką strukturę:<?php class DATABASE_CONFIG { public $default = array( 'datasource' => 'Database/Mysql', 'persistent' => false, 'host' => 'przykladowy.host', 'login' => 'login', 'password' => 'haslo', 'database' => 'nazwabazy', 'prefix' => '', 'encoding' => 'utf8', ); }

Table w bazie danych

Będziemy potrzebować tylko jednej tabeli w bazie danych. Mianowicie

users
I tutaj mamy pierwszą styczność z konwencjami nazewnictwa w CakePHP. Jeśli kontroler ma się nazywać Users, tabela w bazie danych powinna się nazywać tak samo, oczywiście nie musi, lecz zaoszczędzi nam to pisania zbędnych w tym momencie linii kodu. Natomiast model w tym wypadku musi nazywać się User, czyli liczba pojedyncza. Tyle na razie o konwencjach.

Niech tabela ma następujący układ: CREATE TABLE IF NOT EXISTS `users` ( `id` int(10) NOT NULL AUTO_INCREMENT, `username` varchar(255) NOT NULL, `name` varchar(255) NOT NULL, `password` varchar(255) NOT NULL, `email` varchar(255) NOT NULL, `last_login` datetime NOT NULL, `created` datetime NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `username` (`username`), UNIQUE KEY `email` (`email`) ) ENGINE=InnoDB DEFAULT CHARSET=latin2;

AppController

AppController jest to kontroler główny, czyli taki, po którym dziedziczyć będą wszystkie tworzone przez nas kontrolery. Znajduje się w

/app/Controller/AppController.php

Tutaj musimy dodać komponent wbudowany w CakePHP, czyli AuthComponent oraz CookieComponent.
Tak więc, uczyńmy to: class AppController extends Controller { public $components = array( 'Cookie', 'Auth' ); ...

Tym sposobem, AuthComponent będzie ładowany w każdym miejscu naszej aplikacji, na czym nam zależy, bo nie chcemy przecież, aby ktoś mógł wejść tam gdzie nie może.

W AppController musimy jeszcze zadbać o to jak ma się zachowywać ten komponent.
Posłuży nam do tego metoda

beforeFilter()
wykonująca się zawsze przed wykonaniem jakichkolwiek innych czynności.

class AppController extends Controller { ... public function beforeFilter() { $this->Auth->deny(); // z góry blokujemy dostęp do każdej strony if (!$this->Auth->loggedIn() && $this->Cookie->read('remember_me_cookie')) { //sprawdzamy czy użytkownik nie jest już zalogowany oraz czy istnieje ciasteczko pozwalające na zalogowanie się $cookie = $this->Cookie->read('remember_me_cookie'); $user = $this->User->find('first', array( // sprawdzamy czy w bazie istnieje użytkownik o loginie i haśle pozostawionym w ciasteczku 'conditions' => array( 'User.username' => $cookie['username'], 'User.password' => $cookie['password'] ) )); if ($user && !$this->Auth->login($user['User'])) { // jeśli te dane są nieprawidłowe przekierowanie na formularz logowania $this->redirect(array('controller' => 'users', 'action' => 'login')); } } $this->Auth->loginAction = array('controller' => 'users', 'action' => 'login'); // tutaj definiujemy akcje logowania $this->Auth->logoutRedirect = array('controller' => 'users', 'action' => 'login'); // akcja wylogowania $this->Auth->loginRedirect = array('controller' => 'index', 'action' => 'index'); // akcja wykonywana po poprawnym zalogowaniu się }

Tym sposobem obsługujemy cały proces logowania się

Model

Przyszedł czas na stworzenie modelu modułu do zarządzania użytkownikami.
Tworzymy plik

/app/Model/User.php

Tutaj przyszedł czas na zadbanie o poprawną walidacje wpisywanych danych przez użytkowników.

class User extends AppModel { public $validate = array( 'username' => array( 'rule1' => array( 'required' => true, 'rule' => 'notEmpty', 'message' => 'To pole jest wymagane' ), 'password' => array( 'required' => true, 'rule' => 'notEmpty', 'message' => 'To pole jest wymagane' ), );

Tym sposobem pola username oraz password będą wymagane podczas logowania.

Kontroler

Przyszedł czas na zaprogramowanie akcji które mają się wydarzyć podczas logowania.
Stworzymy metody login, logout potrzebne do obsługi tych czynności.

class UsersController extends AppController { ... public function login() { if ($this->Auth->loggedIn()) { // sprawdzamy czy już zalogowany a jeśli tak przekierowanie na stronę główną $this->redirect(array('controller' => 'index', 'action' => 'index')); } if ($this->request->is('post')) { // kontrolujemy czy zapytanie zostało wysłane metodą 'post' if ($this->Auth->login()) { // logujemy się if ($this->request->data['User']['remember_me'] == 1) { // jeśli w formularzu zaznaczyliśmy 'zapamiętaj mnie' niszczymy ewentualnie już istniejace ciasteczko oraz tworzymy nowe unset($this->request->data['User']['remember_me']); $this->request->data['User']['password'] = $this->Auth->password($this->request->data['User']['password']); $this->Cookie->write('remember_me_cookie', $this->request->data['User'], true, '2 weeks'); } $this->User->id = $this->Auth->user('id'); $this->User->saveField('last_login', date('Y-m-d H:i:s')); // zapisujemy czas logowania $this->redirect($this->Auth->redirect()); // przekierowanie na stronę główną } else { $this->Session->setFlash('Nieprawidłowy login lub hasło'); // jeśli nie powiodło się } } } public function logout() { if ($this->Auth->loggedIn()) { $this->Cookie->delete('remember_me_cookie'); // niszczymy ciasteczko $this->redirect($this->Auth->logout()); // logout } else { $this->redirect(array('action' => 'login')); } }

Prawda, że proste?

Widok

No i nadszedł czas na sam formularz do logowania.
Tworzymy plik

/app/View/Users/login.ctp
A w nim:

<h2 class="title">Zaloguj się na swoje konto</h2> <?php echo $this->Form->create('User'); echo $this->Form->input('username'); echo $this->Form->input('password'); echo $this->Form->input('remember_me', array('label' => 'Zapamiętaj mnie', 'type' => 'checkbox')); echo $this->Form->end(array('label' => 'Zaloguj się')); ?>

Tutaj wykorzystaliśmy Form Helper, dzięki któremu tworzymy formularz zgodny z konwencjami nazewnictwa oraz automatycznie dodane zostają dane typu maxlength podane przy tworzeniu tabeli w bazie danych.

Na zakończenie

Mam nadzieje, że poradnik się komuś przyda. Jeśli coś jest mało jasne, zapraszam do dyskusji .

Pozdrawiam i dziękuję. 

internet bezpieczeństwo programowanie

Komentarze

0 nowych
matiit   7 #1 22.05.2013 19:25

Polecam Laravel :) BTW: pierwsza tu osoba, którą znam na żywo :>

matiit   7 #2 22.05.2013 19:32

A co do dyskusji - o ile i tak piszesz w php projekty dla siebie i nie martwisz się tym, że masz za starą wersję PHP na serwerze itd, to fajnie jest używać

> echo $this->Form->end(['label' => 'Zaloguj się']);

Zamiast

> echo $this->Form->end(array('label' => 'Zaloguj się'));

i beforeFilter() to metoda, nie funkcja :)

Nie znam CakePHP, ale jak widzę jest bardzo podobny do Laravel w wersji 3.

alucosoftware   7 #3 22.05.2013 19:46

Uuuuuuu... hasło pozostawiasz w ciasteczku? Nieładnie :)

B.Andy   4 #4 22.05.2013 19:46

Hmm, jak działa to zapamiętywanie użytkownika? Mam nadzieję, że źle kod odczytałem, że zapisujesz ciasteczko z danymi logowania? Szukam i szukam, gdzie hasło jest hashowane?

btw. w tabeli masz kodowanie latin2, w kodzie utf-8 ;-)

knyku   7 #5 22.05.2013 20:03

tak tak, metoda :) mój błąd.

hasło jest zachaszowane już bazie. Framework wymusza podczas instalacji podanie soli do chyba, nie jestem pewien, sha-256 i wtedy w modelu jeszcze jest potrzebna metoda beforeSave() gdzie własnie to hasło haszujemy, więc w ciasteczku jest zahashowane, posolone hasło

flaszer   10 #6 22.05.2013 20:14

Prosto i przejrzyście. Trzeba przyznać, że Cake to całkiem dobry framework ;)

marrrysin   6 #7 23.05.2013 00:40

Jak robisz coś większego, to ma to pewnie sens, ale do prostego skryptu np. backupu serwera po ftp wystarczy kilka własnych funkcji i też wszystko jest ładnie i przejrzyście. A hasło/logowanie obsługuję na sesji, bez ciasteczek.

Ale chciałbym napisać bazę danych dla magazynu drukarni i tutaj już myślę zastosowanie frameworka miałoby sens. Zupełnym pójściem na łatwiznę byłoby zaprzśgnięcie np. Wordpressa do obsługi wszystkiego i też by działało, ale chcę napisać coś sam "prawie od zera".

Jest na ten framework jakaś gotowa nakładka (nie wiem jak to nazwac - view? tak? ) z twitter bootstrap? bo uwielbiam tak tworzyć strony, a do zaplecz nadaje się wręcz idealnie, ze swoimi czytelnymi formularzami, paskami itd...

Czajo   10 #8 23.05.2013 07:03

czemu nie napisałeś tego w tamtym roku? Wtedy facet od php kazał nam z palca pisać MVC bez żadnych frameworków

knyku   7 #9 23.05.2013 08:23

@marrrysin
nakładki niby są, ale z tego co patrzyłem to są średnie. ja osobiście samemu porobiłem sobie helpery korzystające z bootstrapa, mogę za jakiś czas napisać na ten temat

Thuridr   4 #10 23.05.2013 08:28

Świetny post, czekam na więcej :)

Jim1961   7 #11 23.05.2013 12:30

IMO:
- coś dziwna ta konwencja - katalogi powinny się nazywać "Controllers", "Views" itp., a nie liczba pojedyńcza
- trzymanie hasła bądź hashu hasła w ciasteczka - można to lepiej zrobić
- czy to $this->Form->... zawiera już walidację?? bo coś mi sie wydaje, że same pola
- macie coś takiego jak migracje?

jeszcze pytanie ogólne:
po co define('DS', DIRECTORY_SEPARATOR); (src: github / index) skoro "/" działa wszędzie poprawnie - a przynajmniej ja się nie spotkałem

@flaszer:
Laravel i Yii są IMHO bardziej przejrzyste

knyku   7 #12 23.05.2013 15:38

> - coś dziwna ta konwencja - katalogi powinny się nazywać "Controllers", "Views" itp., a nie liczba pojedyńcza
możliwe że od tego ze pliku z kontrolerem nie nazywasz XxxControllers.php
> - trzymanie hasła bądź hashu hasła w ciasteczka - można to lepiej zrobić
chętnie się dowiem (to nie było ironiczne)
> - czy to $this->Form->... zawiera już walidację?? bo coś mi sie wydaje, że same pola
dzięki takiej konstrukcji nazwy pól są takie jak wymaga walidacja którą mamy w modelu
> - macie coś takiego jak migracje?
szczerze mówiąc pierwszy raz słyszę takie pojecie odnośnie programowania, możesz wyjaśnić ?

co do stałej - lepiej dmuchać na zimne IMO, zresztą w środowiskach UNIX'owych różnie bywa chyba

Jim1961   7 #13 23.05.2013 16:03

@knyku
- skoro katalog zawiera kontrolery to powinien nazywać się "kontrolery" - w tłumaczeniu na angielski oczywiście; odpowiednio Modele, Widoki ... twórcy Cake chcieli być wyjątkowi spośród frameworków MVC; zapewne można to łatwo nadpisać (przynajmniej w Yii), ale chodzi o domyślne ustawienie
- można zrobić osobną tabelę w bazie na tokeny zapisywane w ciasteczkach, token byłby per urządzenie i per user unikalny, a użytkownik mógłby mieć w profilu liste urządzeń na jakich jest zapamiętany (z możliwością usunięcia) --> hasło nie leci w żadnej postaci do urządzenia
- czyli mam rozumieć, że z tego generowany jest już np. komunikat o pustym polu albo zbyt krótkim ciągu znaków??
- to bardziej z dziedziny oprogramowania/deweloperki - pozwala na zarządzanie zmianami w bazie w czasie rozwoju aplikacji już po jego opublikowaniu - googluj :]

właśnie na UNIX to zawsze jest "/", tylko windows ma domyślnie "\" i tu może być problem, bo np. "\n" to znak nowej lini; ja używam "/" wszędzie i niema nigdy problemów

knyku   7 #14 23.05.2013 17:42

powiem tak, nie widzę problemu w nazwaniu folderów w liczbie pojedynczej, tak jest i tyle. nawet nie patrze się na nazwę folderu jak coś z nim robię, widzę con... i już wiem co to jest.

tokeny wykorzystuje do przypominania hasła.
dla mało 'ważnych' aplikacji, myślę że ciasteczko z hasłem, które i tak jest zahashowane wystarczy. oczywiście dla aplikacji trzymających dane osobowe itpe, ok, ale w takim przypadku, w ogóle bym nie polecał funkcji zapamiętywania logowania.
oczywiście, możemy zrobić zapamiętywanie urządzeń czy IP, tylko pytanie czy to ma sens dla aplikacji typu CMS, czy jakiś mały serwis ?

tak. jest dodawany atrybut typu required, czy pole typu e-mail, a poza tym po wysłaniu formularza, np dla IE, czy nieprawidłowej walidacji email w Chromowym WebKicie, jest przetwarzany po stronie serwera i ewentualnie zwraca standardowe, lub też tak jak ja ustawiłem swoje komunikaty.

co do DS, ale czemu miałbyś używać \n w ścieżkach ? nie widzę powodu. a tak jest zachowana maksymalna przenośność.

  #15 23.05.2013 17:55

"oczywiście, możemy zrobić zapamiętywanie urządzeń czy IP, tylko pytanie czy to ma sens dla aplikacji typu CMS, czy jakiś mały serwis ? "

Tak, jak najbardziej.
W ciachu możesz trzymać np. identyfikator sesji... i tyle.
A w bazie identyfikatory, IP, user agenta no i oczywiście id użytkownika jakiemu jest dany identyfikator przypisany. Przy każdej stronie trzeba sprawdzać IP, user agenta, identyfikator, a potem go zmieniać (coś jak regenerate z funkcji wbudowanych).

Autor edytował komentarz.
Jim1961   7 #16 23.05.2013 18:00

@knyku
- też nie widzę problemu, tylko mnie to dziwi ... aczkolwiek podstawy angielskiego miałem już dawno ;)
- wiem, że nie polecane; ale wzrasta UX i czasem klient wymaga; pokazałem jak to można zrobić lepiej; IMHO bezpieczeństwo zawsze ma sens;

no właśnie ta "maksymalna przenośność" mnie zastanawia, dla mnie maksymalna przenośność to "/" wszędzie i bez wyjątku ... ale co ja tam wiem

Jim1961   7 #17 23.05.2013 18:05

@lukasamd
jakoś się wzbraniam od weryfikowania po IP - w dobie mobilnego i darmowego internetu, to IP co minutę może się zmieniać ... mógłbym prosić o coś więcej

  #18 23.05.2013 19:34

@Jim1961:
Nie musimy badać całego IP, możemy ograniczyć się do trzech oktetów.

Songokuu   14 #19 23.05.2013 19:41

@knyku

Przez Twój avatar nie będę mógł spać w nocy...
Horror z dzieciństwa powrócił...

Autor edytował komentarz.
Jim1961   7 #20 23.05.2013 19:43

@lukasamd
I to jest myśl :]

knyku   7 #21 23.05.2013 22:53

@Songokuu
Buka zawsze z nami!

  #22 18.07.2013 16:37

Dzięki serdeczne za ten tutorial, przyda mi się do jednego projektu. Z tego co widzę, od standardowego logowania w cake'u różni się tylko zapisanie i kasowanie odpowiedniego cookie?

Lewandowski-pl   5 #23 15.02.2014 23:27

W AppController w komponentach poza Cookie i Auth trzeba dołączyć jeszcze Session.
Nie wiem, czy to winna nowszej wersji CakePHP czy drobnego niedopatrzenia.

Co do hasła w ciasteczku... tak, to bardzo zły pomysł. Nie szkodzi, że zahaszowane i posolone. Im mniej danych po stronie użytkownika tym lepiej.

Wiem, że dyskusja już trochę stara, ale:
@Jim1961, mi się wydaje, że chodziło twórcom nie o coś w stylu:
/ludzie/kowalski.php
/rzeczy/talerz.php
tylko
/człowiek/kowalski.php
/rzecz/talerz.php
oznajmiając, że kowalski.php to człowiek i talerz.php to rzecz, a nie, że należą do większego zbioru. Mam nadzieję, że czytający dobrze mnie zrozumieją.

  #24 30.05.2014 15:30

Dodaj informację w którym miejscu wrzucamy folder z cakephp - w folderze xampp -> hdocs

  #25 06.08.2014 19:20

Fakt laravel jest masakra :) cake to przeżytek