Michael Hartl 第 12 章密码重置重定向到主页
Michael Hartl Chap 12 Password Reset redirects to homepage
当通过电子邮件发送重置密码link时,它似乎不起作用。由于以下原因,单击它会导致重定向到主页:
Filter chain halted as :valid_user rendered or redirected
这里是valid_user
# Confirms a valid user.
def valid_user
unless (@user && @user.activated? &&
@user.authenticated?(:reset, params[:id]))
redirect_to root_url
end
end
密码重置控制器
class PasswordResetsController < ApplicationController
before_action :get_user, only: [:edit, :update]
before_action :valid_user, only: [:edit, :update]
before_action :check_expiration, only: [:edit, :update] # Case (1)
def new
end
def create
@user = User.find_by(email: params[:password_reset][:email].downcase)
if @user
@user.create_reset_digest
@user.send_password_reset_email
flash[:info] = "Email sent with password reset instructions"
redirect_to root_url
else
flash.now[:danger] = "Email address not found"
render 'new'
end
end
def edit
end
def update
if params[:user][:password].empty? # Case (3)
@user.errors.add(:password, "can't be empty")
render 'edit'
elsif @user.update_attributes(user_params) # Case (4)
log_in @user
flash[:success] = "Password has been reset."
redirect_to @user
else
render 'edit' # Case (2)
end
end
private
def user_params
params.require(:user).permit(:password, :password_confirmation)
end
# Before filters
def get_user
@user = User.find_by(email: params[:email])
end
# Confirms a valid user.
def valid_user
unless (@user && @user.activated? &&
@user.authenticated?(:reset, params[:id]))
redirect_to root_url
end
end
# Checks expiration of reset token.
def check_expiration
if @user.password_reset_expired?
flash[:danger] = "Password reset has expired."
redirect_to new_password_reset_url
end
end
end
User.rb
class User < ActiveRecord::Base
attr_accessor :remember_token, :activation_token, :reset_token
before_save :downcase_email
before_create :create_activation_digest
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :username, presence: true, length: { maximum: 50 }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token.
def User.new_token
SecureRandom.urlsafe_base64
end
# Remembers a user in the database for use in persistent sessions.
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
# Returns true if the given token matches the digest.
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
# Forgets a user.
def forget
update_attribute(:remember_digest, nil)
end
# Activates an account.
def activate
update_attribute(:activated, true)
update_attribute(:activated_at, Time.zone.now)
end
# Sends activation email.
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
# Sets the password reset attributes.
def create_reset_digest
self.reset_token = User.new_token
update_attribute(:reset_digest, User.digest(reset_token))
update_attribute(:reset_sent_at, Time.zone.now)
end
# Sends password reset email.
def send_password_reset_email
UserMailer.password_reset(self).deliver_now
end
# Returns true if a password reset has expired.
def password_reset_expired?
reset_sent_at < 2.hours.ago
end
private
# Converts email to all lower-case.
def downcase_email
self.email = email.downcase
end
# Creates and assigns the activation token and digest.
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end
我似乎无法弄清楚为什么要这样做。当我删除经过身份验证的? valid_user 方法中的部分代码,它仍在重定向到主页。
我假设您的 application_controller.rb
中有 before_filter :valid_user
。如果是这样,请尝试向 valid_user
添加条件以防止检查身份验证和激活。例如,您可以调整以下方法以满足您的需要:
def valid_user
unless controller_name == "password_resets"
unless (@user && @user.activated? &&
@user.authenticated?(:reset, params[:id]))
redirect_to root_url
end
end
end
如果您的 password_resets_controller.rb
中有 before_filter :valid_user
,您可以将其删除,因为用例包括:用户在尝试重置 his/her 密码时将不会通过身份验证。
问题是我正在使用我创建的用户对此进行测试,但由于我创建用户的方式而没有通过 "activation" 方法。我测试了一个用户在网站上完成注册过程并且它有效。
当通过电子邮件发送重置密码link时,它似乎不起作用。由于以下原因,单击它会导致重定向到主页:
Filter chain halted as :valid_user rendered or redirected
这里是valid_user
# Confirms a valid user.
def valid_user
unless (@user && @user.activated? &&
@user.authenticated?(:reset, params[:id]))
redirect_to root_url
end
end
密码重置控制器
class PasswordResetsController < ApplicationController
before_action :get_user, only: [:edit, :update]
before_action :valid_user, only: [:edit, :update]
before_action :check_expiration, only: [:edit, :update] # Case (1)
def new
end
def create
@user = User.find_by(email: params[:password_reset][:email].downcase)
if @user
@user.create_reset_digest
@user.send_password_reset_email
flash[:info] = "Email sent with password reset instructions"
redirect_to root_url
else
flash.now[:danger] = "Email address not found"
render 'new'
end
end
def edit
end
def update
if params[:user][:password].empty? # Case (3)
@user.errors.add(:password, "can't be empty")
render 'edit'
elsif @user.update_attributes(user_params) # Case (4)
log_in @user
flash[:success] = "Password has been reset."
redirect_to @user
else
render 'edit' # Case (2)
end
end
private
def user_params
params.require(:user).permit(:password, :password_confirmation)
end
# Before filters
def get_user
@user = User.find_by(email: params[:email])
end
# Confirms a valid user.
def valid_user
unless (@user && @user.activated? &&
@user.authenticated?(:reset, params[:id]))
redirect_to root_url
end
end
# Checks expiration of reset token.
def check_expiration
if @user.password_reset_expired?
flash[:danger] = "Password reset has expired."
redirect_to new_password_reset_url
end
end
end
User.rb
class User < ActiveRecord::Base
attr_accessor :remember_token, :activation_token, :reset_token
before_save :downcase_email
before_create :create_activation_digest
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :username, presence: true, length: { maximum: 50 }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token.
def User.new_token
SecureRandom.urlsafe_base64
end
# Remembers a user in the database for use in persistent sessions.
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
# Returns true if the given token matches the digest.
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
# Forgets a user.
def forget
update_attribute(:remember_digest, nil)
end
# Activates an account.
def activate
update_attribute(:activated, true)
update_attribute(:activated_at, Time.zone.now)
end
# Sends activation email.
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
# Sets the password reset attributes.
def create_reset_digest
self.reset_token = User.new_token
update_attribute(:reset_digest, User.digest(reset_token))
update_attribute(:reset_sent_at, Time.zone.now)
end
# Sends password reset email.
def send_password_reset_email
UserMailer.password_reset(self).deliver_now
end
# Returns true if a password reset has expired.
def password_reset_expired?
reset_sent_at < 2.hours.ago
end
private
# Converts email to all lower-case.
def downcase_email
self.email = email.downcase
end
# Creates and assigns the activation token and digest.
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end
我似乎无法弄清楚为什么要这样做。当我删除经过身份验证的? valid_user 方法中的部分代码,它仍在重定向到主页。
我假设您的 application_controller.rb
中有 before_filter :valid_user
。如果是这样,请尝试向 valid_user
添加条件以防止检查身份验证和激活。例如,您可以调整以下方法以满足您的需要:
def valid_user
unless controller_name == "password_resets"
unless (@user && @user.activated? &&
@user.authenticated?(:reset, params[:id]))
redirect_to root_url
end
end
end
如果您的 password_resets_controller.rb
中有 before_filter :valid_user
,您可以将其删除,因为用例包括:用户在尝试重置 his/her 密码时将不会通过身份验证。
问题是我正在使用我创建的用户对此进行测试,但由于我创建用户的方式而没有通过 "activation" 方法。我测试了一个用户在网站上完成注册过程并且它有效。