web-dev-qa-db-fra.com

RoR Devise: Connectez-vous avec le nom d'utilisateur OR email

Quelle est la meilleure façon de permettre aux utilisateurs de se connecter avec leur adresse e-mail OR leur nom d'utilisateur? J'utilise warden + devise pour l'authentification. Je pense que ce ne sera probablement pas trop difficile à faire, mais je suppose que j'ai besoin de quelques conseils ici sur l'endroit où mettre toutes les choses nécessaires. Peut-être que devise fournit déjà cette fonctionnalité? Comme dans le fichier config/initializers/devise.rb, vous écririez:

config.authentication_keys = [ :email, :username ]

Pour exiger à la fois le nom d'utilisateur et l'adresse e-mail pour la connexion. Mais je veux vraiment avoir un seul champ pour le nom d'utilisateur et l'e-mail et n'en exiger qu'un seul. Je vais juste visualiser qu'avec un peu d'art ASCII, cela devrait ressembler à ceci dans la vue:

Username or Email:
[____________________]

Password:
[____________________]

[Sign In]
59
Patrick Oscity

J'ai trouvé une solution au problème. Je ne suis pas tout à fait satisfait (je préfère avoir un moyen de le spécifier dans l'initialiseur), mais cela fonctionne pour l'instant. Dans le modèle utilisateur, j'ai ajouté la méthode suivante:

def self.find_for_database_authentication(conditions={})
  find_by(username: conditions[:email]) || find_by(email: conditions[:email])
end

Comme l'ont souligné @sguha et @Chetan, ne autre grande ressource est disponible sur le wiki officiel de l'appareil .

46
Patrick Oscity
25
Chetan
def self.find_for_authentication(conditions)
  conditions = ["username = ? or email = ?", conditions[authentication_keys.first], conditions[authentication_keys.first]]
  # raise StandardError, conditions.inspect
  super
end

tilisez leur exemple!

9

Assurez-vous que vous avez déjà ajouté le champ de nom d'utilisateur et ajoutez le nom d'utilisateur à attr_accessible. Créer un attribut virtuel de connexion dans les utilisateurs

1) Ajouter une connexion en tant qu'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) Ajouter une connexion à attr_accessible

attr_accessible :login

Dites à Devise d'utiliser: connectez-vous dans les clés d'authentification

Modifiez config/initializers/devise.rb pour avoir:

config.authentication_keys = [ :login ]

Écraser la méthode find_for_database_authentication de Devise dans Users

# 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

Mettre à jour vos vues Assurez-vous d'avoir les vues Devise dans votre projet afin de pouvoir les personnaliser

remove <%= f.label :email %>
remove <%= f.email_field :email %>
add <%= f.label :login %>   
add <%= f.text_field :login %>
7
aku

Platforma Tec (auteur de la conception) a publié une solution sur son wiki github qui utilise une stratégie d'authentification sous-jacente de Warden plutôt que de se connecter au contrôleur:

https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-sign-in-using-their-username-or-email-address

(Une réponse antérieure avait un lien brisé, qui, je crois, était destiné à relier à cette ressource.)

2
Mike Jarema

https://Gist.github.com/867932 : Une solution pour tout. Connectez-vous, mot de passe oublié, confirmation, instructions de déverrouillage.

2
Kulbir Saini

Avec squeel gem vous pouvez faire:

  def self.find_for_authentication(conditions={})
    self.where{(email == conditions[:email]) | (username == conditions[:email])}.first
  end
1
tarmo

J'ai écrit comme ça et ça marche. Je ne sais pas si c'est "une solution laide", mais si je trouve une meilleure solution, je vous le ferai savoir ...

 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
1
maverick

J'utilise un hack rapide pour cela, pour éviter de changer le code spécifique d'un appareil et l'utiliser pour mon scénario spécifique (je l'utilise en particulier pour une API où les applications mobiles peuvent créer des utilisateurs sur le serveur).

J'ai ajouté un before_filter à tous les contrôleurs de devise où, si le nom d'utilisateur est transmis, je génère un e-mail à partir du nom d'utilisateur ("# {params [: user] [: username]} @ mycustomdomain.com") et enregistre l'utilisateur. Pour tous les autres appels également, je génère l'e-mail en fonction de la même logique. Mon before_filter ressemble à ceci:

def generate_email_for_username
    return if(!params[:user][:email].blank? || params[:user][:username].blank?)
    params[:user][:email] = "#{params[:user][:username]}@mycustomdomain.com"
end

J'enregistre également le nom d'utilisateur dans le tableau des utilisateurs, donc je sais que les utilisateurs dont le courrier électronique se termine par @ mycustomdomain.com ont été créés à l'aide du nom d'utilisateur.

1
amit_saxena

Si vous utilisez MongoDB (avec MongoId), vous devez interroger différemment:

  def self.find_for_database_authentication(conditions={})
    self.any_of({name: conditions[:email]},{email: conditions[:email]}).limit(1).first
  end

juste pour que ce soit quelque part en ligne.

1
CamelCamelCamel

Voici une solution Rails qui remodèle la réponse de @ padde. Elle utilise find_by d'ActiveRecord pour simplifier les appels, garantit qu'il n'y a qu'un seul appel basé sur l'expression régulière et prend également en charge les identifiants numériques si vous souhaitez autoriser cela ( utile pour les scripts/API) .Le regex pour le courrier électronique est aussi simple qu'il doit l'être dans ce contexte; il suffit de vérifier la présence d'un @ car je suppose que votre nom d'utilisateur validtor n'autorise pas les caractères @.

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

Comme le wiki et la réponse de @ aku, je recommanderais également de créer un nouveau paramètre: login en utilisant attr_accessible et authentication_keys au lieu d'utiliser: email ici. (Je l'ai gardé comme: email dans l'exemple pour montrer la solution rapide.)

1
mahemoff