2024. április 20., szombat

Gyorskeresés

PHPPOT User Login működése

Írta: | Kulcsszavak: PHP . HTML . CSS . Javascript . User . Login . System . Webfejlesztés . SQL . MySQL

[ ÚJ BEJEGYZÉS ]

Egy egyszerű PHP alapú felhasználó beléptetőrendszer file-jainak elemzése. Főleg magam részére, de publikusan, hátha másnak is hasznos lesz ez a leírás, esetleg ez alapján jobban megérti, hogyan is működik egy ilyen beléptető rendszer. Egyszerűsége végett könnyű megérteni a működését kezdő PHP-vel ismerkedők számára is.

1. /view/login-form.php
<html>
<head>
<title>User Login</title>
<link href="./view/css/style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div>
<form action="login-action.php" method="post" id="frmLogin" onSubmit="return validate();">
<div class="demo-table">

<div class="form-head">Login</div>
<?php
if(isset($_SESSION["errorMessage"])) {
?>
<div class="error-message"><?php echo $_SESSION["errorMessage"]; ?></div>
<?php
unset($_SESSION["errorMessage"]);
}
?>
<div class="field-column">
<div>
<label for="username">Username</label><span id="user_info" class="error-info"></span>
</div>
<div>
<input name="user_name" id="user_name" type="text"
class="demo-input-box">
</div>
</div>
<div class="field-column">
<div>
<label for="password">Password</label><span id="password_info" class="error-info"></span>
</div>
<div>
<input name="password" id="password" type="password"
class="demo-input-box">
</div>
</div>
<div class=field-column>
<div>
<input type="submit" name="login" value="Login"
class="btnLogin"></span>
</div>
</div>
</div>
</form>
</div>
<script>
function validate() {
var $valid = true;
document.getElementById("user_info").innerHTML = "";
document.getElementById("password_info").innerHTML = "";

var userName = document.getElementById("user_name").value;
var password = document.getElementById("password").value;
if(userName == "")
{
document.getElementById("user_info").innerHTML = "required";
$valid = false;
}
if(password == "")
{
document.getElementById("password_info").innerHTML = "required";
$valid = false;
}
return $valid;
}
</script>
</body>
</html>

- HTML form. Ez a form van illesztve minden jelszó által védett lapra, ha az user nincs bejelentkezve, ezzel találkozik minden lapon.
- A következő adatokat küldi a login-action.php akció fájlnak POST metódussal: Username (user_name ID-vel), Password (password ID-vel)
- $_SESSION["errorMessage"]-ben hibaüzenetet fogad, amit a login-action.php küld vissza, ha az User/Password nem található az adatbázisban, vagy bármi egyéb panasza van. Majd unseteli azt, hogy a következő frissítésnél már ne legyen benne a sessionban az error message.
- onSubmit eseménynél egy Javascript kóddal ellenőrzést végez. Megakadályozza a form adatok nélküli elküldését.
- Ha minden rendben és az adatok nem üresek, akkor elküldésre kerülnek a login akció fájlnak, ami feldolgozza azokat.

2. /login-action.php

<?php
namespace Phppot;

use \Phppot\Member;
if (! empty($_POST["login"])) {
session_start();
$username = filter_var($_POST["user_name"], FILTER_SANITIZE_STRING);
$password = filter_var($_POST["password"], FILTER_SANITIZE_STRING);
require_once (__DIR__ . "/class/Member.php");

$member = new Member();
$isLoggedIn = $member->processLogin($username, $password);
if (! $isLoggedIn) {
$_SESSION["errorMessage"] = "Invalid Credentials";
}
if (!empty($_SERVER['HTTP_REFERER']))
header("Location: ".$_SERVER['HTTP_REFERER']);
else
header("Location: ./index.php");
exit();
}

