Una de las cosas que antes o después haremos en PHP será crear un sistema de autenticación de usuarios(no confundir con sistema de autorización). Autenticar es comprobar si una entidad es quien realmente pretende ser, basado en un conjunto de credenciales. Si Fulanito intenta entrar en la página "privado.php", el sistema de autenticación deberá comprobar que realmente Fulanito es quien dice ser, y esto lo hará a través de unas credenciales: el nombre de usuario + una contraseña(por ejemplo). La autorización se encarga de decidir si se permite el acceso a la entidad y qué permisos tiene. En base a esos permisos podremos hacer unas u otras tareas. No tendrá los mismos permisos el administrador de un foro(eliminar usuarios, crear nuevos subforos, etc.) que el que tiene un usuario común(crear nuevos temas, responder mensajes, etc.).

Como reza el título de este tema nos centraremos en la parte de autenticación. Y como la autenticación muchas veces se aplica en sistemas muy sencillos veremos que de manera implícita estaremos realizando también una autorización: visualizar el contenido, completo, de la página "privado.php".

Comencemos definiendo la tabla de la base de datos que vamos a utilizar. Estará formada por tres campos: usuario, passwd y email. Un ejemplo podría ser el siguiente:

create table user (
   username varchar(16) primary key,
   passwd char(40) not null,
   email varchar(100) not null
);

El número que hay entre char() y varchar() indica el número máximo de caracteres que se pueden almacenar en estos campos. Así char(30) indica que podemos guardar 30 caracteres.

La dimensión de char es fija, huecos que falten se rellenan con espacios en blanco que son eliminados al recuperar un valor. Varchar es de dimensión variable y su tamaño va aumentando a medida que insertamos datos. Char es más rápido pero varchar ocupa el espacio de manera dinámica.

Lee más acerca de los tipos char y varchar.

El archivo que queremos proteger: privado.php

<?php
// Si el usuario no está autenticado entonces lo mandamos a la página de autenticación(login.php)
if ( !userIsAuth() ){
header('Location: login.php'); // Cuidado con esta función. Debe ser llamada antes de MOSTRAR NADA por pantalla, incluidos espacios en blanco.
}

// Si no, significa que está autenticado y podemos cargar el contenido de la página.
?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
[...]
</html>

La función userIsAuth() devuelve true si el usuario ha iniciado sesión, sino devuelve false.

function userIsAuth(){
  // Si no hay una sesión iniciada, lo hacemos
  if ( !isset($_SESSION) ){
    session_start();
  }

  // If existe la variable de sesión "user" entonces es que un usuario ha iniciado sesión
  if ( isset($_SESSION['user']) ){
    return true;
  } else {
    return false;
  }
}

Hemos dicho que si un usuario intenta acceder a una página protegida sin estar autenticado, será redirigido a una pantalla de inicio de sesión: login.php

<?php
// Comprobamos si hemos iniciado sesión con anterioridad
if (userIsAuth()) {
  echo 'Ueeps, pero ¿queeeé pasa? Si ya has iniciado una sesión. ¿Quieres irte? <a href="logout.php">Sí</a> | <a href="http://disney.com">No</a>';
  exit;
// Comprobamos si hemos recibido algo del formulario de login y que estos datos no sean vacios
} else if ($_POST) {
  if (!empty($_POST['username']) && !empty($_POST['passwd']) ) {

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

    if ( login($username, $passwd) ) {
      'Enhorabuena ' . htmlentities($username) . '. Te has autenticado correctamente.';
      exit;
    } else {
      echo 'Vaya, no hemos encontrado nada que coincida con este nombre de usuario y contraseña en nuestra base de datos';
    }

  } else {
    echo 'Que mal rollo... ¡Tienes que rellenar todos los campos!';
  }
}

/*
 * Si no habíamos iniciado sesión o lo hemos intentado pero ha fallado el proceso entonces mostrará el formulario de login.
 * No voy a implementar la función do_html_form_login(), lo podéis hacer vosotros. Es un simple formulario en html.
 */
do_html_form_login();
?>

Como habéis comprado utilizo la función exit() que se puede usar con la palabra reservada exit, para detener la ejecución del script. No es una buena práctica controlar el flujo de la información de esta forma. Como alternativa, y creo que hubiese sido la mejor opción, podríamos haber utilizado excepciones(en vez de echo).

La función login($username, $passwd) devuelve true si el usuario y contraseña pasados por parámetros existen y coinciden en la base de datos, sino devuelve false.

function login($username, $passwd){
  if ( !isset($_SESSION) ){
    session_start();
  }

  // Nos conectamos a la base de datos
  $conn = db_connect();

  // Evitemos un poco de SQL-injection
  $username = mysql_real_escape_string($username);
  $passwd = mysql_real_escape_string($passwd);

  // comprobamos si el nombre de usuario es exclusivo
  $result = $conn->query("select * from user
                          where username='$username'
                          and passwd='$passwd'");

  // Miramos si hemos podido hacer la consulta a la base de datos
  if ( !$result ){
    return false;
  }

  // Si hemos autenticado al usuario entonces lo registramos en la sesión
  if ( $result->num_rows > 0 ){
    $_SESSION['user'] = $username;
    return true;
  } else {
    return false;
  }
}

También utilizamos la función db_connect() para establecer la conexión con la base de datos:

function db_connect(){
  $result = new mysqli($host, $username, $password, $dbname);
  if( !$result ){
    throw new Exception('No se ha podido conectar a la base de datos');
  } else {
    return $result;
  }
}

Por último, queremos que el usuario pueda cerrar su sesión. Esto lo implementaremos en el script logout.php

<!-- logout.php -->
<?php
// Comprobamos si tenemos alguna sesión iniciada
if (userIsAuth()) {
  if ( !isset($_SESSION) ){
    session_start();
  }

  unset($_SESSION['user']);
  session_destroy();

  echo 'Hasta la <a href="login.php">próxima</a> :D';

} else {
  echo 'Hey, brother, no tienes ninguna sesión de usuario activa';
}

Y hasta aquí hemos llegado, espero que sin haberla liado mucho. Podríamos añadir algunos scripts y mejoras para la gestión de usuarios como uno para registrar nuevos usuarios u otro para poder recuperar contraseñas olvidadas.