YII – Autenticación de usuarios

YII – Autenticación de usuarios + ultimo acceso

Este post tiene por objetivo crear la funcionalidad de autenticación de usuarios contra una base de datos usando el humilde y eficiente framework YII.

Se pretende con este post ampliar la explicación del capitulo 9 de la GUIÁ BÁSICA DE YII FRAMEWORK añadiendo algunos detalles como la captura y gestión del la fecha del ultimo acceso al sistema por cada usuario, condicionar mostrar elementos del menú según el perfil del usuario.

YII Autenticación de usuarios

Creamos la tabla que vamos a usar para la practica en este caso estamos usando como base de datos postgreSQL, creamos la columna created_at que guardara automáticamente la fecha en que se ingresa cada registro haciendo uso de la función de postgres now().

[sourcecode language=»sql»]
CREATE TABLE usuario
(
id_usuario serial NOT NULL,
cedula character varying(12) NOT NULL,
nombre character varying(50) NOT NULL,
apellido character varying(50) NOT NULL,
email character varying(100) NOT NULL,
username character varying(128) NOT NULL,
password character varying(128) NOT NULL,
perfil character varying(10) NOT NULL,
created_at timestamp without time zone DEFAULT now(),
last_login timestamp without time zone,
CONSTRAINT usuario_pkey PRIMARY KEY (id_usuario )
);
[/sourcecode]

created at last login php yii

Como hacer autenticación de usuarios en Yii Framework

Se deberá auto-generar el modelo y el CRUD para esta tabla usando el modulo gii (YII code generator). Acceden a el con este url http://localhost/sistema/index.php?r=gii

En lo sucesivo es importante que antes de modificar cualquier archivo realicen una copia del mismo por si se trancan o no resulta puedan restituir y revisar con calma desde el comienzo.

Sustituiremos el contenido del archivo protected/components/UserIdentity.php por lo siguiente:

[sourcecode language=»php» toolbar=»true» title=»protected/components/UserIdentity.php»]
<?php /** * UserIdentity represents the data needed to identity a user. * It contains the authentication method that checks if the provided * data can identity the user. */ class UserIdentity extends CUserIdentity { private $_id; public function authenticate(){ $username=strtolower($this->username);
$user=Usuario::model()->find(‘LOWER(username)=?’,array($username));
if($user===null)
$this->errorCode=self::ERROR_USERNAME_INVALID;
else if(!$user->validatePassword($this->password))
$this->errorCode=self::ERROR_PASSWORD_INVALID;
else{
$this->_id=$user-&gt;id_usuario;
$this->username=$user-&gt;username;
$this->errorCode=self::ERROR_NONE;

/*Consultamos los datos del usuario por el username ($user-&gt;username) */
$info_usuario = Usuario::model()-&gt;find(‘LOWER(username)=?’, array($user->username));
/*En las vistas tendremos disponibles last_login y perfil pueden setear las que requieran */
$this->setState(‘last_login’,$info_usuario->last_login);
$this->setState(‘perfil’,$info_usuario->perfil);

/*Actualizamos el last_login del usuario que se esta autenticando ($user-&gt;username) */
$sql = "update usuario set last_login = now() where username=’$user->username’";
$connection = Yii::app()->db;
$command = $connection->createCommand($sql);
$command->execute();

}
return $this->errorCode==self::ERROR_NONE;
}

public function getId(){
return $this->_id;
}

}

[/sourcecode]

Las lineas 22, 23, 24 y 25 son las que hacen la magia de guardar la fecha, hora, minutos y segundos actual en el campo dispuesto para almacenar el ultimo acceso del usuario cuyas credenciales (usuario y contraseña) para entrar al sistema sean validas.

luego debemos agregar en el Modelo relacionado a la tabla usuario lo necesario para que convierta la contraseña que se envía desde el formulario de login al método de su preferencia, en esta practica usaremos MD5 para luego comparar la cadena resultante con la cadena ya almacenada en el campo password en la tabla usuario.

En el archivo protected/models/Usuario.php

agregaremos las siguientes lineas:

[sourcecode language=»php»]

public function validatePassword($password){
return $this-&gt;hashPassword($password)===$this-&gt;password;
}

public function hashPassword($password){
return md5($password);
}

[/sourcecode]

Por si tienen dudas, las lineas anteriores van dentro de la clase Usuario (después de: class Usuario extends CActiveRecord { y antes del ultimo } )

Ahora es el turno de agregar y ajustar un par de lineas en el controlador ( protected/controllers/UsuarioController.php ) para que todo marche bien.

Es necesario que cuando se cree o se modifique un usuario la cadena de caracteres que representa la contraseña sea guardado en MD5 para asi ser guardado en la Base de Datos.

Para esto solo agregaremos una linea que pasa la cadena a MD5  $model->password=md5($model->password); antes de guardar o modificar

[sourcecode language=»php»]

public function actionCreate()
{
$model=new Usuario;

// Uncomment the following line if AJAX validation is needed
// $this-&gt;performAjaxValidation($model);

if(isset($_POST[‘Usuario’]))
{
$model-&gt;attributes=$_POST[‘Usuario’];
$model-&gt;password=md5($model-&gt;password); //agregar esta linea antes del save() lo mismo en la funcion de modificar actionUpdate()

if($model-&gt;save())
$this-&gt;redirect(array(‘view’,’id’=&gt;$model-&gt;id_usuario));
}

$this-&gt;render(‘create’,array(
‘model’=&gt;$model,
));
}

[/sourcecode]

Para que puedan tener acceso a los módulos de editar, crear, eliminar, el admin y demás es necesario agregar permisos en accessRules(), indicando que podrán ser usados esos módulos por todos los usuarios que estén autenticados con el símbolo @ o directamente el nombre del usuario.

En el siguiente código indicaremos que crear y modificar usuarios solo lo podrá hacer el usuario leninmhs y accederán al modulo admin de usuarios todos los usuarios autenticados.

[sourcecode language=»php»]
public function accessRules()
{
return array(
array(‘allow’,  // allow all users to perform ‘index’ and ‘view’ actions
‘actions’=&gt;array(‘index’,’view’),
‘users’=&gt;array(‘*’),
),
array(‘allow’, // allow authenticated user to perform ‘create’ and ‘update’ actions
‘actions’=&gt;array(‘create’,’update’,’delete’),
‘users’=&gt;array(‘leninmhs’),
),
array(‘allow’, // allow admin user to perform ‘admin’ and ‘delete’ actions
‘actions’=&gt;array(‘admin’),
‘users’=&gt;array(‘@’),
),
array(‘deny’,  // deny all users
‘users’=&gt;array(‘*’),
),
);
}
[/sourcecode]

En caso contrario se toparan con este mensaje de error que les indica que no están autorizados para usar estos módulos.

Error 403

You are not authorized to perform this action.
De este punto en adelante solo trabajaremos en las vistas ( todos los directorios y archivos que se encuentran en: protected/views/ )

Si auto generaron el crud en base a la tabla que se presento al inicio, en el archivo protected/views/usuario/_form.php deberán quitar o comentar como mejor le parezca las lineas de código que forman los campos de created_at y last_login ya que esto se llena tras bambalinas.

[sourcecode language=»php»]

&lt;div&gt;
&lt;?php //echo $form-&gt;labelEx($model,’created_at’); ?&gt;
&lt;?php //echo $form-&gt;textField($model,’created_at’); ?&gt;
&lt;?php //echo $form-&gt;error($model,’created_at’); ?&gt;
&lt;/div&gt;

&lt;div&gt;
&lt;?php //echo $form—&gt;labelEx($model,’last_login’); ?&gt;
&lt;?php //echo $form-&gt;textField($model,’last_login’); ?&gt;
&lt;?php //echo $form-&gt;error($model,’last_login’); ?&gt;
&lt;/div&gt;

[/sourcecode]

en caso contrario obtendrán un error como este:

CDbCommand failed to execute the SQL statement: SQLSTATE[22007]: Invalid datetime format: 7 ERROR: la sintaxis de entrada no es válida para tipo timestamp: «»
LINE 1: …inmhs’, ’74be16979710d4c4e7c6647856088456′, ‘ADMIN’, », »)
^. The SQL statement executed was: INSERT INTO «usuario» («cedula», «nombre», «apellido», «email», «username», «password», «perfil», «created_at», «last_login») VALUES (:yp0, :yp1, :yp2, :yp3, :yp4, :yp5, :yp6, :yp7, :yp8)

Ahora vamos a imprimir la fecha del ultimo acceso cuando el usuario este autorizado a para usar el sistema y vamos a mostrar el menú de acceso al modulo de usuarios solo si el perfil del usuario es de administrador ( ADMIN )

modificaremos el archivo: protected/views/layouts/main.php

[sourcecode language=»php»]
&lt;div id="mainmenu"&gt;
&lt;?php

$admin = (isset(Yii::app()-&gt;user-&gt;perfil) and Yii::app()-&gt;user-&gt;perfil == ‘ADMIN’) ? true : false ;
$this-&gt;widget(‘zii.widgets.CMenu’,array(
‘items’=&gt;array(
array(‘label’=&gt;’Home’, ‘url’=&gt;array(‘/site/index’)),
array(‘label’=&gt;’About’, ‘url’=&gt;array(‘/site/page’, ‘view’=&gt;’about’)),
array(‘label’=&gt;’Contact’, ‘url’=&gt;array(‘/site/contact’)),
array(‘label’=&gt;’Usuarios’, ‘url’=&gt;array(‘/usuario/admin’), ‘visible’ =&gt; $admin),
array(‘label’=&gt;’Login’, ‘url’=&gt;array(‘/site/login’), ‘visible’=&gt;Yii::app()-&gt;user-&gt;isGuest),
array(‘label’=&gt;’Salir (‘.Yii::app()-&gt;user-&gt;name.’)’, ‘url’=&gt;array(‘/site/logout’), ‘visible’=&gt;!Yii::app()-&gt;user-&gt;isGuest)
),
)); ?&gt;
&lt;/div&gt;&lt;!– mainmenu –&gt;

&lt;div style="text-align: right;"&gt;
&lt;?php if(!Yii::app()-&gt;user-&gt;isGuest and isset(Yii::app()-&gt;user-&gt;last_login)){
echo "Ultimo Acceso: ".Yii::app()-&gt;dateFormatter-&gt;format("d-M-y h:m a", Yii::app()-&gt;user-&gt;last_login);} ?&gt;
&lt;/div&gt;
[/sourcecode]

Básicamente nos interesa:

  • La linea 4 y la 10 que muestra o oculta la opción de menu que lleva a la administración de usuarios mediante la opción «visible» de CMenu.que solo acepta valores booleanos, es por esto que antes creamos una variable de nombre $admin el cual mediante un if ternario le asigno true o false según sea o no ADMIN.
  • De la linea 17 a la 20 validamos si el usuario se autentico y si tiene fecha de ultimo acceso para proceder a darle formato a la fecha en cuestión y imprimirla.

De modo que se tienen dos usuarios uno con perfil ADMIN y otro con un perfil distinto, el primero vera la opción de ir a la administración de usuarios y el segundo no.

last login yii cmenu

El resultado final debería ser algo como esto:

36 comentarios en “YII – Autenticación de usuarios + ultimo acceso”

  1. Gracias por el tutorial, muy bien explicado. Una duda, en la linea:
    $admin = (isset(Yii::app()->user->perfil) and Yii::app()->user->perfil == ‘ADMIN’) ? true : false ;

    la palabra perfil es un campo de la base de datos?

    Gracias!

  2. La primera linea del UserIdentity,php me generaba un error.

    $username=strtolower($this—>username);

    Cambienla por esta-

    $username=strtolower($this->username);

    Gracias por el tuto!

    1. Cuando dices que no te lo reconoce YII es que no logras la autenticación con esos usuarios? o que no lo ves en el admin de yii? intenta comparar y verificar los pasos que realizaste con los de esta entrada…

    1. Si, Yii tiene incorporado mecanismos de autenticación y autorización muy completos. En el accessRules puedes usar: actions, rules, users, ip, verbs, expression y un mas…
      Siguiendo la dinámica de esta entrada coloca: ‘expression’ => «Yii::app()->user->perfil == ‘ADMIN'»,
      en accessRules y quitas la linea donde indico el usuario.
      http://www.yiiframework.com/doc/guide/1.1/es/topics.auth
      http://www.yiiframework.com/doc/api/1.1/CAccessRule

  3. Pingback: Entendiendo Yii | Leninmhs

  4. Hola que tal lennin como estas, lennin hago todos los pasos con mucho cuidado pero pasa algo cuando voy a ingresar con los usuarios que tengo creado en la BD no me deja acceder y me msj de error q el usuario o el password pueden ser incorrectos. Y esta tal cual, claro la unica diferencia es q la clave no esta en MD5 ya que agregue los usuarios a pedal y pulmón x postgres..! Que podrá ser estoy viendo si puedo hacer el logueo de Tutorial de PostgreSQL..

  5. Hola amigo espero te encuentres bn. Lenin tengo una duda xq en el ejemplo que presentas creas una tabla usuario con el schema public por defecto. Ahora viene la gran duda ya que tengo mi tabla usuario dentro de un schema distinto al public y por tal motivo el ejemplo que presentas no me funciona. Que tendria que modificar ? Me supongo que las modificaciones serian mas que todo en el controlador y el modelo. Gracias x ese gran aporte!!

    1. No tienes que modificar nada en realidad!! cuando vallas a usar el Model Generator desde Gii, coloca el nombre del esquema.tutabla, si tu esquema se llama por ejemplo «compartido», entonces colocaras compartido.usuario y listo en la funcion tableName del modelo Usuario.php que genere colocara «compartido.usuario2»
      Adicionalmente si haces alguna consulta usando sql directo como el update usuario deberias colocar update tuesquema.usuario
      Saludos!!

  6. Pingback: Sessions en base de datos con Yii | Leninmhs

  7. Soy un usuario con pocos conocimientos de PHP y de Yii y buscando información di con tú guía básica y quería agradecerte el tiempo que te tomaste en elaborar tan buen material de estudio y el hecho de dejarlo disponible para quien quiera descargarlo. Si no lo tomas a mal me gustaría recomendar también a quien busque más información la serie de vídeos de Gustavo Salgado en youtube(http://www.youtube.com/user/Gustalh). Saludos

  8. Saludos,
    Alguien me puede ayudar?
    La tuto me servido mucho, Gracias!
    pero tengo un pequeño problema en la parte final en el menú no se ocultan los etiquetas que supuestamente no se tiene acceso, es decir me muestra TODO sin ocultar las que no debería mostrar. El nivel de acceso lo cumple perfectamente, pero no oculta en el menu.
    Si pueden ayudarme se los agradecería bastante!!!

    1. En esta practica solo estamos usando condicionales (IF) asi lo refleje:
      $admin = (isset(Yii::app()->user->perfil) and Yii::app()->user->perfil == ‘ADMIN’) ? true : false ;
      luego: poner visible true o false asi lo deje: array(‘label’=>’Usuarios’, ‘url’=>array(‘/usuario/admin’), ‘visible’ => $admin),
      Pudieras poner un par de if o case segun necesites… Saludos!!

  9. Hola que tal, mira este ejemplo me sirvió de mucho para mi proyecto universitario pero mira, ya aplique todo y no me genera ningun error, no uso la misma tabla que tu pero me funciona pero a la hora de poner $admin = (isset(Yii::app()->user->rol_GU) and Yii::app()->user->rol_GU == ‘Administrador’) ? true : false ; y luego asignar a cada espacio del menu que quiero que entre, por ejemplo array(‘label’=>’Usuarios’,’url’=>array(‘/user/admin’),’visible’ => $admin),

    Resulta que cuando entro con un usuario que tiene el rol de administrador no me aparece ese espacio, me lo oculta como si el usuario no tiene como rol o perfil administrador.

    como podrás ver a todo lo que tu tienes como perfil yo lo tengo como rol_GU, pero eso es lo de menos, no se si es que no se esta conectando bien o no se.

    la tabla que yo tengo es: id_GU – username – password – rol_GU – identificación_GU y pues solo no le inclui todo aquello que decia last_login y pues bueno, si no me da error de nada todo esta bien solo que no se por qué cuando lo especifico no aparece, o sea si no estoy autentificado no me aprese y cuando me autentifico tampoco aparece. Porfa ocupo ayuda en esto.

    1. bueno ya encontré el problema, soluciono mi problema solamente fue que no agregué la siguiente linea $this->setState(‘perfil’,$info_usuario->perfil); ..bueno gracias, me sirvió de mucho este ejemplo, saludos

  10. Pues muchísimas gracias por el tutorial. De momento quiero tocar el tema de las vistas, que me falta eso, pero hasta ahora me ha sido de mucha ayuda. Solo un pequeño apunte. Para que me pudiese leer bien las contraseñas encriptadas, además de todo lo que dices, en protected/components/UserIdentity.php me ha tocado, en el método authenticate() cambiar la línea

    else if(($users->clave != $this->password) && $users!==null)

    por

    else if(($users->CampoCalveTabla != md5($this->password)) && $users!==null)

    para que también, al comprobar que no coinciden, transforme la contraseña, ya que si no me daba error todo el rato.

    Un saludo y gracias de nuevo

    1. perdón. $users->clave es igual que $users->CampoCalveTabla. Ahí habría que poner el nombre del la columna donde almacenéis la contraseña en vuestra tabla de usuarios de la BBDD

  11. Hola, muy bueno el tutorial, me funciona todo perfectamente, solo que cuando edito el usuario para cambiar otro campo y guardo, luego no puedo ingresar con ese usuario, me dice que el usuario o contraseña son incorrectos, alguien que me de euna mano en solucionar esto, es lo unico que me falta por resolver.
    Saludos y gracias de antemano!!

  12. Pingback: Sessions en base de datos con Yii - Leninmhs

  13. Como estas leninmhs estoy con tu aplicación me da el siguiente error
    Trying to get property of non-object

    if($user===null)
    44 $this->errorCode=self::ERROR_USERNAME_INVALID;
    45 else if(!$user->validatePassword($this->password))
    46 $this->errorCode=self::ERROR_PASSWORD_INVALID;
    47 else{
    48 $this->_id=$user->id_usuario;
    49 $this->username=$user->username;
    50 $this->errorCode=self::ERROR_NONE;
    51
    52 /*Consultamos los datos del usuario por el username ($user->username) */
    53 $info_usuario = Usuario::model()->find(‘LOWER(username)=?’, array($user->username));
    54 /*En las vistas tendremos disponibles last_login y perfil pueden setear las que requieran */
    55 $this->setState(‘last_login’,$info_usuario->last_login);
    56 $this->setState(‘perfil’,$info_usuario->perfil);
    57
    58 /*Actualizamos el last_login del usuario que se esta autenticando ($user->username) */
    59 $pgsql = «update usuario set last_login = now() where username=’$user->username'»;
    60 $connection = Yii::app() -> db;
    61 $command = $connection -> createCommand($pgsql);
    62 $command -> execute();
    63
    64 }
    65 return $this->errorCode==self::ERROR_NONE;
    66 }
    67

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *