繁体   English   中英

Devise 忘记登录用户的密码

[英]Devise Forgot Password for logged in user

我想知道是否有一种方法可以在不强制我的用户注销的情况下调用“忘记密码”程序

我遇到的情况是:

  1. 用户使用 Facebook 登录,为他们生成一个假密码
  2. 然后用户想更改他们的电子邮件/姓名/密码,或者只使用非 Facebook 登录

由于 devise 需要密码才能更改这些字段,因此用户无法修改它们

我曾考虑过不强制设置密码,但这对安全性没有意义,所以我只是将字段显示为文本并通知用户按照“忘记密码”程序来设置密码和然后他们可以更改字段

那么问题是我不能简单地从用户个人资料链接到这个,因为 devise 会告诉用户他们不能在已经登录的情况下这样做。

那么有没有一种方法可以覆盖忘记密码或 /users/password/edit 方法,以便登录用户也可以执行此操作?

您无法重置密码的原因是设备尝试使用当前会话对用户进行身份验证,并且当成功时,您将自动重定向到它应该去的任何路径。 您需要的是覆盖密码控制器的编辑和更新操作,使其跳过此步骤。

这是代码。 在您的密码控制器中添加以下代码(您可以请设计为您生成控制器,或者您可以创建以下控制器)。 更新覆盖是必要的,否则登录用户将在重置密码后自动注销。 (或者,如果你希望它像你那样可以摆脱#update覆盖)

class PasswordsController < Devise::PasswordsController
  # here we need to skip the automatic authentication based on current session for the following two actions
  # edit: shows the reset password form. need to skip, otherwise it will go directly to root
  # update: updates the password, need to skip otherwise it won't even reset if already logged in
  skip_before_filter :require_no_authentication, :only => [:edit, :update]

  # we need to override the update, too.
  # After a password is reset, all outstanding sessions are gone.
  # When already logged in, sign_in is a no op, so the session will expire, too.
  # The solution is to logout and then re-login which will make the session right.
  def update
    super
    if resource.errors.empty?
      sign_out(resource_name)
      sign_in(resource_name, resource)
    end
  end
end

路线如下

# config/routes.rb
devise_for :users, :controllers => {:passwords => 'passwords'}

您可以使用@user.send_reset_password_instructions生成密码重置令牌并发送电子邮件。 如果您只是直接调用邮件程序,则不会生成密码重置令牌来验证重置。

我在这里完整的解决方案,因为我还了解到用户在点击电子邮件中的链接后必须注销,就是添加一些额外的UserController操作来实际编辑密码并保存密码。 这不是一个理想的解决方案,冷却可能以更好的方式完成,但它对我有用。

用户控制器; 添加了重置方法

    before_filter :authenticate_user!, :except => [:do_reset_password, :reset_password_edit]

    def reset_password
        id = params[:id]
        if id.nil?
          id = current_user.id
        end    
        if (!user_signed_in? || current_user.id.to_s != id.to_s)
        flash[:alert] = "You don't have that right." 
          redirect_to '/home'
          return
        end

        @user = User.find(id)
        @user.send_reset_password_instructions

        respond_to do |format|
            format.html { redirect_to '/users/edit', notice: 'You will receive an email with instructions about how to reset your password in a few minutes.' }
        end
     end


    def do_reset_password
        id = params[:id]
        if id.nil? && !current_user.nil?
          id = current_user.id
        end

        if id.nil?
            @user = User.where(:reset_password_token => params[:user][:reset_password_token]).first
        else
            @user = User.find(id)
        end
        if  @user.nil? || @user.reset_password_token.to_s != params[:user][:reset_password_token]
          flash[:alert] = "Url to reset was incorrect, please resend reset email." 
          redirect_to '/home'
          return
        end

        # there may be a better way of doing this, devise should be able to give us these messages
        if params[:user][:password] != params[:user][:password_confirmation]
            flash[:alert] = "Passwords must match." 
              redirect_to :back
              return
        end
        if @user.reset_password!(params[:user][:password],params[:user][:password_confirmation])
            @user.hasSetPassword = true
            @user.save
            respond_to do |format|
                format.html { redirect_to '/home', notice: 'Your password has been changed.' }
            end
        else
            flash[:alert] = "Invalid password, must be at least 6 charactors." 
              redirect_to :back 
        end
    end

    def reset_password_edit
        @user = User.where(:reset_password_token => params[:reset_password_token]).first
        if  @user.nil? || !@user.reset_password_period_valid?
            flash[:alert] = "Password reset period expired, please resend reset email" 
            redirect_to "/home"
            return
        end
    end

意见/设计/注册/编辑; 将视图更改为不让用户编辑需要密码的字段

    <%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
      <%= devise_error_messages! %>

      <% if !resource.hasSetPassword %>                                           
          <%= f.label :name %><br />
          <p style="line-height:24px;"><b><%= @user.name %></b></p>             
          <div><%= f.label :email %><br />
              <p style="line-height:24px;"><b><%= @user.email %> </b></p>
              <p style="position:relative; left:150px; width:420px;">
                <i>you cannot change any settings because you have not set a password <br />yet, you can do so by following the </i>
                <%= link_to "Forgot your password", "/users/reset_password" %> <i> procedure</i>
              </p>
          </div>
      <% else %>                      
          <p><%= f.label :name %><br />
          <%= f.text_field :name %></p>         
          <div><%= f.label :email %><br />
          <%= f.email_field :email %></div>

          <div><%= f.label :password %> <br />
          <%= f.password_field :password %><i>(leave blank if you don't want to change it)</i></div>

          <div><%= f.label :password_confirmation %><br />
          <%= f.password_field :password_confirmation %></div>

          <div><%= f.label :current_password %> <br />
          <%= f.password_field :current_password %>
          <i>(we need your current password to confirm your changes)</i>
          </div>
        <div><%= f.submit "Update" %></div>
      <% end %>
    <% end %>

视图/设计/邮寄者/ reset_password_instructions; 在我们的新案例中,我必须将其更改为指向正确的URL

    <p>Hello <%= @resource.email %>!</p>

    <p>Someone has requested a link to change your password, and you can do this through the link below.</p>

    <% if !@resource.hasSetPassword %>
        <p><%= link_to 'Change my password', 'http://streetsbehind.me/users/reset_password_edit?reset_password_token='+@resource.reset_password_token %></p>
    <!-- todo: there's probably a better way of doing this than just hardcoding streetsbehind.me -->
    <% else %>
        <p><%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) %></p>
    <% end %>
    <p>If you didn't request this, please ignore this email.</p>
    <p>Your password won't change until you access the link above and create a new one.</p>

意见/用户/ reset_password_edit.erb

<%= form_for(@user, :url => url_for(:action => :do_reset_password) , :html => { :method => :post }) do |f| %>

  <%= f.hidden_field :reset_password_token %>

  <div><%= f.label :password, "New password" %><br />
  <%= f.password_field :password %></div>

  <div><%= f.label :password_confirmation, "Confirm new password" %><br />
  <%= f.password_field :password_confirmation %></div>

  <div><%= f.submit "Change my password" %></div>
<% end %>

配置/ routes.rb中

get "users/reset_password"
get "users/reset_password_edit"

resource :users do
  post 'do_reset_password'
end

我改编了@user3294438 的答案以使其完美适合我。

class PasswordsController < Devise::PasswordsController
  # here we need to skip the automatic authentication based on current session for the following four actions

  # new : shows the "enter email to reset". need to skip, otherwise it will go directly to root
  # create : launches the reset email
  # edit: shows the reset password form. need to skip, otherwise it will go directly to root
  # update: updates the password, need to skip otherwise it won't even reset if already logged in
  skip_before_action :require_no_authentication, :only => [:new, :create, :edit, :update]

  # we need to override the update, too.
  # After a password is reset, all outstanding sessions are gone.
  # When already logged in, sign_in is a no op, so the session will expire, too.
  # The solution is to logout and then re-login which will make the session right.
  def update
    super
    if resource.errors.empty?
      sign_out(resource_name)
      sign_in(resource_name, resource)
    end
  end

  private

  # Overriding this method allows to show a nice flash message to the signed-in user that just 
  # asked for a password reset by email. Otherwise he gets a "you are already signed in" falsh error
  def after_sending_reset_password_instructions_path_for(resource_name)
    if current_user
      flash[:info] = I18n.t "devise.passwords.send_instructions"
      return root_path
    end

    super
  end
end

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM