使用多个数据库字段来标识用户以进行登录

时间:2014-03-24 16:03:36

标签: ruby-on-rails ruby ruby-on-rails-4

我正在开发一个RoR应用程序,要求用户可以使用以下4种可能的标识符登录:电子邮件,家庭电话号码,主要电话号码或工作电话号码#。我目前的解决方案是使用正则表达式来查看它是否是正确形成的电子邮件地址,如果不是,则连续搜索三种类型的电话字段中的每一种的用户表。

# did the user try to log in with an email?
if is_email params[:session][:login_param]
    user = User.find_by(email: params[:session][:login_param].downcase)
else    # if the login parameter isn't an email...
    # try finding a user with this home number
    user = User.find_by(phone_home: params[:session][:login_param])
    if user.nil?
        # try finding a user with this cell number
        user = User.find_by(phone_cell: params[:session][:login_param])
    end
    if user.nil?
        # try finding a user with this work number
        user = User.find_by(phone_work: params[:session][:login_param])
    end
end
# verify password with user.authenticate()

我的问题是,如果记录在表格末尾附近的用户尝试使用他们的工作号登录,则有可能搜索整个用户表3次;我不知道这是否会使登录生产过于缓慢。

有更清洁/更快的方法吗?

3 个答案:

答案 0 :(得分:1)

在Rails 4中,您可以将where子句与find_by方法结合使用,以便为单个用户进行查询:

login = params[:session][:login_param]
password = <however you are handling password>
User.where("email = ? OR phone_home = ? OR phone_cell = ? or phone_work = ?", login, login, login, login).find_by(encrypted_password: password)

这为您提供了所需的查询:

SELECT "users".* FROM "users" WHERE (email = <> OR phone_home = <> OR phone_cell = <> OR phone_work = <>) AND "users"."encrypted_password" = <> LIMIT 1

将返回符合条件的用户,如果找不到用户,则会返回nil

答案 1 :(得分:1)

与engineermnky方法类似,但有一些改进:

class User
  # Pick the method name you prefer 
  def self.authenticate_with(login, password)
    where('(email = :login OR phone_home = :login OR phone_cell = :login OR phone_work = :login) AND password = :password', { login: login.downcase, password: password })
  end
end

@user = User.authenticate_with(params[:session][:login_params], <password>)

答案 2 :(得分:-2)

只要您在创建时验证电话号码的唯一性,这就应该有效。

class User 
  def self.find_by_login(login)
      where("email = ? OR phone_home = ? OR phone_cell = ? OR phone_work = ?",*([login] * 4)).limit(1)
  end
end
@user = User.find_by_login(params[:session][:login_params])
#this will generate the following query with login_params = 'hello'
#=> "SELECT \"users\".* FROM \"users\"  WHERE (email = 'hello' OR phone_home = 'hello' OR phone_cell = 'hello' OR phone_work = 'hello')"
#authenticate @user 

编辑

正如所指出的那样,原始版本使用where返回了一个数组,因此我添加了limit(1)来返回User的实例。同样如上所述,您必须验证每个字段的唯一性,以确保它将提取正确的记录。