ruby on rails - rails - RoR Devise: Inicia sesión con nombre de usuario O correo electrónico
devise rails (11)
Aquí hay una solución de Rails que refactores @ padde responde. Utiliza el find_by de ActiveRecord para simplificar las llamadas, asegura que solo hay una llamada basada en la expresión regular, y también admite identificadores numéricos si desea permitir eso (útil para scripts / API). La expresión regular para el correo electrónico es tan simple como debe ser en este contexto; solo comprobando la presencia de un @ ya que supongo que su nombre de usuario no permite @ caracteres.
def self.find_for_database_authentication(conditions={})
email = conditions[:email]
if email =~ /@/
self.find_by_email(email)
elsif email.to_s =~ //A[0-9]+/z/
self.find(Integer(email))
else
self.find_by_username(email])
end
end
Al igual que la respuesta de wiki y @ aku, también recomendaría crear un nuevo parámetro de inicio de sesión usando attr_accessible y authentication_keys en lugar de usar: correo electrónico aquí. (Lo guardé como: correo electrónico en el ejemplo para mostrar la solución rápida).
¿Cuál es la mejor manera de permitir a los usuarios iniciar sesión con su dirección de correo electrónico O su nombre de usuario? Estoy usando warden + idear para la autenticación. Creo que probablemente no sea demasiado difícil hacerlo, pero supongo que necesito algunos consejos sobre dónde poner todo lo que se necesita. Tal vez el diseño ya proporciona esta característica? Al igual que en config / initializers / devise.rb escribirías:
config.authentication_keys = [ :email, :username ]
Para solicitar el nombre de usuario Y el correo electrónico para iniciar sesión. Pero realmente quiero tener solo un campo para el nombre de usuario y el correo electrónico, y solo necesito uno. Simplemente visualizaré que con algo de arte ASCII, debería verse algo así en la vista:
Username or Email:
[____________________]
Password:
[____________________]
[Sign In]
Asegúrate de que ya has agregado un campo de nombre de usuario y agrega un nombre de usuario a attr_accessible. Crear un atributo virtual de inicio de sesión en Usuarios
1) Agregue el inicio de sesión como un attr_accessor
# Virtual attribute for authenticating by either username or email
# This is in addition to a real persisted field like ''username''
attr_accessor :login
2) Agregue el inicio de sesión a attr_accessible
attr_accessible :login
Dile a Devise que use: inicia sesión en authentication_keys
Modifique config / initializers / devise.rb para tener:
config.authentication_keys = [ :login ]
Sobrescribir el método find_for_database_authentication de Devise en Usuarios
# Overrides the devise method find_for_authentication
# Allow users to Sign In using their username or email address
def self.find_for_authentication(conditions)
login = conditions.delete(:login)
where(conditions).where(["username = :value OR email = :value", { :value => login }]).first
end
Actualice sus vistas Asegúrese de tener las vistas Diseñadas en su proyecto para que pueda personalizarlas
remove <%= f.label :email %> remove <%= f.email_field :email %> add <%= f.label :login %> add <%= f.text_field :login %>
Con gema squeel puedes hacer:
def self.find_for_authentication(conditions={})
self.where{(email == conditions[:email]) | (username == conditions[:email])}.first
end
Escribí así y funciona. No sé si es "feo arreglo", pero si se me ocurre una solución mejor, te lo haré saber ...
def self.authenticate(email, password)
user = find_by_email(email) ||
username = find_by_username(email)
if user && user.password_hash = BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
He encontrado una solución para el problema. No estoy del todo satisfecho (prefiero tener una forma de especificar esto en el inicializador), pero funciona por ahora. En el modelo de usuario agregué el siguiente método:
def self.find_for_database_authentication(conditions={})
find_by(username: conditions[:email]) || find_by(email: conditions[:email])
end
Como @sguha y @Chetan han señalado, hay otro gran recurso disponible en la wiki oficial de la invención .
Platforma Tec (autor del diseño) ha publicado una solución para su wiki de github que utiliza una estrategia de autenticación de Warden subyacente en lugar de conectarse al controlador:
(Una respuesta anterior tenía un enlace roto, que creo que estaba destinado a vincular a este recurso).
Si está utilizando MongoDB (con MongoId), debe realizar una consulta diferente:
def self.find_for_database_authentication(conditions={})
self.any_of({name: conditions[:email]},{email: conditions[:email]}).limit(1).first
end
así será en algún lugar en línea.
Utilizo un truco rápido para esto, para evitar cambiar cualquier código específico de diseño y usarlo para mi escenario específico (en particular, lo uso para una API donde las aplicaciones móviles pueden crear usuarios en el servidor).
He agregado un before_filter a todos los controladores donde se pasa el nombre de usuario, genero un correo electrónico del nombre de usuario ("# {params [: user] [: username]} @ mycustomdomain.com") y guardo al usuario. Para todas las demás llamadas, genero el correo electrónico basado en la misma lógica. Mi before_filter se ve así:
def generate_email_for_username
return if(!params[:user][:email].blank? || params[:user][:username].blank?)
params[:user][:email] = "#{params[:user][:username]}@mycustomdomain.com"
end
También estoy guardando el nombre de usuario en la tabla de usuarios, así que sé que los usuarios con el correo electrónico que termina en @ mycustomdomain.com fueron creados usando el nombre de usuario.
https://gist.github.com/867932 : Una solución para todo. Inicie sesión, olvidó la contraseña, la confirmación, las instrucciones de desbloqueo.
def self.find_for_authentication(conditions)
conditions = ["username = ? or email = ?", conditions[authentication_keys.first], conditions[authentication_keys.first]]
# raise StandardError, conditions.inspect
super
end