¿Cómo crear un login en PHP y MySQL en menos de una hora?

Juan Carlos G 2020-04-05
1

📌 Suscríbete 🤘

Hoy vamos hacer algo que estoy seguro te va a encantar y que has estado esperando si has seguido mi curso, hoy veremos como crear un Login en PHP y MySQL, en un vídeo que no dura ni una hora, aprenderás como limitar tus aplicaciones y comprenderás el manejo de sesiones en PHP.

 

📌 Suscríbete a mi canal y activa la campanita para que no te pierdas ningún video 🤘

 

¿Cómo haremos esto?

Bien lo primero que debes saber es que este post forma parte de mi curso de PHP y MySQL, en un capitulo anterior creamos un dashboard donde mostramos algunos indicadores y una gráfica.

Ahora bien, el objetivo de este nuevo vídeo es agregarle seguridad a ese dashboard que creamos y limitar el acceso, crearemos un Login desde cero y aprenderás a manejar las sesiones en una aplicación monolítica.

Entonces, debes ver el vídeo para que puedas ver paso a paso la creación del Login, a continuación te dejo los conceptos básicos que debes saber para poder entender correctamente:

 

¿Qué necesito para crear un Login en PHP?

Principalmente necesitamos lo siguiente:

  • Manejo de sesiones en PHP.
  • Te dejo este post donde aprenderás la teoría de la Programación Orientada a Objetos, lo vas a necesitar ya que usamos POO en este tutorial.
  • La maquetación web la haremos con Bootstrap, aquí te dejo este otro vídeo donde hacemos una página web completa.
  • Vamos a utilizar AJAX para comunicar Javascript y el back end en PHP.

Y listo creo que estos son los puntos más importantes, obviamente te invito a revisar todos los vídeos de este curso que con este ya son 8 capítulos y si te gustan suscríbete a mi canal de YouTube.

 

Pasos para crear nuestro Login

  • Primero es que clones el repositorio que tenemos en esta URL, o dale descargar aquí, te aviso que vamos avanzando y tal vez descargues versiones posteriores a las que te muestro en este vídeo, solo es cuestión que revises el código

 

https://github.com/EWebik/php-mysql-desde-cero.git

 

  • Crea los nuevos archivos como muestro en el vídeo
  • Crear tabla usuarios donde almacenaremos los usuarios que tendrán acceso a nuestro dashboard. Solo debes correr el query directamente en phpMyAdmin.

 

CREATE TABLE usuarios (
		id                  		INT(11)     UNSIGNED AUTO_INCREMENT PRIMARY KEY,
		nombre                      VARCHAR(45) NOT NULL DEFAULT '',
		correo                   	VARCHAR(45) NOT NULL DEFAULT '',
		password                    VARCHAR(45) NOT NULL DEFAULT '',
		fecha_alta                  datetime    NOT NULL DEFAULT CURRENT_TIMESTAMP,
		estado						boolean		NOT NULL DEFAULT 0
	)

 

  • Ahora solo debemos crear la lógica para la conexión a la base de datos y el manejo de sesiones.

 

Manejo de sesiones en PHP

El manejo de sesiones en PHP principalmente se utiliza para preservar o almacenar información que se utilizaremos en procesos subsiguientes o futuros al proceso actual.

Con las sesiones podemos guardar información de usuario en memoria y así poder identificarlo en cualquier memento.

En este ejemplo ocuparemos:

  • session_start, iniciar una nueva sesión o reanudar la existente
  • session_destroy, elimina la sesión
  • $_SESSION, almacena las variables de sesión

 

Manejando la sesión en una clase

La sesión la podemos manejar de diferentes maneras, a mi en lo personal me gusta mucho la POO así que siempre la uso, entonces como te muestro en el vídeo creamos la clase sesion.php.

 

<?php
include "bd.php";
class Sesion extends BD
{
    const SESION_INIT = TRUE;
    const SESION_NO_INIT = FALSE;

    //Operadores de resolución de ambito
    //self --> Hace referencia a la clase actual y se usa cuando accedemos a atributos y métodos const o static desde dentro de la clase
    //this --> Hace referencia al objeto actual y lo usamos en atributos o métodos no const y static
    //parent --> Temas de herencia, y lo usamos para acceder a atributos o static de una clase padre

    //Estado de la sesión
    private $edoSesion = self::SESION_NO_INIT;

    //Instancia de la clase
    private static $instancia;

    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Retorna la instancia de la sesión
     * Si la sesión no existe se inicializa
     */
    public static function getInstancia()
    {
        //isset -> Determina si una variable está definida y no es NULL
        if (!isset(self::$instancia)) {
            self::$instancia = new self;
        }
        self::$instancia->iniSesion();
        return self::$instancia;
    }

    /**
     * Inicializa o reinicializa la sesión
     * @retur bool 
     * true -> si la sesión se inicializa
     * false -> si no 
     */
    public function iniSesion()
    {
        //session_start — Iniciar una nueva sesión o reanudar la existente
        if ($this->edoSesion == self::SESION_NO_INIT) {
            $this->edoSesion = session_start();
        }
        return $this->edoSesion;
    }

    /**
     * Destruir la sesión
     */
    public function destruirSesion()
    {
        if ($this->edoSesion == self::SESION_INIT) {
            $this->edoSesion = !session_destroy();
            unset($_SESSION);
            return !$this->edoSesion;
        }
        return false;
    }

    /**
     * Almacena datos en la sesión
     * $instancia->attr = 'value';
     */
    public function __set($name, $value)
    {
        //$_SESSION --> variables de sesión

        $_SESSION = $value;
    }

    /**
     * Permite obtener datos de la sesión
     * echo $instancia->attr;
     */
    public function __get($name)
    {
        if (isset($_SESSION)) {
            return $_SESSION;
        }
    }

    /**
     * Validar una variable de sesión
     */
    public function __isset($name)
    {
        return isset($_SESSION);
    }


    /**
     * Eliminamos una variable de sesión
     */
    public function __unset($name)
    {
        //unset — Destruye una variable especificada
        unset($_SESSION);
    }
}

 

  • Ahora bien, la clase extiende de otra clase de nombre DB, esta clase nos permite conectarnos a una base de datos que necesitaremos para validar que el usuario existe en el sistema.

 

<?php
include get_include_path() . 'config.php';
class BD
{
    public $oConBD = null;

    public function __construct()
    {
        global $usuarioBD, $passBD, $ipBD, $nombreBD;
        $this->usuarioBD = $usuarioBD;
        $this->passBD = $passBD;
        $this->ipBD = $ipBD;
        $this->nombreBD = $nombreBD;
    }

    /**
     * Conexión BD por PDO
     */
    public function conBDPDO()
    {
        try {
            $this->oConBD = new PDO("mysql:host=" . $this->ipBD . ";dbname=" . $this->nombreBD, $this->usuarioBD, $this->passBD);
            return true;
        } catch (PDOException $e) {
            echo "Error al conectar a la base de datos: " . $e->getMessage() . "\n";
            return false;
        }
    }
}

 

  • Ahora si podemos crear nuestra clase usuarios.php donde administraremos el Loguin y también nos servirá en las clases siguientes.

 

<?php
include "sesion.php";
class Usuarios extends Sesion
{
    public function __construct()
    {
        parent::__construct();
        $this->sesion = null;
    }

    public function login($correo, $pass)
    {
        $query = " select nombre, correo from usuarios where correo= :correo and password= :pass and estado = 1 ;";
        $oUsuario = null;
        if ($this->conBDPDO()) {
            $pQuery = $this->oConBD->prepare($query);
            $pQuery->bindParam(':correo', $correo);
            $pQuery->bindParam(':pass', $pass);
            $pQuery->execute();
            $pQuery->setFetchMode(PDO::FETCH_ASSOC);
            while ($usuario = $pQuery->fetch()) {
                $oUsuario = new Usuario();
                $oUsuario->nombre = $usuario;
                $oUsuario->correo = $usuario;
            }
            $this->sesion = $this->getInstancia();
            if ($oUsuario != null) {
                $jDatos = json_encode($oUsuario);
                $this->sesion->__set("rolWeb",  $jDatos);
                return 200;
            } else {
                $this->sesion->__set("rolWeb",  null);
                return 401;
            }
        }
    }
}

class Usuario
{
    public $nombre = "";
    public $correo = "";
}

 

Maquetación de nuestro Login

En este punto ya tenemos las clases principales que manejarán el acceso desde el Back End, pero, en el Front también necesitamos hacer cambios para que limitemos el acceso por ejemplo, vamos a maquetar nuestro Login utilizando Bootstrap.

 

<!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>MySQL con PHP</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <link rel="stylesheet" href="http://localhost/ewebik/web/css/style.css">
</head>

<body>

    <div class="container">
        <div class="centrar">
            <form method="POST" action="http://localhost/ewebik/web/php/login.php">
                <img src="https://ewebik.com/static/img/logoN.png" class="img-fluid" />
                <div class="form-group">
                    <label for="idCorreo">Correo electrónico</label>
                    <input type="email" name="correo" class="form-control" id="idCorreo" aria-describedby="emailHelp" placeholder="Correo electrónico">
                    <small id="emailHelp" class="form-text text-muted">Valor no valido.</small>
                </div>
                <div class="form-group">
                    <label for="idPass">Contraseña</label>
                    <input type="password" name="pass" class="form-control" id="idPass" placeholder="Contraseña">
                </div>

                <button type="submit" class="btn btn-primary">Submit</button>
            </form>
            <div id="idMensaje" style="display: none;" class="alert alert-danger my-3" role="alert">
                <p>El Usuario y/o contraseña no es correcto.</p>
            </div>
        </div>
    </div>

    <script src="http://localhost/ewebik/web/js/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js@2.8.0"></script>
    <script>
        const queryString = window.location.search;
        if (queryString != "") {
            //?c=401
            const eCode = parseInt(queryString.replace("?c=", ""));
            if (eCode == 401) {
                document.getElementById("idMensaje").style.display = "block";
            }
        }
    </script>
</body>

</html>

 

Maquetación del LOGIN

 

Del código anterior destacamos el siguiente código, donde interceptamos la respuesta de error del servidor que veremos a continuación y mostramos un mensaje al usuario, tu puedes modificar la respuesta para adaptarla a tu proyecto.

 

 const queryString = window.location.search;
        if (queryString != "") {
            //?c=401
            const eCode = parseInt(queryString.replace("?c=", ""));
            if (eCode == 401) {
                document.getElementById("idMensaje").style.display = "block";
            }
        }

 

Creando el servicio que atenderá los request del Front End

Ya tenemos el Front y necesitamos comunicarlo con el Back End, para ello si viste en la maqueta utilizamos un tag form que cuenta con un método submit, al hacer clic en el botón este lanza un request al Back para realizar el Login.

 

          <form method="POST" action="http://localhost/ewebik/web/php/login.php">
                <img src="https://ewebik.com/static/img/logoN.png" class="img-fluid" />
                <div class="form-group">
                    <label for="idCorreo">Correo electrónico</label>
                    <input type="email" name="correo" class="form-control" id="idCorreo" aria-describedby="emailHelp" placeholder="Correo electrónico">
                    <small id="emailHelp" class="form-text text-muted">Valor no valido.</small>
                </div>
                <div class="form-group">
                    <label for="idPass">Contraseña</label>
                    <input type="password" name="pass" class="form-control" id="idPass" placeholder="Contraseña">
                </div>

                <button type="submit" class="btn btn-primary">Submit</button>
            </form>

 

