RoR Devise: 使用用户名或电子邮件登录

63

如何让用户可以使用他们的电子邮件地址或用户名登录?我正在使用warden + devise进行身份验证。我认为这可能不是太难,但我需要一些建议,告诉我需要放置所有必要的内容的位置。也许devise已经提供了这个功能?例如,在config/initializers/devise.rb中写入:

config.authentication_keys = [ :email, :username ]

要求用户在登录时需要提供用户名和电子邮件地址。但我只想要一个字段来同时输入用户名和电子邮件,并且只需要其中一项填写。我用一些ASCII艺术来展示这个想法,在视图中应该会像这样:

Username or Email:
[____________________]

Password:
[____________________]

[Sign In]

3
标题不应该是“RoR Devise:使用用户名或电子邮件登录”吗? - Moox
这些用户名保证是唯一的吗? - scunliffe
@Moox:你说得对,打错字了很抱歉。 @scunliffe:是的,用户名是唯一的。 - Patrick Oscity
11个回答

47

我已经找到了解决这个问题的方法。虽然我不太满意它(我更希望有一种在初始化器中指定的方法),但目前它可以工作。在用户模型中,我添加了以下方法:

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

正如@sguha和@Chetan指出的那样,官方devise wiki中提供了另一个很好的资源


这篇帖子中链接的博客文章已经失效。 - Gareth
8
这个也在设备Wiki上有详细说明:https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-sign-in-using-their-username-or-email-address - sguha
@sguha发布的链接是正确的方法。它甚至创建了一个虚拟属性登录,这是一种更干净的方法来完成这个任务。 - hadees
上面的方法可能应该重写为:self.find_by(username: conditions[:email]) || self.find_by(email: conditions[:email]) - Kevin Sylvestre
1
@PatrickOscity find_by!find 会抛出异常,但是 find_by 返回 nil - Kevin Sylvestre
显示剩余2条评论

28

1
这个解决方案更好,因为它在 Devise Wiki 上有文档记录。 - Khaled

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

Use their example!


使用他们的示例,它不应该是 def self.find_for_database_authentication(conditions) 吗? - Patrick Oscity
顺便提一下,使用Rails 3.0.0.beta4和他们的示例,我遇到了一个NoMethodError in Devise/sessionsController#create undefined method assert_valid_keys' for ["username = ? or email = ?", "xxx", "xxx"]:Array`错误,所以我使用了自己的解决方案。 - Patrick Oscity

7

请确保您已添加了用户名字段,并将用户名添加到attr_accessible中。在用户中创建一个登录虚拟属性。

1)将登录添加为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) 在attr_accessible中添加登录

attr_accessible :login

告诉Devise在authentication_keys中使用:login

修改config/initializers/devise.rb文件,添加以下内容:

config.authentication_keys = [ :login ]

在用户中覆盖 Devise 的 find_for_database_authentication 方法
# 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

更新你的视图 确保在项目中具有Devise视图,以便您可以自定义它们

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

在修改config/initializers/devise.rb文件时,要注意出现的:email。 例如: config.case_insensitive_keys = [:email] config.strip_whitespace_keys = [:email] 对于上述情况,我们必须手动处理。 - Aravind

2

2

1

如果您正在使用带有MongoId的MongoDB,则需要以不同的方式进行查询:

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

只是为了让它在网上有个位置。


1
我像这样写就可以了,不知道是否是“丑陋的修复”,但如果我想到更好的解决方案,我会告诉你的...
 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

使用squeel gem,您可以做到以下事情:

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

1
不需要使用 squeel gem,您也可以像这样做:User.where("username = :email OR email = :email", email: "user@site.com") - tdgs

1
我使用了一个快速的技巧,避免改变任何特定设备的代码并将其用于我的特定场景(我特别将其用于API,在此API中移动应用程序可以在服务器上创建用户)。
我已经向所有的devise控制器添加了一个before_filter,如果传递了用户名,则从用户名生成电子邮件(“#{params[:user] [:username]}@mycustomdomain.com”)并保存用户。对于所有其他调用,我也根据同样的逻辑生成电子邮件。我的before_filter如下所示:
def generate_email_for_username
    return if(!params[:user][:email].blank? || params[:user][:username].blank?)
    params[:user][:email] = "#{params[:user][:username]}@mycustomdomain.com"
end

我还在用户表中保存了用户名,因此我知道以 @mycustomdomain.com 结尾的电子邮件地址对应的用户是使用用户名创建的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接