Muchas veces no debemos permitir inicios de sesión duplicados cuando trabajamos con php, es decir, que desde 2 navegadores distintos se abra la misma cuenta en nuestra aplicación.
La verdad que podemos llevar a cabo esta tarea de distintas formas y posiblemente todas sea válidas.
En esta entrada vamos a ver cómo podemos evitar inicios de sesión duplicados en php aplicando una sencilla lógica en nuestra base de datos.
Lo primero que vamos a hacer es crear una tabla de usuarios muy sencilla.
1 2 3 4 5 6 7 |
SELECT * FROM multiple_login.users;CREATE TABLE `users` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `username` varchar(45) COLLATE utf8_spanish_ci NOT NULL, `password` varchar(45) COLLATE utf8_spanish_ci NOT NULL, `sess_id` varchar(45) COLLATE utf8_spanish_ci DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_spanish_ci; |
Lo único que tenemos de “especial” en esta tabla es la columna sess_id, así que vamos a guardar el session_id del usuario en base de datos, de esta forma sabremos si el usuario en cuestión puede o no acceder a la aplicación.
Cómo ya sabemos, la función de php session_id() nos devuelve el identificador del usuario actual, siempre que estemos utilizando $_SESSION de php, así que nos vamos a aprovechar de esto.
Lo primero que debemos hacer es crear un poco de html para simular nuestro proceso, así que crea un archivo .php con el siguiente código.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<!DOCTYPE html> <html lang="en"> <head> <?php session_start() ?> <title></title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <?php if(isset($_SESSION["username"])) { ?> <a href="app.php?action=getProducts">Obtener productos</a> <a href="app.php?action=logout">Logout</a> <?php } else { ?> <form action="login.php" method="POST"> <input name="username" /> <input name="password" /> <input type="submit" name="submit" value="Submit" /> </form> <?php } ?> </body> </html> |
Muy sencillo, si el usuario ha iniciado sesión mostramos un par de enlaces, en otro caso el formulario para que inicie sesión, vamos a iniciar sesión.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
<?php error_reporting(E_ALL); ini_set('display_errors', 1); session_start(); if(isset($_POST['submit'])) { $user = $_POST["username"]; $pass = $_POST["password"]; $mysqli = new mysqli("localhost", "root", "root", "multiple_login"); /* comprobar la conexión */ if ($mysqli->connect_errno) { printf("Falló la conexión: %s\n", $mysqli->connect_error); exit(); } if ($stmt = $mysqli->prepare("SELECT id, username, password FROM users WHERE username = ? AND password = ?")) { /* ligar parámetros para marcadores */ $stmt->bind_param("ss", $user, $pass); /* ejecutar la consulta */ $stmt->execute(); $stmt->store_result(); /* ligar variables de resultado */ $stmt->bind_result($id, $username, $password); /* obtener valor */ $stmt->fetch(); if($stmt->num_rows == 1) { $_SESSION["username"] = $username; $sess_id = session_id(); $smtp = $mysqli->prepare("UPDATE users SET sess_id = ? WHERE id = ?"); $smtp->bind_param("si", $sess_id, $id); $smtp->execute(); $smtp->close(); header("Location: your-site.com"); } } } |
En el script anterior todo es un proceso normal (y bastante desatendido) de login, pero lo más importante viene aquí.
1 2 3 4 5 6 7 8 9 |
{ $_SESSION["username"] = $username; $sess_id = session_id(); $smtp = $mysqli->prepare("UPDATE users SET sess_id = ? WHERE id = ?"); $smtp->bind_param("si", $sess_id, $id); $smtp->execute(); $smtp->close(); header("Location: your-site.com"); } |
Aquí es donde obtenemos el id de sesión del usuario actual y actualizamos ese campo cada vez que un usuario hace login, de esta forma podremos comprobar si el usuario que intenta acceder a nuestra aplicación es el correcto.
Una vez hemos iniciado sesión en la aplicación, podemos acceder a los recursos de la misma, para este ejemplo podrían ser productos, así que vamos a simular la lógica al pulsar el enlace Obtener productos.
Evitar inicios de sesión duplicados en php
Pues bien, para este propósito lo único que debemos hacer es comprobar en cada petición si un usuario ha iniciado sesión y también si la sesión que tiene en sesión valga la redundancia es la que tenemos en base de datos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
if(isset($_GET["action"])) { if($_GET["action"] == "getProducts") { $mysqli = new mysqli("localhost", "root", "root", "multiple_login"); if ($stmt = $mysqli->prepare("SELECT id FROM users WHERE username = ? AND sess_id = ?")) { $user = $_SESSION["username"]; $sess_id = session_id(); /* ligar parámetros para marcadores */ $stmt->bind_param("ss", $user, $sess_id); /* ejecutar la consulta */ $stmt->execute(); $stmt->store_result(); /* obtener valor */ $stmt->fetch(); if($stmt->num_rows == 1) { echo "tu sesión es correcta, podemos obtener los productos"; } else { echo "tu sesión se ha cerrado, no tienes acceso"; } } } } |
Con eso es suficiente, si la sesión que tiene el usuario es la misma que tenemos en base de datos el usuario podrá navegar, en otro caso significa que se ha accedido de forma paralela a la misma cuenta y la primera sesión será machacada, por lo tanto, el usuario A cuando intente acceder a algún recurso que requiera inicio de sesión verá que su sesión ha sido cerrada.
Ahora ya tenemos nuestra aplicación protegida contra inicios de sesión duplicados.