Registro de usuario y verificación de correo electrónico PHP y MySQL

Ya escribí una publicación de blog sobre la creación de un sistema completo de registro e inicio de sesión de usuario utilizando PHP y MySQL, pero no incluí la verificación por correo electrónico.

Este tutorial es un poco como una actualización del tutorial anterior. Además de poder registrarse, iniciar sesión y cerrar sesión en su cuenta, un usuario también recibirá un correo electrónico de verificación a su dirección de correo electrónico con un enlace donde puede hacer clic y verificar su dirección de correo electrónico.

En la mayoría de las aplicaciones que va a crear, es importante agregar una función de verificación de correo electrónico por muchas razones: es posible que desee enviar un correo electrónico más tarde al usuario y asegurarse de que lo verá; o el usuario podría olvidar su contraseña y necesitar restablecerla, y para hacer esto, tendremos que enviarle por correo electrónico un enlace de restablecimiento de contraseña; hay muchas otras razones, pero entiendes el punto.

En este sistema que estamos construyendo hoy, los usuarios que no hayan verificado su correo electrónico no podrán realizar ciertas acciones (solo usaré una demostración simple como ver un botón. Solo los usuarios verificados podrán ver ese botón).

Para comenzar, cree un nuevo proyecto PHP llamado verificar-usuario y en esta carpeta, cree dos archivos: signup.php y login.php.

signup.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">

  <!-- Bootstrap CSS -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css" />
  <link rel="stylesheet" href="main.css">
  <title>User verification system PHP</title>
</head>
<body>
  <div class="container">
    <div class="row">
      <div class="col-md-4 offset-md-4 form-wrapper auth">
        <h3 class="text-center form-title">Register</h3>
        <form action="signup.php" method="post">
          <div class="form-group">
            <label>Username</label>
            <input type="text" name="username" class="form-control form-control-lg" value="<?php echo $username; ?>">
          </div>
          <div class="form-group">
            <label>Email</label>
            <input type="text" name="email" class="form-control form-control-lg" value="<?php echo $email; ?>">
          </div>
          <div class="form-group">
            <label>Password</label>
            <input type="password" name="password" class="form-control form-control-lg">
          </div>
          <div class="form-group">
            <label>Password Confirm</label>
            <input type="password" name="passwordConf" class="form-control form-control-lg">
          </div>
          <div class="form-group">
            <button type="submit" name="signup-btn" class="btn btn-lg btn-block">Sign Up</button>
          </div>
        </form>
        <p>Already have an account? <a href="login.php">Login</a></p>
      </div>
    </div>
  </div>
</body>
</html>

Es solo un simple archivo HTML / CSS. Lo único que vale la pena señalar es que estamos usando el marco CSS Bootstrap 4 para diseñar nuestra página. Puede usar cualquier otro marco de estilo de su elección o escribir su propio CSS si lo desea.

Inmediatamente después de Bootstrap CSS, incluimos un main.css archivo para un estilo personalizado. Creemos ese archivo ahora. En la carpeta raíz de la aplicación, cree un archivo llamado main.css.

main.css:

@import url('https://fonts.googleapis.com/css?family=Lora');
li { list-style-type: none; }
.form-wrapper {
  margin: 50px auto 50px;
  font-family: 'Lora', serif;
  font-size: 1.09em;
}
.form-wrapper.login { margin-top: 120px; }
.form-wrapper p { font-size: .8em; text-align: center; }
.form-control:focus { box-shadow: none; }
.form-wrapper {
  border: 1px solid #80CED7;
  border-radius: 5px;
  padding: 25px 15px 0px 15px;
}
.form-wrapper.auth .form-title { color: #007EA7; }
.home-wrapper button,
.form-wrapper.auth button {
  background: #007EA7;
  color: white;
}
.home-wrapper {
  margin-top: 150px;
  border-radius: 5px;
  padding: 10px;
  border: 1px solid #80CED7;
}

En la primera línea de este archivo, estamos importando y usando algunas fuentes de Google para hacer que nuestras fuentes se vean más hermosas.

Ahora vaya al archivo login.php y haga algo similar.

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">
  <!-- Bootstrap CSS -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css" />
  <link rel="stylesheet" href="main.css">
  <title>User verification system PHP - Login</title>
</head>
<body>
  <div class="container">
    <div class="row">
      <div class="col-md-4 offset-md-4 form-wrapper auth login">
        <h3 class="text-center form-title">Login</h3>
        <form action="login.php" method="post">
          <div class="form-group">
            <label>Username or Email</label>
            <input type="text" name="username" class="form-control form-control-lg" value="<?php echo $username; ?>">
          </div>
          <div class="form-group">
            <label>Password</label>
            <input type="password" name="password" class="form-control form-control-lg">
          </div>
          <div class="form-group">
            <button type="submit" name="login-btn" class="btn btn-lg btn-block">Login</button>
          </div>
        </form>
        <p>Don't yet have an account? <a href="signup.php">Sign up</a></p>
      </div>
    </div>
  </div>
</body>
</html>

En su navegador, vaya a http: //localhost/cwa/verify-user/signup.php y verá un hermoso formulario de registro (lo mismo para iniciar sesión). Ignore los errores en los campos de entrada, lo arreglaremos pronto.

Por ahora, configuremos la base de datos. Crea una base de datos llamada verificar-usuario y en esta base de datos, cree un usuarios tabla con atributos de la siguiente manera:

CREATE TABLE `users` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `username` varchar(100) NOT NULL,
 `email` varchar(100) NOT NULL,
 `verified` tinyint(1) NOT NULL DEFAULT '0',
 `token` varchar(255) DEFAULT NULL,
 `password` varchar(255) NOT NULL,
 PRIMARY KEY (`id`)
)

Nada inusual excepto, tal vez, el simbólico y el verificado campos, que explicaré en un momento.

Ahora comenzamos con la lógica de registro real. Por lo general, me gusta referirme a la parte lógica de mi aplicación como controladores y eso es lo que haré aquí. En la carpeta raíz del proyecto, cree una carpeta llamada controladores y dentro de los controladores, crea un archivo llamado authController.php.

<?php
session_start();
$username = "";
$email = "";
$errors = [];

$conn = new mysqli('localhost', 'root', '', 'verify-user');

// SIGN UP USER
if (isset($_POST['signup-btn'])) {
    if (empty($_POST['username'])) {
        $errors['username'] = 'Username required';
    }
    if (empty($_POST['email'])) {
        $errors['email'] = 'Email required';
    }
    if (empty($_POST['password'])) {
        $errors['password'] = 'Password required';
    }
    if (isset($_POST['password']) && $_POST['password'] !== $_POST['passwordConf']) {
        $errors['passwordConf'] = 'The two passwords do not match';
    }

    $username = $_POST['username'];
    $email = $_POST['email'];
    $token = bin2hex(random_bytes(50)); // generate unique token
    $password = password_hash($_POST['password'], PASSWORD_DEFAULT); //encrypt password

    // Check if email already exists
    $sql = "SELECT * FROM users WHERE email='$email' LIMIT 1";
    $result = mysqli_query($conn, $sql);
    if (mysqli_num_rows($result) > 0) {
        $errors['email'] = "Email already exists";
    }

    if (count($errors) === 0) {
        $query = "INSERT INTO users SET username=?, email=?, token=?, password=?";
        $stmt = $conn->prepare($query);
        $stmt->bind_param('ssss', $username, $email, $token, $password);
        $result = $stmt->execute();

        if ($result) {
            $user_id = $stmt->insert_id;
            $stmt->close();

            // TO DO: send verification email to user
            // sendVerificationEmail($email, $token);

            $_SESSION['id'] = $user_id;
            $_SESSION['username'] = $username;
            $_SESSION['email'] = $email;
            $_SESSION['verified'] = false;
            $_SESSION['message'] = 'You are logged in!';
            $_SESSION['type'] = 'alert-success';
            header('location: index.php');
        } else {
            $_SESSION['error_msg'] = "Database error: Could not register user";
        }
    }
}

// LOGIN
if (isset($_POST['login-btn'])) {
    if (empty($_POST['username'])) {
        $errors['username'] = 'Username or email required';
    }
    if (empty($_POST['password'])) {
        $errors['password'] = 'Password required';
    }
    $username = $_POST['username'];
    $password = $_POST['password'];

    if (count($errors) === 0) {
        $query = "SELECT * FROM users WHERE username=? OR email=? LIMIT 1";
        $stmt = $conn->prepare($query);
        $stmt->bind_param('ss', $username, $password);

        if ($stmt->execute()) {
            $result = $stmt->get_result();
            $user = $result->fetch_assoc();
            if (password_verify($password, $user['password'])) { // if password matches
                $stmt->close();

                $_SESSION['id'] = $user['id'];
                $_SESSION['username'] = $user['username'];
                $_SESSION['email'] = $user['email'];
                $_SESSION['verified'] = $user['verified'];
                $_SESSION['message'] = 'You are logged in!';
                $_SESSION['type'] = 'alert-success';
                header('location: index.php');
                exit(0);
            } else { // if password does not match
                $errors['login_fail'] = "Wrong username / password";
            }
        } else {
            $_SESSION['message'] = "Database error. Login failed!";
            $_SESSION['type'] = "alert-danger";
        }
    }
}

Si ha seguido mis tutoriales anteriores, nada en este archivo debería ser nuevo para usted. Pero por el bien de los principiantes, voy a explicar un poco.

Lo primero es que estamos comenzando la sesión usando session_start () ya que necesitaremos almacenar la información del usuario registrado en la sesión. Después de iniciar la sesión, estamos inicializando el $ nombre de usuario y $ correo electrónico variables que estamos usando en nuestros formularios, y también las $ errores matriz que contendrá los errores de validación de nuestro formulario.

A continuación, nos conectamos a la base de datos. Los dos siguientes Si Las declaraciones que siguen son, respectivamente, el código que se ejecuta cuando el usuario hace clic en los botones de registro o de inicio de sesión. En el caso de registrarse, verificamos que todos los campos obligatorios se hayan completado correctamente y solo entonces procedemos a guardar al usuario en la base de datos. También estamos generando una simbólico (una cadena aleatoria única) y guardándola con el usuario como atributo. Esto se utilizará para verificar el correo electrónico del usuario. Más sobre esto más tarde.

Dado que nuestro archivo authController.php es responsable del registro y el inicio de sesión, tenemos que incluirlo en la parte superior de las páginas signup.php y login.php porque es allí donde se envían los datos del formulario. Al igual que:

signup.php y login.php (en la parte superior):

<?php include 'controllers/authController.php' ?>

Si hay algún mensaje de error en el $ errores matriz, necesitamos mostrarlos en el formulario. Para hacer eso, agregue esta declaración if dentro de su formulario directamente debajo del título del formulario tanto para las páginas de registro como para las de inicio de sesión.

<!-- form title -->
<h3 class="text-center form-title">Register</h3> <!-- or Login -->

<?php if (count($errors) > 0): ?>
  <div class="alert alert-danger">
    <?php foreach ($errors as $error): ?>
    <li>
      <?php echo $error; ?>
    </li>
    <?php endforeach;?>
  </div>
<?php endif;?>

Si no hay errores, nuestro script procederá a guardar al usuario en la base de datos. Después de guardar al usuario en la base de datos, lo registramos inmediatamente. En nuestro caso, iniciar la sesión de un usuario significa almacenar sus datos en la sesión y eso es lo que acabamos de hacer.

En este punto, ya puede registrarse e incluso iniciar sesión como usuario. Pero después de iniciar sesión, será redirigido a la página index.php que no existe. Lo crearemos pronto.

En el authController.php, estamos almacenando mensaje y escribe variables en la sesión que se mostrarán tan pronto como el usuario haya iniciado sesión. mensaje es el texto real del mensaje mientras que el escribe es la clase de estilo Bootstrap que formateará el mensaje con los colores apropiados dependiendo de la escribe valor.

Este mensaje se muestra después de que el usuario ha iniciado sesión y se muestra en el archivo index.php. Creemos ese archivo ahora en la carpeta raíz de nuestro proyecto.

index.php:

<?php include 'controllers/authController.php'?>
<?php
// redirect user to login page if they're not logged in
if (empty($_SESSION['id'])) {
    header('location: 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">

  <!-- Bootstrap CSS -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css" />
  <link rel="stylesheet" href="main.css">
  <title>User verification system PHP</title>
</head>

<body>
  <div class="container">
    <div class="row">
      <div class="col-md-4 offset-md-4 home-wrapper">

        <!-- Display messages -->
        <?php if (isset($_SESSION['message'])): ?>
        <div class="alert <?php echo $_SESSION['type'] ?>">
          <?php
            echo $_SESSION['message'];
            unset($_SESSION['message']);
            unset($_SESSION['type']);
          ?>
        </div>
        <?php endif;?>

        <h4>Welcome, <?php echo $_SESSION['username']; ?></h4>
        <a href="logout.php" style="color: red">Logout</a>
        <?php if (!$_SESSION['verified']): ?>
          <div class="alert alert-warning alert-dismissible fade show" role="alert">
            You need to verify your email address!
            Sign into your email account and click
            on the verification link we just emailed you
            at
            <strong><?php echo $_SESSION['email']; ?></strong>
          </div>
        <?php else: ?>
          <button class="btn btn-lg btn-primary btn-block">I'm verified!!!</button>
        <?php endif;?>
      </div>
    </div>
  </div>
</body>
</html>

Esta página está destinada a ser accesible solo para usuarios registrados. Es por eso que en la sección superior del archivo, estamos redirigiendo al usuario a la página de inicio de sesión si no ha iniciado sesión. En algún lugar del centro de la página, mostramos el mensaje. Después de mostrar el mensaje, desarmamos el mensaje y escribe variables porque no queremos que permanezca allí en la página incluso después de que el usuario actualice la página.

Por último, en el centro de la página, realizamos una verificación para ver si el usuario que inició sesión ha verificado su dirección de correo electrónico o no. Recuerde que agregamos el verificado variable en la sesión cuando iniciamos la sesión del usuario. Si el usuario ha sido verificado, mostramos el mensaje “¡¡¡Estoy verificado !!!” botón para que lo vean. Si no están verificados, les informamos sobre el enlace de verificación que enviamos a su dirección de correo electrónico y les pedimos que hagan clic en ese enlace para verificar su correo electrónico.

Verificar correo electrónico

En el archivo authController.php, usamos un comentario para indicar dónde enviaríamos el correo electrónico de verificación al usuario llamando al Envia un correo electronico de verificación (). Vaya al archivo authController.php y descomente la llamada a la función así:

// TO DO: send verification email to user
sendVerificationEmail($email, $token);

Definiremos esta función en otro archivo e incluiremos ese archivo dentro de authController.php. Dentro controladores carpeta, crea un archivo con nombre sendEmails.php.

Antes de agregar cualquier código en este archivo, permítanme decirles un poco sobre PHP SwiftMailer, la biblioteca popular para enviar correos electrónicos en PHP que usaremos en este proyecto para enviar correos electrónicos desde localhost.

SwiftMailer es una biblioteca popular rica en funciones para enviar correos electrónicos en aplicaciones PHP.

Para usar Swiftmailer, primero debe instalar compositor. Una vez que haya instalado Composer, abra su terminal o línea de comando y navegue hasta la carpeta raíz del proyecto y ejecute el siguiente comando para agregar la biblioteca Swift Mailer con todos sus archivos a nuestro proyecto:

composer require "swiftmailer/swiftmailer:^6.0"

Esto crea un vendedor carpeta en la raíz de nuestra aplicación que contiene todo el código (clases) requerido para enviar un correo electrónico y también crea un archivo composer.json en la raíz de la aplicación que se ve así:

{
    "require": {
        "swiftmailer/swiftmailer": "^6.0"
    }
}

Ahora abre el sendEmails.php archivo que creamos anteriormente y vamos a escribir la función Envia un correo electronico de verificación():

<?php
require_once './vendor/autoload.php';

// Create the Transport
$transport = (new Swift_SmtpTransport('smtp.gmail.com', 465, 'ssl'))
    ->setUsername(SENDER_EMAIL)
    ->setPassword(SENDER_PASSWORD);

// Create the Mailer using your created Transport
$mailer = new Swift_Mailer($transport);

function sendVerificationEmail($userEmail, $token)
{
    global $mailer;
    $body = '<!DOCTYPE html>
    <html lang="en">

    <head>
      <meta charset="UTF-8">
      <title>Test mail</title>
      <style>
        .wrapper {
          padding: 20px;
          color: #444;
          font-size: 1.3em;
        }
        a {
          background: #592f80;
          text-decoration: none;
          padding: 8px 15px;
          border-radius: 5px;
          color: #fff;
        }
      </style>
    </head>

    <body>
      <div class="wrapper">
        <p>Thank you for signing up on our site. Please click on the link below to verify your account:.</p>
        <a href="http://localhost/cwa/verify-user/verify_email.php?token=' . $token . '">Verify Email!</a>
      </div>
    </body>

    </html>';

    // Create a message
    $message = (new Swift_Message('Verify your email'))
        ->setFrom(SENDER_EMAIL)
        ->setTo($userEmail)
        ->setBody($body, 'text/html');

    // Send the message
    $result = $mailer->send($message);

    if ($result > 0) {
        return true;
    } else {
        return false;
    }
}

La primera declaración requiere la autoload.php archivo en este archivo. Este archivo autoload.php incluirá automáticamente todas las clases de la biblioteca Swift Mailer en el vendedor carpeta que usamos en este archivo.

Estamos usando Gmail en este ejemplo. Por lo tanto, puede reemplazar SENDER_EMAIL y SENDER_PASSWORD con su dirección y contraseña de Gmail que desea usar como la dirección de correo electrónico del remitente. (La dirección de correo electrónico del destinatario es la que se envía a través del formulario).

Normalmente, para enviar un correo electrónico a alguien, debe iniciar sesión en su cuenta de Gmail antes de redactar el correo y enviarlo. Es lo mismo que hace la biblioteca Swift Mailer. Entonces, cuando el receptor ($ userEmail) recibe el correo electrónico, es su dirección de Gmail (SENDER_EMAIL) que verán como el correo electrónico de envío.

Ahora estamos llamando al Envia un correo electronico de verificación() función en nuestro authController.php archivo pero definimos la función dentro sendEmails.php filete. Incluyamos el archivo sendEmails.php dentro de nuestro authController.php para que esta función esté disponible en el archivo. En la cima de authController.php, justo antes de session_start (), agregue la siguiente línea:

require_once 'sendEmails.php';

Eso es todo lo que necesitamos, señores (y señoras) para enviar un correo electrónico a nuestro usuario con un enlace de verificación de correo electrónico. Pero estamos trabajando en localhost y Gmail bloqueará cualquier intento de inicio de sesión de Swift Mailer que se ejecute en localhost.

Envío de correo electrónico desde localhost

Si desea que esto se ejecute en localhost, deberá configurar su cuenta de Gmail para aceptar el inicio de sesión desde aplicaciones menos seguras. Por supuesto, esto puede suponer alguna vulnerabilidad en su cuenta de Gmail, pero puede hacerlo solo durante el corto tiempo que necesita para probar esta aplicación en localhost. Después de la prueba, puede deshacer la configuración en su cuenta de Gmail. Una vez que su aplicación se haya alojado en Internet, estará listo. Solo hacemos esto porque estamos en localhost.

Puede ser aún más cauteloso y crear otra cuenta de Gmail solo para fines como estos.

Así que inicie sesión en su Gmail en su navegador, vaya a https://myaccount.google.com/security#connectedapps y cambie el valor ‘Permitir aplicaciones menos seguras’ a ON.

Puede desactivar esto una vez que haya terminado de probar el proyecto en localhost.

Con esto, podrá enviar un correo electrónico desde localhost con un enlace de verificación una vez que el usuario se registre. Ahora mira el Envia un correo electronico de verificación() método nuevamente y notará que en el cuerpo del correo electrónico que estamos enviando al usuario, el token que generamos para ese usuario en particular (el token es único) se ha establecido como un parámetro en el enlace para que cuando el usuario haga clic en el enlace en el correo electrónico, serán dirigidos a nuestra aplicación en una página llamada verify_email.php con ese token en la URL. Como esto:

<a href="http://localhost/cwa/verify-user/verify_email.php?token=0a150966418fa3a694bcb3ab8fcacd2063a096accc0ee33c3e8c863538ee825c0b52f2e1535d0e1377558c378ba5fc3106eb">Verify Email!</a>

Entonces podemos obtener este token en nuestro verify_email.php así (cálmate, crearemos este verify_email.php pronto):

$token = $_GET['token'];

Ahora podemos usar este token para buscar al usuario que tiene este token en particular (recuerde que el token es único) y si obtenemos ese usuario, actualizamos su registro cambiando el atributo verificado a verdadero en la base de datos. Entonces, podemos decir con orgullo que hemos verificado la dirección de correo electrónico de ese usuario.

Creemos este archivo verify_email.php en la carpeta raíz de nuestro proyecto:

verify_email.php:

<?php
session_start();

$conn = new mysqli('localhost', 'root', '', 'verify-user');

if (isset($_GET['token'])) {
    $token = $_GET['token'];
    $sql = "SELECT * FROM users WHERE token='$token' LIMIT 1";
    $result = mysqli_query($conn, $sql);

    if (mysqli_num_rows($result) > 0) {
        $user = mysqli_fetch_assoc($result);
        $query = "UPDATE users SET verified=1 WHERE token='$token'";

        if (mysqli_query($conn, $query)) {
            $_SESSION['id'] = $user['id'];
            $_SESSION['username'] = $user['username'];
            $_SESSION['email'] = $user['email'];
            $_SESSION['verified'] = true;
            $_SESSION['message'] = "Your email address has been verified successfully";
            $_SESSION['type'] = 'alert-success';
            header('location: index.php');
            exit(0);
        }
    } else {
        echo "User not found!";
    }
} else {
    echo "No token provided!";
}

Tenga en cuenta que establecer el valor de verificado valor a 1 es lo mismo que establecerlo en verdadero, ya que en la base de datos MySQL, el tipo Boolean se interpreta como tinyint.

Ahora, cuando el usuario hace clic en el enlace en su correo electrónico y lo lleva a esta página, actualiza ese usuario. verificado status a verdadero, los registra y los redirige a la página index.php. En la página de índice, después de verificar al usuario, notará que el mensaje de advertencia que le aconseja al usuario que verifique su dirección de correo electrónico ya no está y en su lugar tenemos el mensaje “¡¡¡Estoy verificado !!!” botón que solo es visible para usuarios verificados.

Una última cosa, en la página index.php después de iniciar la sesión del usuario, hay un enlace de cierre de sesión que apunta a un archivo logout.php que se supone que cierra la sesión del usuario. Creemos ese archivo en la raíz de nuestra aplicación:

logout.php:

<?php
session_destroy();
unset($_SESSION['id']);
unset($_SESSION['username']);
unset($_SESSION['email']);
unset($_SESSION['verify']);
header("location: login.php");

Entonces, además de poder registrarse, verificar el correo electrónico, iniciar sesión, el usuario ahora también puede cerrar la sesión.

Conclusión

Eso es todo con el registro de usuario y la verificación de correo electrónico. Si tiene algún comentario, pregunta o palabras de aliento, déjelo en el comentario a continuación. Y, por favor, recuerde compartir esta publicación o recomendar este sitio a sus amigos si lo encuentra útil. ¡Me anima mucho!

¡Qué tengas un lindo día!

Awa Melvine

Deja un comentario