Implementación de roles basada en PHP y MySQL

Una pregunta que veo a menudo:

Estoy haciendo un inicio de sesión en php. el usuario solo debe ingresar su nombre y sera re direccionado dependiendo del rol que tenga.


Estoy realizando un sistema en php y mysql quisiera saber cómo trabajar con múltiples sesiones como por ejemplo que tenga una cuenta de administrador y pueda trabajar con toda las páginas y tener cuenta de usuario que algunos vean cierta cantidad de páginas y realizar pocas funciones en el sistema


¿Cómo debo crear una sesión para el administrador? ¿En qué lugar de la aplicación se tiene que reflejar?
El usuario normal no debe ver esa parte que le corresponde al Administrador.


quiero ingresar roles de usuario en mi código pero no se como hacerlo.


Voy a crear una aplicación web para unas personas que llevan el control de unas muestras geológicas, básicamente intervienen dos grupos de usuarios el gerente del área y sus especialistas, por lo tanto no pueden tener los mismos privilegios.

La necesidad de tener diferentes secciones del sitio disponibles para diferentes roles o niveles de usuario es sumamente común.

En este artículo mostraré cómo podría implementarse un esquema como este usando PHP y MySQL.

Aclaración: en mi caso personal, para resolver este problema utilizaría Symfony, pero en este post utilizaré PHP puro para hacer la solución más generalmente aplicable

Cómo guardar los roles en la base de datos

Mi sugerencia siempre es comenzar por definir el modelo de datos.

La base de datos constituye los cimientos de cualquier aplicación web y si éstos no son sólidos, por más magia que tenga nuestro PHP, CSS, JavaScript y demás va a ser difícil terminar con algo mucho mejor que esto:

Así que… mejor pisar sobre seguro.

La primera pregunta que debés responder es: ¿cada usuario tendrá un único rol?

Si la respuesta es sí el esquema de la base de datos es bastante simple, sólo se trata de agregar un campo a la tabla users: role.

Aquí se abre una segunda bifurcación: ¿de qué tipo debe ser el campo role?

La idea más natural es crearlo como string (o tal vez enumerado).

Mi sugerencia, sin embargo, es hacer algo un poco más complicado:

  • Crear una nueva tabla roles con dos campos:
    • id
    • nombre
  • Agregar un campo role_id (en lugar de role) a la tabla users como una clave foránea a la tabla roles

En definitiva, este sería el código SQL de la creación de las tablas:

CREATE TABLE `roles` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
);

CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `email` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
  `password` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
  `role_id` int(10) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `email` (`email`),
  KEY `fk_roles` (`role_id`),
  CONSTRAINT `fk_roles` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`)
);

De esta forma el sistema está preparado para incorporar nuevos roles sin necesidad de alterar la estructura de la base (Y por lo tanto, posibilitar hacerlo desde el front-end sin intervención del desarrollador).

En el caso de que los usuarios puedan tener más de un rol simultáneamente la estructura sería un poco más compleja: deberá incluir una tabla intermedia para implementar la relación N-a-M:

-- Queda igual que en el caso anterior
CREATE TABLE `roles` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
);

-- Se elimina el campo role_id
CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `email` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
  `password` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `email` (`email`)
);

CREATE TABLE `users_roles` (
  `user_id` int(10) unsigned NOT NULL,
  `role_id` int(10) unsigned NOT NULL,
  PRIMARY KEY (`user_id`,`role_id`),
  KEY `fk_roles_2` (`role_id`),
  CONSTRAINT `fk_roles_2` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`),
  CONSTRAINT `fk_users` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
);

Para no complicar mucho el ejemplo voy a tomar el caso de un rol por usuario aunque no cambiará mucho si se trata del otro (Tal vez lo escriba en un próximo artículo).

Bien, la base de datos está, ahora es momento de ver cómo hacer el código PHP.

Cómo redireccionar al usuario según su rol

El siguiente desafío consiste en, una vez haya ingresado el usuario, redirigirlo a la sección del sitio que le corresponda según su rol.

Comencemos por un simple formulario de login:

<html>
<body>
  <form action="processLogin.php" method="post">
     <input type="text" name="username"/>
     <input type="password" name="password"/>
     <input type="submit" value="Ingresar"/>
  </form>
</body>
</html>

Y el archivo del backend

<?php

$username = $_POST['username'];
$password = $_POST['password'];

$sql = "SELECT u.id, r.name AS role, password FROM users u INNER JOIN roles r ON r.id = u.role_id WHERE username = '$username';";

// Conectar a la base de datos
// ejecutar la consulta
// $result contiene el resultado de la consulta

if (password_verify($result['password'], password_hash($password, PASSWORD_BCRYPT))) {
   $startingPage = [
      'admin' => 'admin_home.php',
      'user' => 'user_home.php',
   ];

   $nextPage = array_key_exists($result['role'], $startingPage) ? $startinPage['role'] : 'user_home.php';
   if (array_key_exists($result['role'], $startingPage)) {
      $nextPage = $startinPage[$result['role']];
   } else {
      $nextPage = $startinPage['user'];
      error_log('There is no starting page for role '.$result['role']);
   }
   session_start();
   $_SESSION['user_id'] = $result['id'];
   $_SESSION['role'] = $result['role'];
   header('Location: '.$nextPage);
} else {
   header('Location: login.html');
}

Aquí estoy usando las funciones password_verify y password_hash para hacer un login seguro.

Y con esto hemos logrado dirigir a cada persona a la sección que le corresponde…

Lo que nos falta es dejar afuera a los que quieran ir a mirar donde no les corresponde 😉

Cómo restringir el acceso a una sección del sitio según el rol del usuario

Una forma simple de restringir el acceso para alguien que no se logeó la podés leer acá.

La idea de esta sección es ampliar un poco sobre esa misma línea y permitir el acceso sólo para aquellos usuarios cuyo rol está habilitado:

 <?php

// admin_home.php

session_start();

if (!array_key_exists('user_id', $_SESSION)) {
   header('Location: login.html');
   die;
}

$allowedRoles = ['admin'];

if (!array_key_exists('role', $_SESSION) || !in_array($_SESSION['role'], $allowdRoles)) {
   header('Location: login.html');
   die;
}
?>
<h1>Bienvenido amind!</h1>

Habrás observado que el protagonista de esta pequeña novela es el array $_SESSION.

Si todavía no lo tienes super claro, te sugiero continuar leyendo por aquí.

mchojrin

¿Te quedó alguna duda? Publica aca tu pregunta

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

A %d blogueros les gusta esto: