Hace poco he retomado Laravel y he decidido actualizar un proyecto que tenía de la versión 5.0 a la 5.1 (no he querido lanzarme a la 5.2 todavía). La cosa es que en este proyecto tenía preparado el acceso de usuarios (login) para que pudieran acceder con su nombre de usuario o su email indistintamente más la contraseña pero en Laravel 5.1 de nuevo el Auth no viene preparado para esto.
En la versión 5.0 ya lo solucioné e hice un pequeño tutorial al respecto: Laravel 5 cómo hacer login con email o username
Pero para Laravel 5.1 quería hacer algo un poco más sencillo y elegante, así que me puse a buscar y preguntar.
Encontré que es fácil cambiar de login con email, que es la opción por defecto, a login con username. No hay más que añadir una variable protected al AuthController.
class AuthController extends Controller { ... protected $username = "username";
Encontré, y me comentaron también, cómo hacerlo añadiendo un código sencillo en un archivo dentro de vendor pero aunque se supone que añadiendo alguna línea también en el composer.json esa solución no iba a sobreescribirse en el próximo composer update no me convencía la solución.
Finalmente llegué a mi propia solución sencilla, segura y sin necesidad de tocar el vendor, voy a explicarla en 6 pasos:
En el formulario que utilicemos para que el usuario haga login, ponemos un input name=»login» en lugar de input name=»email» o input name=»username»
En la ruta donde va el formulario para identificar al usuario vamos a cambiar ‘AuthController@postLogin‘ por ‘CustomAuthController@postLogin’ o el nombre que queramos darle al nuevo controller.
Route::post( 'auth/login', 'CustomAuthController@postLogin');
Creamos un nuevo controller al que llamamos CustomAuthController.php u otro nombre, esto queda al gusto del consumidor.
Ahora vamos a echar una miradita a AuthController y vamos a encontrar que tiene la línea
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
Esto quiere decir que está usando esos dos traits. ThrottlesLogins controla la cantidad de intentos de acceso, AuthenticatesAndRegistersUsers es el que nos interesa ahora. Mirando un poco más arriba en el archivo encontramos su ruta y vamos a hacerle una visita.
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers
Entramos en vendor \ laravel \ framework \ src \ Illuminate \ Foundation \ Auth \ AuthenticatesAndRegistersUsers y encontramos que no hay casi nada porque este archivo a su vez hace uso de AuthenticatesUsers que es quien realmente tiene lo que buscamos.
use AuthenticatesUsers, RegistersUsers { ...
En AuthenticatesUsers está la función que queremos: postLogin. La copiamos entera y la pegamos en nuestro CustomAuthController.
Ahora vamos a añadir algunas líneas en nuestro CustomAuthController para ponerlo en marcha
namespace App\Http\Controllers\Auth; use Illuminate\Support\Str; use Illuminate\Support\Facades\Auth; use Illuminate\Http\Request; use Illuminate\Foundation\Auth\AuthenticatesUsers; use App\Http\Requests; use App\Http\Controllers\Controller; class CustomAuthController extends Controller { use AuthenticatesUsers; protected $redirectPath = '/descubre'; protected $username = 'login'; public function postLogin(Request $request) { ...
Esto es lo necesario hasta llegar al único método de nuestro Controller que es el que nos importa, el postLogin.
De un repaso rápido vemos que estamos utilizando (use) AuthenticatesUsers para sobreescribir su método postLogin,
protected $redirectPath está aquí para definir la ruta donde mandamos al usuario una vez identificado y protected $username es el nombre del campo login que hemos añadido/cambiado antes en el formulario de acceso de usuario.
Tenemos el método postLogin traido de AuthenticatesUsers tal cual y vamos a hacer un pequeño cambio para adaptarlo a lo que queremos.
Buscamos las líneas encargadas de recoger las credenciales ($credentials) del usuario, su nombre ( email o username ) y contraseña y de probar a acceder con ellas.
$credentials = $this->getCredentials($request); if (Auth::attempt($credentials, $request->has('remember'))) { return $this->handleUserWasAuthenticated($request, $throttles); }
y metemos en medio este foreach cuya función es recorrer las credentials y evaluar si el valor del campo login es un email o no para decirle después a Laravel si lo que viene como nombre tiene que comprobarlo con el campo email o con el campo username.
foreach ($credentials as $key => $value) { if (! Str::contains($key, 'password')) { $newkey = filter_var( $value, FILTER_VALIDATE_EMAIL) ? 'email' : 'username'; $credentials[$newkey] = $value; unset($credentials[$key]); } }
Y finalmente nos queda el método postLogin completo tal que así:
public function postLogin(Request $request) { $this->validate($request, [ $this->loginUsername() => 'required', 'password' => 'required', ]); // If the class is using the ThrottlesLogins trait, we can automatically throttle // the login attempts for this application. We'll key this by the username and // the IP address of the client making these requests into this application. $throttles = $this->isUsingThrottlesLoginsTrait(); if ($throttles && $this->hasTooManyLoginAttempts($request)) { return $this->sendLockoutResponse($request); } $credentials = $this->getCredentials($request); foreach ($credentials as $key => $value) { if (! Str::contains($key, 'password')) { $newkey = filter_var( $value, FILTER_VALIDATE_EMAIL) ? 'email' : 'username'; $credentials[$newkey] = $value; unset($credentials[$key]); } } if (Auth::attempt($credentials, $request->has('remember'))) { return $this->handleUserWasAuthenticated($request, $throttles); } // If the login attempt was unsuccessful we will increment the number of attempts // to login and redirect the user back to the login form. Of course, when this // user surpasses their maximum number of attempts they will get locked out. if ($throttles) { $this->incrementLoginAttempts($request); } return redirect($this->loginPath()) ->withInput($request->only($this->loginUsername(), 'remember')) ->withErrors([ $this->loginUsername() => $this->getFailedLoginMessage(), ]); }
Y listo, ya hemos preparado nuestro login con username o email en laravel 5.1 con estos sencillos 6 pasos.
Me gustaría terminar lanzando una pregunta a quien lea esto: ¿consideras que aún es necesario o útil el login con username o ya solo debería hacerse con email porque lo del username es muy 90’s?
Este fue un pequeño debate que surgió cuando pregunté a unos amigos ( también desarrolladores ) sobre cómo hacer esto y me gustaría conocer más opiniones.
(Imágenes de pixabay.com)
Tu sabes quién soy
Yo opino que es de los 90!!!
Mario
Jesu espero tengas buen día, agradezco tu post, en realidad es muy útil. Debo realizar una autenticación con un RUT (chileno) y no he encontrado mayor información, todos utilizan el correo para realizar login.
soleguia
Me alegro que te haya sido útil 😀
Melv
Es cierto que puede parecer de los 90’s pero no deja de ser muy útil ya que va a depender de la aplicación que se esté desarrollando y el rubro de negocio. Por ejemplo: Si desarrollas una aplicación para una empresa en la que no todas las posiciones requieren de un correo personalizado por usuario, imaginemos un hotel; el PMS es usado por los recepcionista a quienes casi nunca les asignan cuentas de correos, ya que al decir verdad, la naturaleza de lo que hacen no lo requiere a ese nivel. hacen check-in, check-out, servicio al cliente in-house, entre otras actividades; en todo momento la herramienta que necesitan es es el PMS y no tienen contacto con los clientes vía correo, para el hotel es más factible el «username» y no incurrir en gastos de cuentas de correos que solo se usará para hacer login a una aplicación.
Ahora paso a mi pregunta, esto 6 pasos lo puedo hacer en la versión 5.4 y me funcionará?
Saludos,
soleguia
Hola, Melv
Pues para serte sincero, aún no me he puesto con laravel 5.4 aunque tengo un proyecto pendiente de actualizar de versión.
Pero con esta solución no deberías tener problema porque lo que hacemos es sobreescribir el método para añadir una funcionalidad propia sin necesidad de tocar el core de laravel.
Si acaso puede que necesites hacer algún ajuste si es que se han producido cambios en el AuthController original o en el método postLogin en laravel 5.4. Que como te digo no he tocado aún.
Cuando me ponga con ello, haré una actualización de esta entrada aunque sea para decir que sigue vigente 😀
Un saludo.
Melv
De acuerdo, muchas gracias. Igual me haré una copia de seguridad e intentaré sobrescribir el método a ver si me funciona.
Saludos,
Eliseo
Hola disculpa la pregunta, podrías explicarme si existe alguna forma de hacer login desde distintas tablas en un mismo proyecto. De ante mano gracias.
Eliseo
Estoy utilizando la versión 5.1 de laravel.
JSoleguia
Hola, Eliseo
Para serte sincero alguna vez he pensado en hacer eso mismo pero por falta de tiempo o por seguir avanzando otras áreas del proyecto nunca he profundizado en ello. Haciendo una búsqueda superficial encuentro esto que puede ser una primera pista:
https://laraveles.com/foro/viewtopic.php?id=4873
Entrando a la documentación de Laravel 5.2
https://laravel.com/docs/5.2/authentication#authenticating-users
En el apartado «Specifying Additional Conditions» entiendo que podrías customizar las condiciones de acceso de usuario consultando si el usuario existe en esa otra tabla.
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
// The user is active, not suspended, and exists.
}
Podrías añadir un or en ese if que chequee la existencia del usuario, su contraseña y si está activo en otra tabla. De forma que tienes separadas las tablas de tus tipos de usuarios (supongo que quieres trabajar con «usuarios» por un lado y «administradores» por otro, o alguna catalogación similar) y cuando alguien intente acceder compruebas si existe en alguna de las tablas.
Espero que esto te sirva de ayuda, o al menos de punto de apoyo.
Cuando lo resuelvas eres bienvenido y te agradecería que me comentes la solución a la que has llegado.
Un saludo.