Prije ovoga bacite oko na ovu objavu.
Kod je na github.
Cijeli postupak ide ovako:
- registracija korisnika
- korisnik pošalje email i šifru
- ako email ne postoji napravi novi korisnički račun
- prijava korisnika
- korisnik pošalje email i šifru
- ako email postoji – prijavi korisnika
Kod registracije unosimo email, šifru i user id (UUID) u bazu podataka.
Kad se korisnik prijavi šaljemo mu (http-only) browser-only cookie. Znači cookie do kojeg se ne može doći sa javascriptom u internet pregledniku. U sesiji (session) dodajmo ključ [“user-logged-on” => ‘f750204d-e73d-43e7-a5c2-cff967d233c6’].
Na ponovnoj posjeti, provjeravamo (preko cookie-ja) “user-logged-on” iz sesije.
Na odjavi, brišemo ključ sesije.
U phpMyAdmin (ili preko terminala) pravimo tablicu:
DROP DATABASE IF EXISTS ci_login_app; CREATE DATABASE ci_login_app; DROP USER IF EXISTS 'ci_login_app'@'localhost'; CREATE USER 'ci_login_app'@'localhost' IDENTIFIED BY 'ci_login_app'; GRANT ALL ON ci_login_app.* TO 'ci_login_app'@'localhost'; USE ci_login_app; CREATE TABLE users ( id int(11) NOT NULL AUTO_INCREMENT, email varchar(320) DEFAULT NULL, password varchar(256) DEFAULT NULL, uuid varchar(36) DEFAULT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Instaliramo CI4 i instaliramo libse:
composer create-project codeigniter4/appstarter ci_login_app cd ./ci_login_app composer require ramsey/uuid composer require respect/validation
Kopiramo env u .env
cp ./env ./.env
…i mjenjamo pod “Database” i “Enviroment”:
#-------------------------------------------------------------------- # ENVIRONMENT #-------------------------------------------------------------------- CI_ENVIRONMENT = development #... #-------------------------------------------------------------------- # DATABASE #-------------------------------------------------------------------- database.default.hostname = localhost database.default.database = ci_login_app database.default.username = ci_login_app database.default.password = ci_login_app database.default.DBDriver = MySQLi # database.default.DBPrefix =
Otvaramo dodatni tab u terminalu i startamo dev server:
php spark serve
Server je na:
http://localhost:8080
Pravimo “Users” controller i model:
php spark make:model Users --suffix php spark make:controller Users --suffix
Zatim pravimo view predloške:
touch ./app/Views/dashboard.php touch ./app/Views/login.php touch ./app/Views/logout.php touch ./app/Views/register.php
Unosimo rute (putanje) “./app/Config/Routes.php”:
//ispod: $routes->get('/', 'Home::index'); $routes->get('/login', 'UsersController::login'); $routes->post('/login-post', 'UsersController::loginPost'); $routes->get('/register', 'UsersController::register'); $routes->post('/register-post', 'UsersController::registerPost'); $routes->get('/dashboard', 'UsersController::dashboard'); $routes->get('/logout', 'UsersController::logout');
U naš “./app/Models/UsersModel.php” unosimo promjene:
<?php namespace App\Models; use CodeIgniter\Model; class UsersModel extends Model { protected $DBGroup = 'default'; protected $table = 'users'; protected $primaryKey = 'id'; protected $useAutoIncrement = true; protected $insertID = 0; protected $returnType = 'array'; protected $useSoftDeletes = false; protected $protectFields = true; protected $allowedFields = ['password','email','uuid']; // Dates protected $useTimestamps = false; protected $dateFormat = 'datetime'; protected $createdField = 'created_at'; protected $updatedField = 'updated_at'; protected $deletedField = 'deleted_at'; // Validation protected $validationRules = [ "password" => "required|min_length[8]|max_length[256]", "email" => "required|valid_email|min_length[5]|is_unique[users.email]", ]; protected $validationMessages = [ "password" => [ "required" => "Password is required", "min_length" => "Minimum length of password should be 8 chars", "max_length" => "Maximum length of password should be 256 chars", ], "email" => [ "required" => "Email needed", "valid_email" => "Please provide a valid email address" ], ]; protected $skipValidation = false; protected $cleanValidationRules = true; // Callbacks protected $allowCallbacks = true; protected $beforeInsert = []; protected $afterInsert = []; protected $beforeUpdate = []; protected $afterUpdate = []; protected $beforeFind = []; protected $afterFind = []; protected $beforeDelete = []; protected $afterDelete = []; }
U predloške unosimo: “./app/Views/login.php”
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Login</title> <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css"> </head> <body> <div class="w3-panel w3-blue-grey w3-animate-top info-text-conatiner" style="margin:0; display:none;"> <h3>Information!</h3> <p class="info-text"></p> </div> <div class="w3-card-4"> <header class="w3-container w3-blue"> <h1>Login</h1> </header> <div class="w3-container"> <br> <form class="w3-container"> <input class="w3-input email-input" type="text"> <label>Email</label> <input class="w3-input password-input" type="text"> <label>Password</label> </form> <br> <button class="w3-button w3-blue" onclick="login()"> Submit </button> <br> <br> </div> </div> </body> </html> <script> const login = async () => { const email = document.querySelector(".email-input"); const password = document.querySelector(".password-input"); const messageContainer = document.querySelector(".info-text-conatiner"); const messageContainerText = document.querySelector(".info-text"); if (email.value < 4) { console.log( "bad email" ); return null; } if (password.value < 4) { console.log( "bad password" ); return null; } const postData = new FormData(); postData.append("login-form-email", email.value); postData.append("login-form-password", password.value); try { const response = await fetch('/login-post', { method: 'POST', body: postData, }); const jsonUserData = await response.json(); messageContainer.style.display = 'block'; messageContainerText.innerHTML = jsonUserData.message; if (!jsonUserData.error) { window.location = '/dashboard'; } //console.log( jsonUserData.name ); } catch (error) { console.error(error); } } </script>
.app/Views/logout.php
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Logged off!</title> <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css"> </head> <body> <div class="w3-card-4"> <header class="w3-container w3-blue"> <h1>Logged off!</h1> </header> <div class="w3-margin w3-padding"> <p>User logged off.</p> <div class="w3-button w3-blue" onclick="window.location='/login'"> Go to login page. </div> <br><br> </div> </div> </body> </html>
./app/Views/register.php
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Register</title> <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css"> </head> <body> <div class="w3-panel w3-blue-grey w3-animate-top info-text-conatiner" style="margin:0; display:none;"> <h3>Information!</h3> <p class="info-text"></p> </div> <div class="w3-card-4"> <header class="w3-container w3-blue"> <h1>Register</h1> </header> <div class="w3-container"> <br> <form class="w3-container"> <input class="w3-input email-input" type="text"> <label>Email</label> <input class="w3-input password-input" type="text"> <label>Password</label> </form> <br> <button class="w3-button w3-blue" onclick="register()"> Submit </button> <br> <br> </div> </div> </body> </html> <script> const register = async () => { const email = document.querySelector(".email-input"); const password = document.querySelector(".password-input"); const messageContainer = document.querySelector(".info-text-conatiner"); const messageContainerText = document.querySelector(".info-text"); if (email.value < 4) { console.log( "bad email" ); return null; } if (password.value < 4) { console.log( "bad password" ); return null; } const postData = new FormData(); postData.append("register-form-email", email.value); postData.append("register-form-password", password.value); try { const response = await fetch('/register-post', { method: 'POST', body: postData, }); const jsonUserData = await response.json(); messageContainer.style.display = 'block'; messageContainerText.innerHTML = jsonUserData.message; console.log( jsonUserData.name ); } catch (error) { console.error(error); } } </script>
./app/Views/dashboard.php
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Dashboard</title> <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css"> </head> <body> <div class="w3-card-4"> <header class="w3-container w3-blue"> <h1>Dashboard</h1> </header> <div class="w3-margin w3-padding"> <p>Only logged user can see this.</p> <div class="w3-button w3-blue" onclick="window.location='/logout'"> Logout </div> <br><br> </div> </div> </body> </html>
I na kraju, unosimo u “./app/Controllers/UsersController.php”:
<?php namespace App\Controllers; use App\Controllers\BaseController; use App\Models\UsersModel; use Ramsey\Uuid\Uuid; use Respect\Validation\Validator as v; class UsersController extends BaseController { public function index() { // } public function login() { $session = session(); if ($session->has('loginId')) { return redirect()->to('/dashboard'); } return view("login"); } public function loginPost() { header('Content-type:application/json;charset=utf-8'); $session = session(); $email = $this->request->getVar("login-form-email"); $password = $this->request->getVar("login-form-password"); if (!v::email()->validate($email)) { http_response_code(500); echo json_encode([ 'error' => true, 'message' => 'Bad email format!', 'data' => null, ]); die(); } if (!v::alnum()->noWhitespace()->length(8, 40)->validate($password)) { http_response_code(500); echo json_encode([ 'error' => true, 'message' => 'Password must be alphanumeric 8-40 chars!', 'data' => null, ]); die(); } $usersModel = new UsersModel(); $emailExists = $usersModel->getWhere(["email" => $email]); $emailExistsResult = $emailExists->getResult(); if (!$emailExistsResult) { http_response_code(500); echo json_encode([ 'error' => true, 'message' => 'User email not found!', 'data' => null, ]); die(); } $passHash = $emailExistsResult[0]->password; $uuid = $emailExistsResult[0]->uuid; if (!password_verify($password, $passHash)) { http_response_code(500); echo json_encode([ 'error' => true, 'message' => 'Bad login password!', 'data' => null, ]); die(); } else { $session->set('loginId', $uuid); echo json_encode([ 'error' => false, 'message' => 'User login valid!', 'data' => $uuid, ]); die(); } http_response_code(500); echo json_encode([ 'error' => true, 'message' => 'Fail, internal server error.', 'data' => null, ]); die(); } public function register() { $session = session(); if ($session->has('loginId')) { return redirect()->to('/dashboard'); } return view("register"); } public function registerPost() { header('Content-type:application/json;charset=utf-8'); $session = session(); $email = $this->request->getVar("register-form-email"); $password = $this->request->getVar("register-form-password"); $uuid = Uuid::uuid4(); if (!v::email()->validate($email)) { http_response_code(500); echo json_encode([ 'error' => true, 'message' => 'Bad email format!', 'data' => null, ]); die(); } if (!v::alnum()->noWhitespace()->length(8, 40)->validate($password)) { http_response_code(500); echo json_encode([ 'error' => true, 'message' => 'Password must be alphanumeric 8-40 chars!', 'data' => null, ]); die(); } $passwordHash = password_hash($password, PASSWORD_BCRYPT, ['cost'=>12]); $usersModel = new UsersModel(); $emailExists = $usersModel->getWhere(["email" => $email]); $emailExistsResult = $emailExists->getResult(); if ($emailExistsResult) { http_response_code(500); echo json_encode([ 'error' => true, 'message' => 'Email already exists!', 'data' => null, ]); die(); } $insertUserId = $usersModel->insert([ 'email' => $email, 'password' => $passwordHash, 'uuid' => $uuid, ]); if ($insertUserId > 0) { echo json_encode([ 'error' => false, 'message' => 'User added.', 'data' => $uuid, ]); die(); } http_response_code(500); echo json_encode([ 'error' => true, 'message' => 'Fail, internal server error.', 'data' => null, ]); die(); } public function dashboard() { return view("dashboard"); } public function logout() { $session = session(); $session->remove('loginId'); return view('logout'); } }
Testiranje
Password polje sam namjerno ostavio vidljivo. Kada odemo na “/register” putanju dobijemo registracijsku formu:
![](https://jezgra.online/wp-content/uploads/2022/05/iooxstudio_register.jpg)
I vidimo unos u bazi podataka:
![](https://jezgra.online/wp-content/uploads/2022/05/iooxstudio_phpmyadmin.jpg)
Na “/login” imamo naravno login formu:
![](https://jezgra.online/wp-content/uploads/2022/05/iooxstudio_login.jpg)
I kada smo spojeni možemo na “/dashboard”.
![](https://jezgra.online/wp-content/uploads/2022/05/iooxstudio_dashboard.jpg)
I na kraju da odjavimo korisnika idemo na “/logout” rutu.
![](https://jezgra.online/wp-content/uploads/2022/05/iooxstudio_logoff.jpg)