El request lo atenderemos en el Back de la siguiente manera.

 

<?php
set_include_path("D:\\desarrollos\\xampp\\htdocs\\ewebik\\");
include "./cuenta/usuarios.php";
$oUsuarios = new Usuarios();
if (!empty($_POST)) {
    $correo = $_POST;
    $pass = $_POST;
    $lPassC = sha1(md5("ewebik" . $pass));
    //echo $lPassC;
    if ($oUsuarios->login($correo, $lPassC) == 200) {
        header("Location: http://localhost/ewebik/web/");
    } else {
        header("Location: http://localhost/ewebik/web/paginas/login/?c=401");
    }
} else {
    header("Location: http://localhost/ewebik/web/paginas/login/?c=401");
}

Donde:

  • $_POST, contiene la información del request
  • sha1 y md5 nos permiten encriptar la contraseña ya que nunca deberías almacenar este dato en tu base como texto plano.

 

Si el usuario no esta registrado o tenemos algún erro regresaremos en la URL los parámetros c=401 un código que representa que el usuario no esta autorizado.

 

header("Location: http://localhost/ewebik/web/paginas/login/?c=401");

 

Comprobando la sesión en las diferentes pantallas

Bien en este punto tu usuario ya debe estar logueado, pero no hemos limitado el acceso en la otras pantallas o URLs, que en este caso solo tenemos la de el Dashboard, para ello debemos ir al servicio que atiende los request del Dashboard y validar la sesión, por ejemplo:

 

<?php
set_include_path("D:\\desarrollos\\xampp\\htdocs\\ewebik\\");
include "./cuenta/usuarios.php";
include './mysql.php';
$oMysql = new MySQL();
$oUsuarios = new Usuarios();
$response = "";

if (!empty($_POST)) {
    $oUsuarios->sesion = $oUsuarios->getInstancia();
    $rolWeb = $oUsuarios->sesion->__get("rolWeb");
    if ($rolWeb) {
        http_response_code(200);


        $rq = $_POST;

        if ($rq == 1) {
            $response = $oMysql->getVendidos();
        } else if ($rq == 2) {
            $response = $oMysql->getAlmacen();
        } else if ($rq == 3) {
            $response = $oMysql->getIngresos();
        } else if ($rq == 4) {
            $response = $oMysql->getDatosGrafica();
        }

        echo $response;
    } else {
        header("Location: http://localhost/ewebik/web/paginas/login/");
        http_response_code(401);
    }
} else {
    header("Location: http://localhost/ewebik/web/paginas/login/");
    http_response_code(401);
}

 

Si vemos que la variable rolWeb existe quiere decir que hay una sesión valida, tu puedes agregar más validaciones como validar el permiso para saber si el usuario puede ver esta pantalla, en este caso como es un ejemplo sencillo no hay problema.

Por ultimo nos falta modificar el archivo index.js quien hace los request desde el Front para obtener los datos del Dashboard, necesitamos que lo primero que se envié sea un request que valide la sesión, por ejemplo:

 