- Ez is ellenőrzi, hogy megérkeztek e a szükséges adatok a belépéshez, ha nem, akkor nem teszt semmit. Fehér lapot ad vissza. Ez főleg akkor lehetséges, ha direktben megnyitjuk a login-action.php fájlt a böngészőben, ekkor értelemszerűen semmilyen adatot nem kap a formtól, így egy fehér lapot kapunk eredményül.
- Ha vannak adatok a login submit folyamatból, akkor elindítja a sessiont, felveszi POST metódusból kapott változókat a $username $password változókba
- Meghívja az egyszer és mindenképp szükséges fájlt a /class/Member.php-t, amiben a processLogin funkció található.
- Új Member osztály példányt kreál $member változóban.
- $isLoggedIn értéke a Member.php processLogin funkciójából kinyert $username, $password értéket kapja, ennek értéke IGAZ vagy HAMIS lesz. Ha hamis akkor "Invalid Credentials"; értéket veszi fel a $_SESSION["errorMessage"] globális változóba. Ha igaz, akkor ezt a lépést kihagyva visszaküld minket oda ahonnan jöttünk vagy az index oldalra. Ekkor már be vagyunk lépve és van értéke a $_SESSION["userId"] szuperglobális változónak a felhasználó ID számával, azaz a belépés megtörtént.
- Ha végzett, refer oldal létezése esetén visszaküld oda ahonnan jöttünk, ellenkező esetben az index oldalra. Ha hibával akkor újra a login form jelenik meg, ami kiadja a $_SESSION["errorMessage"] értéket is, amit unsetel, ahogy fent írtam, ha eredménnyel akkor megjeleníti a session létezése esetén megjeleníthető dolgokat. Pl hogy Hurrá, sikerrel beléptél $user.

3. /class/Memeber.php

<?php
namespace Phppot;

use \Phppot\DataSource;

class Member
{

private $dbConn;

private $ds;

function __construct()
{
require_once "DataSource.php";
$this->ds = new DataSource();
}

function getMemberById($memberId)
{
$query = "select * FROM registered_users WHERE id = ?";
$paramType = "i";
$paramArray = array($memberId);
$memberResult = $this->ds->select($query, $paramType, $paramArray);

return $memberResult;
}

public function processLogin($username, $password) {
$passwordHash = md5($password);
$query = "select * FROM registered_users WHERE user_name = ? AND password = ?";
$paramType = "ss";
$paramArray = array($username, $passwordHash);
$memberResult = $this->ds->select($query, $paramType, $paramArray);
if(!empty($memberResult)) {
$_SESSION["userId"] = $memberResult[0]["id"];
return true;
}
}
}

- Ebben a fájlban található a login-action.php-ben kért processLogin funkció, ami feldolgozza az akció fájl által küldött usernevet és jelszót.
- Alkalmazza a \Phppot\DataSource-t, követeli egyszer a DataSource.php fájlt, amiben az adatbázishoz való kapcsolódási információk és előkészített adatbázis lekérdezési funkciók is találhatók.
- Ha nem üresen jön vissza a select lekérése, akkor $memberResult megkapja azt az ID értéket, amit az User bejelentkezési neve alapján az adatbázisban megtalál és ezt egyenlővé teszi a $_SESSION["userId"] -vel és a funkció visszatérési értékét Treue-ra (azaz IGAZ-ra) állítja
- $this->ds->select($query, $paramType, $paramArray); kéri a DataSource.php-tól a select funkciót az adatokkal, így kapja meg az adatokat a $memberResult

4. /class/DataSource.php

<?php
namespace Phppot;

/**
* Generic datasource class for handling DB operations.
* Uses MySqli and PreparedStatements.
*
* @version 2.3
*/
class DataSource
{

// PHP 7.1.0 visibility modifiers are allowed for class constants.
// when using above 7.1.0, declare the below constants as private
const HOST = 'localhost'; // Change to your own DATA

const USERNAME = 'DB-USERAME'; // Change to your own DATA

const PASSWORD = 'YOUR-DB-PASS'; // Change to your own DATA

const DATABASENAME = 'YOUR-DB-NAME'; // Change to your own DATA

private $conn;

/**
* PHP implicitly takes care of cleanup for default connection types.
* So no need to worry about closing the connection.
*
* Singletons not required in PHP as there is no
* concept of shared memory.
* Every object lives only for a request.
*
* Keeping things simple and that works!
*/
function __construct()
{
$this->conn = $this->getConnection();
}

/**
* If connection object is needed use this method and get access to it.
* Otherwise, use the below methods for insert / update / etc.
*
* @return \mysqli
*/
public function getConnection()
{
$conn = new \mysqli(self::HOST, self::USERNAME, self::PASSWORD, self::DATABASENAME);

if (mysqli_connect_errno()) {
trigger_error("Problem with connecting to database.");
}

$conn->set_charset("utf8");
return $conn;
}

/**
* To get database results
* @param string $query
* @param string $paramType
* @param array $paramArray
* @return array
*/
public function select($query, $paramType="", $paramArray=array())
{
$stmt = $this->conn->prepare($query);

if(!empty($paramType) && !empty($paramArray)) {
$this->bindQueryParams($stmt, $paramType, $paramArray);
}

$stmt->execute();
$result = $stmt->get_result();

if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
$resultset[] = $row;
}
}

if (! empty($resultset)) {
return $resultset;
}
}

/**
* To insert
* @param string $query
* @param string $paramType
* @param array $paramArray
* @return int
*/
public function insert($query, $paramType, $paramArray)
{
print $query;
$stmt = $this->conn->prepare($query);
$this->bindQueryParams($stmt, $paramType, $paramArray);
$stmt->execute();
$insertId = $stmt->insert_id;
return $insertId;
}

/**
* To execute query
* @param string $query
* @param string $paramType
* @param array $paramArray
*/
public function execute($query, $paramType="", $paramArray=array())
{
$stmt = $this->conn->prepare($query);

if(!empty($paramType) && !empty($paramArray)) {
$this->bindQueryParams($stmt, $paramType="", $paramArray=array());
}
$stmt->execute();
}

/**
* 1. Prepares parameter binding
* 2. Bind prameters to the sql statement
* @param string $stmt
* @param string $paramType
* @param array $paramArray
*/
public function bindQueryParams($stmt, $paramType, $paramArray=array())
{
$paramValueReference[] = & $paramType;
for ($i = 0; $i < count($paramArray); $i ++) {
$paramValueReference[] = & $paramArray[$i];
}
call_user_func_array(array(
$stmt,
'bind_param'
), $paramValueReference);
}

/**
* To get database results
* @param string $query
* @param string $paramType
* @param array $paramArray
* @return array
*/
public function numRows($query, $paramType="", $paramArray=array())
{
$stmt = $this->conn->prepare($query);

if(!empty($paramType) && !empty($paramArray)) {
$this->bindQueryParams($stmt, $paramType, $paramArray);
}

$stmt->execute();
$stmt->store_result();
$recordCount = $stmt->num_rows;
return $recordCount;
}
}

- Ez a fájl már további fájl meghívását nem követeli.
- Funkció az adatbázis kapcsolódásra, funkciók a select, insert SQL műveletekhez.

5. Index.php

<?php
session_start();
if(!empty($_SESSION["userId"])) {
require_once './view/dashboard.php';
} else {
require_once './view/login-form.php';
}
?>

Az Index lap egy egyszerű rövid példa a jelszóval védett lapok egyikére.
Ha nem üres $_SESSION["userId"] akkor a dashboardot hívja, ha nincs akkor a login formot.
Ezt el lehet játszani minden levédendő user protected lappal, így levédve a benne lévő tartalmat az illetéktelen felhasználók elől. Az User ID birtokában pedig különböző tartalmakat tudunk megjeleníteni a felhasználóknak.

Kommentben írjátok meg, hogy mennyire jó ez a megoldás, ez a folyamat az user beléptetésére, tudtok e jobbat, láttok e benne hibát stb... Köszönöm a figyelmet!

Hozzászólások

(#1) Savageboy


Savageboy
aktív tag

A PHP-ban van beépített password_hash() és password_verify() függvény, szerintem érdemesebb azokat használni, már csak azért is, mert alapból bcrypt-tel hashelnek, nem pedig MD5-tel, ami nem éppen biztonságos. A beépített függvény egyébként alapból salt-ot is használ, amit szintén nem láttam az általad ismertetett implementációban.

[ Szerkesztve ]

(#2) btz válasza Savageboy (#1) üzenetére


btz
addikt

Köszönöm az észrevételeket, nem az én scriptem amúgy, csak a tanulmányozása végett írtam le a működését, hogy egy helyen átlássam, hogyan is működik, mivel kezdő vagyok a PHP-ben már elég régóta.
Átolvastam az ajánlott manuálokat és tényleg érdemes használni az md5 helyett.

ⓑⓣⓩ

(#3) pelyib


pelyib
tag

Ha valamit meg akarsz erteni, akkor irjal ra teszteket :)
Unit + integration / functional.

További hozzászólások megtekintése...
Copyright © 2000-2024 PROHARDVER Informatikai Kft.