You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails 5中参照Devise密码重置流程实现用户名重置功能的技术咨询

Rails Devise 用户名重置功能后端实现步骤

1. 新增数据库字段

首先生成迁移文件添加用户名重置所需的令牌和发送时间字段:

rails generate migration AddUsernameResetColumnsToUsers

编辑生成的迁移文件内容:

class AddUsernameResetColumnsToUsers < ActiveRecord::Migration[7.0]
  def change
    add_column :users, :username_reset_token, :string
    add_column :users, :username_reset_sent_at, :datetime
    add_index :users, :username_reset_token, unique: true
  end
end

执行迁移:

rails db:migrate

2. User模型添加业务逻辑

在app/models/user.rb中添加和Devise密码重置逻辑对齐的方法:

class User < ApplicationRecord
  # 原有Devise配置保留

  # 生成用户名重置令牌
  def generate_username_reset_token
    raw, enc = Devise.token_generator.generate(self.class, :username_reset_token)
    self.username_reset_token = enc
    self.username_reset_sent_at = Time.now.utc
    save(validate: false)
    raw
  end

  # 发送用户名重置邮件
  def send_username_reset_instructions
    token = generate_username_reset_token
    UserMailer.username_reset_instructions(self, token).deliver_now
  end

  # 校验令牌有效性(默认6小时过期,和Devise密码重置过期时间保持一致)
  def valid_username_reset_token?(token)
    return false if username_reset_sent_at.nil? || username_reset_sent_at < 6.hours.ago
    Devise.token_generator.digest(self.class, :username_reset_token, token) == username_reset_token
  end

  # 重置用户名后清除令牌
  def clear_username_reset_token
    update(username_reset_token: nil, username_reset_sent_at: nil)
  end
end

3. 配置路由

在config/routes.rb中添加Devise作用域下的自定义路由:

devise_scope :user do
  # 提交邮箱发送重置邮件
  post 'users/forgot_username', to: 'username_resets#send_instructions'
  # 跳转重置用户名页面
  get 'users/reset_username', to: 'username_resets#edit'
  # 提交新用户名
  patch 'users/reset_username', to: 'username_resets#update'
end

4. 实现控制器逻辑

新建app/controllers/username_resets_controller.rb:

class UsernameResetsController < ApplicationController
  skip_before_action :authenticate_user!

  # 处理邮箱提交请求
  def send_instructions
    user = User.find_by(email: params[:email].downcase)
    # 无论邮箱是否存在都返回相同提示,避免账号枚举
    if user
      user.send_username_reset_instructions
    end
    flash[:notice] = "如果该邮箱已绑定账号,我们会发送用户名重置链接到您的邮箱,请查收"
    redirect_to new_user_session_path
  end

  # 渲染重置用户名页面
  def edit
    @token = params[:token]
    user = User.find_by(username_reset_token: Devise.token_generator.digest(User, :username_reset_token, @token))
    unless user&.valid_username_reset_token?(params[:token])
      flash[:alert] = "用户名重置链接无效或已过期"
      redirect_to new_user_session_path
    end
  end

  # 处理用户名更新请求
  def update
    user = User.find_by(username_reset_token: Devise.token_generator.digest(User, :username_reset_token, params[:token]))
    unless user&.valid_username_reset_token?(params[:token])
      flash[:alert] = "用户名重置链接无效或已过期"
      redirect_to new_user_session_path and return
    end

    if user.update(username: params[:new_username])
      user.clear_username_reset_token
      flash[:notice] = "用户名重置成功,请使用新用户名登录"
      redirect_to new_user_session_path
    else
      flash[:alert] = user.errors.full_messages.join(", ")
      redirect_to users_reset_username_path(token: params[:token])
    end
  end
end

5. 配置邮件发送逻辑

在app/mailers/user_mailer.rb中添加邮件方法:

class UserMailer < ApplicationMailer
  default from: '你的应用发件邮箱地址'

  def username_reset_instructions(user, token)
    @user = user
    @reset_url = users_reset_username_url(token: token)
    mail(to: @user.email, subject: '用户名重置请求')
  end
end

新建邮件视图app/views/user_mailer/username_reset_instructions.html.erb,内容可参考Devise默认密码重置邮件调整即可。

注意事项

  • 用户名更新的校验规则需要和注册时的规则保持一致,比如长度、特殊字符限制、唯一性校验等
  • 令牌过期时间可根据业务需求调整,不要设置过长的有效时间
  • 不要在错误提示中区分邮箱是否存在,避免恶意用户枚举系统已注册账号

内容的提问来源于stack exchange,提问作者Ben Bagley

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 02:39:03