function index() {
  this.ini = function() {
    console.log("Iniciando...");
    this.getSesion();
  };
  this.getSesion = function() {
    const _this = this;
    $.ajax({
      statusCode: {
        401: function() {
          window.location.href = "http://localhost/ewebik/web/paginas/login/";
        },
        200: function() {
          _this.getInidicadores();
          _this.getDatosGraficas();
        }
      },
      url: "php/servidor.php",
      method: "POST",
      data: {
        rq: "0"
      }
    });
  };
  this.getInidicadores = function() {
    //Vendidos
    $.ajax({
      statusCode: {
        404: function() {
          console.log("Esta página no existe");
        }
      },
      url: "php/servidor.php",
      method: "POST",
      data: {
        rq: "1"
      }
    }).done(function(datos) {
      //La lógica 3,000
      $("#idVendidos").text(parseFloat(datos).toLocaleString());
    });

    //Almacen
    $.ajax({
      statusCode: {
        404: function() {
          console.log("Esta página no existe");
        }
      },
      url: "php/servidor.php",
      method: "POST",
      data: {
        rq: "2"
      }
    }).done(function(datos) {
      //La lógica 3,000
      $("#idAlmacen").text(parseFloat(datos).toLocaleString());
    });

    //Ingresos
    $.ajax({
      statusCode: {
        404: function() {
          console.log("Esta página no existe");
        }
      },
      url: "php/servidor.php",
      method: "POST",
      data: {
        rq: "3"
      }
    }).done(function(datos) {
      //La lógica 3,000
      $("#idIngreso").text(parseFloat(datos).toLocaleString());
    });
  };
  this.getDatosGraficas = function() {
    $.ajax({
      statusCode: {
        404: function() {
          console.log("Esta página no existe");
        }
      },
      url: "php/servidor.php",
      method: "POST",
      data: {
        rq: "4"
      }
    }).done(function(datos) {
      //La lógica
      if (datos != "") {
        let etiquetas = new Array();
        let tVendidos = new Array();
        let tPrecio = new Array();
        let coloresV = new Array();
        let coloresP = new Array();
        var jDatos = JSON.parse(datos);

        var tablaDatos = document.createElement("tabla");
        tablaDatos.classList.add("table", "table-striped");
        var tr = document.createElement("tr");
        var th = document.createElement("th");
        th.innerText = "Fecha";
        tr.appendChild(th);
        th = document.createElement("th");
        th.innerText = "Ventas";
        tr.appendChild(th);
        th = document.createElement("th");
        th.innerText = "Precio";
        tr.appendChild(th);
        tablaDatos.appendChild(tr);

        for (let i in jDatos) {
          etiquetas.push(jDatos.fechaVenta);
          tVendidos.push(jDatos.totalVendidos);
          tPrecio.push(jDatos.totalPrecio);
          coloresV.push("#36004D");
          coloresP.push("679B6B");

          tr = document.createElement("tr");
          var td = document.createElement("td");
          td.innerText = jDatos.fechaVenta;
          tr.appendChild(td);

          td = document.createElement("td");
          td.innerText = parseFloat(jDatos.totalVendidos).toLocaleString();
          tr.appendChild(td);

          td = document.createElement("td");
          td.innerText = parseFloat(jDatos.totalPrecio).toLocaleString();
          tr.appendChild(td);

          tablaDatos.appendChild(tr);
        }

        var idCont = document.getElementById("idContTabla");
        idCont.appendChild(tablaDatos);

        var ctx = document.getElementById("idGrafica").getContext("2d");
        var myChart = new Chart(ctx, {
          type: "bar",
          data: {
            labels: etiquetas,
            datasets: [
              {
                label: "Ventas",
                data: tVendidos,
                backgroundColor: coloresV
              },
              {
                label: "Precios",
                data: tPrecio,
                backgroundColor: coloresP
              }
            ]
          }
        });
      }
    });
  };
}

var oIndex = new index();
setTimeout(function() {
  oIndex.ini();
}, 100);

 

Listo, ya tenemos un Login, hay elementos que en este resumen no están así que te recomiendo ver la clase en el vídeo y mirar el repositorio para que puedas acceder al código completo. No olvides en compartir y suscribirte a mi canal en YouTube.



https://ewebik.com

Juan Carlos G

Electrónica y diseño web


Durante años he desarrollado plataformas dedicadas al rastreo satelital y varios sitios web que se encuentran en la primera página de Google, y hoy quiero compartir contigo lo que se en tecnologías como Node JS, PHP, C# y Bases de datos, si quieres apoyarme sígueme en mis redes sociales y suscríbete a mi canal de YouTube.

@Puedes seguirme en mis redes

📌 Suscríbete 🤘