在用户对象上使用基于角色的枚举后,加密密码会有所不同吗?

时间:2015-05-22 02:10:21

标签: ruby-on-rails ruby authentication encryption

我正在创建一个在注册过程中完美运行的用户登录。当用户填写“注册”表单并提交时,会按照预期将用户登录到其帐户中。但是当我注销并尝试使用登录表单再次登录时,它将不会对用户进行身份验证,即使凭据确实是正确的。

我的应用有三种不同类型的用户角色:播放器所有者 admin 。我相信它与我在注册期间将角色持久保存到用户时有关,这导致登录时的身份验证无法正常工作。

User.rb

class User < ActiveRecord::Base
  # Roles
  enum role: [:player => 0, :owner => 1, :admin => 2]
  after_initialize :set_default_role, :if => :new_record?

  # Validations
  validates :username, presence: true
  validates :password, presence: true
  # validates_confirmation_of :password
  before_save :encrypt_password

  attr_accessor :password_confirmation, :role_type

  def set_default_role
      self.role ||= :player
  end

  def encrypt_password
      self.password_salt = BCrypt::Engine.generate_salt
      self.password = BCrypt::Engine.hash_secret(password, self.password_salt)
  end

  def self.authenticate(username, password)
      user = User.where(username: username).first

      if user && user.password == BCrypt::Engine.hash_secret(password, user.password_salt)
          return user
      else
          return nil
      end
  end
end

users_controller.rb

class UsersController < ApplicationController
  include ApplicationHelper

  before_filter :current_user

  def index
    @user = User.new
  end

  # Display the SignUp Form
  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)

    # Save to the database
    if @user.save
        flash[:notice] = "Welcome, #{@user.username}!"
        session[:user_id] = @user.id

        # Check if they selected Owner checkbox
        if user_params[:role_type].to_i == 1
            @user.owner!
        else
            @user.player!
        end

        redirect_accordingly(@user)
    else
        flash[:alert] = "Some of the info you provided needs tweaking."
        render :new
    end
  end

  private
  def user_params
      params.require(:user).permit(:username, :password, :password_confirmation, :email, :role_type)
  end
end

sessions_controller.rb - 处理登录的位置

class SessionsController < ApplicationController
include ApplicationHelper

#Login Process
def create

    # authenticate method returns nil when a role is 
    # persisted to the user during registration
    # e.g. when @user.owner! is used in the UsersController#create

    if @user = User.authenticate(params[:username], params[:password])
        flash[:notice] = "Welcome back, #{@user.username}!"
        session[:user_id] = @user.id
        redirect_accordingly(@user)
    else
        flash.now[:alert] = "Invalid username or password. #{@user}"
        render :new
    end
end

#Login Form
def new
    @user = User.new
end

#Log Out
def destroy
    session.delete(:user_id)
    redirect_to root_path, :notice => "You have logged out."
end
end

在UsersController #create中,条件if语句在注册期间向用户保留角色,例如@user.owner!@user.player!,因为它将正确的整数添加到数据库role 1}}列,并记录新用户。

我退出后,尝试使用登录表单重新登录用户,但是没有成功。 User.authenticate(params[:username], params[:password])方法返回nil。所以我开始在rails控制台中剖析方法:

假设我在我的数据库中有这个例子:

+----+--------------+--------------------------------------------------------------+-------------------------------+-------------------------+---------------------+---------------------+------+
| id | username     | password                                                     | password_salt                 | email                   | created_at          | updated_at          | role |
+----+--------------+--------------------------------------------------------------+-------------------------------+-------------------------+---------------------+---------------------+------+
| 27 | grahamsutt12 | $2a$10$T6cRC/F0NQOeHhorQfxLjOkVQfOPwvUU0eEbKhlS.Pa4aD2TRvxvy | $2a$10$T6cRC/F0NQOeHhorQfxLjO | graham@gmail.com        | 2015-05-22 01:19:24 | 2015-05-22 01:19:24 |    0 |
| 28 | mike12       | $2a$10$S7vOdf43uhMU0/RUOc/vv.VIJzaGetvnB88kt/IzCEuFK1JgZBzBO | $2a$10$S7vOdf43uhMU0/RUOc/vv. | mike@gmail.com          | 2015-05-22 02:07:33 | 2015-05-22 02:07:33 |    1 |
+----+--------------+--------------------------------------------------------------+-------------------------------+-------------------------+---------------------+---------------------+------+

#password for mike12 is "mikerton"

# Returns incorrectly
User.authenticate("mike12", "mikerton") 
User Load (59.0ms)  SELECT  `users`.* FROM `users` WHERE `users`.`username` = 'mike12'  ORDER BY `users`.`id` ASC LIMIT 1
=> nil 

### Now, diving in to the authenticate method ###

# Returns correctly
user = User.where(username: "mike12").first
User Load (56.9ms)  SELECT  `users`.* FROM `users` WHERE `users`.`username` = 'mike12'  ORDER BY `users`.`id` ASC LIMIT 1
=> #<User id: 28, username: "mike12", password: "$2a$10$S7vOdf43uhMU0/RUOc/vv.VIJzaGetvnB88kt/IzCEu...", password_salt: "$2a$10$S7vOdf43uhMU0/RUOc/vv.", email: "mike@gmail.com", created_at: "2015-05-22 02:07:33", updated_at: "2015-05-22 02:07:33", role: 1>


#!!! Passwords are different from each other? !!!#
BCrypt::Engine.hash_secret("mikerton", user.password_salt)
=> "$2a$10$S7vOdf43uhMU0/RUOc/vv.rus89JjBDd2Mna6aRD8xgkMEO3FEPzK"

user.password
=> "$2a$10$S7vOdf43uhMU0/RUOc/vv.VIJzaGetvnB88kt/IzCEuFK1JgZBzBO"

正如您在两次加密中vv.后看到的密码不同。

此外,我应该在SignUp期间使用@user.owner@user.player时添加加密为相同,但是用户的角色出现在1

为什么这会改变密码加密?如何使用enum使用“Rails方式”正确地将角色存储在数据库中?

1 个答案:

答案 0 :(得分:2)

所以我做的方式如下:

在我的模型中,定义我的枚举:

<%= f.label :role, "Account Type", class: "label-hidden" %>
<%= f.select :role, options_for_user_role %>

然后在我的用户表单中,定义角色:

def options_for_user_role
 [
   ['Select Account Type', ' '],
   ['Player','player'],
   ['Owner','owner']
 ]
end

然后在我的用户帮助中,我已经定义了选项。基本上,您需要发送字符串而不是整数。

def user_params
  params.require(:user).permit(:username, :password, :password_confirmation, :email, :role)
end

然后在您的控制器中,确保将角色添加到您允许的参数

# Check if they selected Owner checkbox
    if user_params[:role_type].to_i == 1
        @user.owner!
    else
        @user.player!
    end

然后在您的用户控制器创建方法中,如果role_type为1或0,则不需要检查代码,您可以将其删除(不需要):

validates :role, presence: true, inclusion: { in: ["player", "owner", "admin"] }

我还在我的用户模型中添加了一些验证,以确保用户具有角色:

if !@user.admin? && @user.save
    flash[:notice] = "Welcome, #{@user.username}!"
    session[:user_id] = @user.id

    redirect_accordingly(@user)
else
    flash[:alert] = "Some of the info you provided needs tweaking."
    render :new
end

现在最后要注意的是,用户可以编辑html并输入&#34; admin&#34;作为选项值并尝试注册为管理员。因此,您希望在create method中的users_controller中对此进行限制:

localhost

希望有道理!让我知道,如果这不起作用或某些事情没有意义。快乐的编码!