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
相关产品推荐
相关产品推荐

