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

Rails使用Devise gem自定义注册流程,实现先填邮箱发确认链接再显示注册表单

Devise 自定义两步注册流程实现方案

前置准备

  • 确保你已经正常安装并初始化了devise gem,用户模型为默认的User
  • 先给User模型添加注册标识字段,执行迁移命令:
rails g migration AddRegistrationFieldsToUsers registration_token:string:index registration_token_expires_at:datetime confirmed_at:datetime
rails db:migrate

第一步:调整模型与Devise基础配置

  • 修改app/models/user.rb,添加核心逻辑:
class User < ApplicationRecord
  # 保留你原有devise模块,新增confirmable模块
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :confirmable
  # 邮箱格式校验
  validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
  # 生成唯一注册token,默认24小时有效期
  def generate_registration_token!
    self.registration_token = SecureRandom.urlsafe_base64(nil, false)
    self.registration_token_expires_at = 24.hours.from_now
    save!
  end
  # 校验token是否有效
  def valid_registration_token?
    registration_token_expires_at > Time.now && encrypted_password.blank?
  end
end
  • 修改config/initializers/devise.rb,关闭默认注册后自动登录逻辑:
config.sign_in_after_sign_up = false

第二步:配置路由与自定义控制器

  • 修改config/routes.rb,添加自定义路由:
Rails.application.routes.draw do
  # 重定向devise注册逻辑到自定义控制器
  devise_for :users, controllers: { registrations: 'users/registrations' }
  # 第二步注册相关路由
  get '/complete_registration', to: 'users/registrations#complete_registration', as: :complete_registration
  put '/update_registration', to: 'users/registrations#update_registration', as: :update_registration
  # 其他原有路由保留
end
  • 新建自定义注册控制器app/controllers/users/registrations_controller.rb:
class Users::RegistrationsController < Devise::RegistrationsController
  # 第一步:展示仅含邮箱的表单
  def new
    @user = User.new
  end
  # 第一步提交邮箱的处理逻辑
  def create
    @user = User.find_or_initialize_by(email: sign_up_params[:email])
    # 校验邮箱是否已完成注册
    if @user.persisted? && @user.encrypted_password.present?
      flash[:alert] = "该邮箱已注册,请直接登录"
      return redirect_to new_user_session_path
    end
    # 校验邮箱格式后发送确认邮件
    if @user.valid?
      @user.generate_registration_token!
      UserMailer.registration_confirmation(@user).deliver_later
      flash[:notice] = "注册确认邮件已发送至你的邮箱,请点击邮件内链接完成后续注册"
      redirect_to root_path
    else
      render :new
    end
  end
  # 第二步:展示完整注册表单
  def complete_registration
    @user = User.find_by(registration_token: params[:token])
    if @user.nil? || !@user.valid_registration_token?
      flash[:alert] = "注册链接无效或已过期"
      redirect_to new_user_registration_path
    end
  end
  # 第二步提交完整注册信息的处理逻辑
  def update_registration
    @user = User.find_by(registration_token: params[:token])
    if @user.nil? || !@user.valid_registration_token?
      flash[:alert] = "注册链接无效或已过期"
      return redirect_to new_user_registration_path
    end
    if @user.update(user_params)
      # 标记邮箱已确认,无需二次验证
      @user.confirm
      # 销毁已使用的token,避免重复提交
      @user.update(registration_token: nil, registration_token_expires_at: nil)
      flash[:notice] = "注册成功,请登录"
      redirect_to new_user_session_path
    else
      render :complete_registration
    end
  end
  private
  # 第一步仅允许提交邮箱
  def sign_up_params
    params.require(:user).permit(:email)
  end
  # 第二步允许提交的字段,可按需新增昵称、手机号等
  def user_params
    params.require(:user).permit(:password, :password_confirmation, :name)
  end
end

第三步:实现注册确认邮件逻辑

  • 生成邮件发送器:
rails g mailer UserMailer registration_confirmation
  • 修改app/mailers/user_mailer.rb配置:
class UserMailer < ApplicationMailer
  # 替换为你的官方发件邮箱
  default from: 'admin@yourdomain.com'
  def registration_confirmation(user)
    @user = user
    @registration_link = complete_registration_url(token: user.registration_token)
    mail(to: @user.email, subject: '请完成你的账号注册')
  end
end
  • 编写邮件模板app/views/user_mailer/registration_confirmation.html.erb:
<h1>欢迎注册</h1>
<p>请点击下方链接完成注册,链接24小时内有效:</p>
<%= link_to '完成注册', @registration_link %>

第四步:编写视图文件

  • 第一步邮箱提交页app/views/users/registrations/new.html.erb:
<h2>注册账号</h2>
<%= form_with model: @user, url: user_registration_path do |f| %>
  <% if @user.errors.any? %>
    <div id="error_explanation">
      <ul>
        <% @user.errors.each do |error| %>
          <li><%= error.full_message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>
  <div class="field">
    <%= f.label :email, "邮箱" %><br />
    <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
  </div>
  <div class="actions">
    <%= f.submit "提交" %>
  </div>
<% end %>
  • 第二步完整注册页app/views/users/registrations/complete_registration.html.erb:
<h2>完成注册</h2>
<%= form_with model: @user, url: update_registration_path(token: @user.registration_token), method: :put do |f| %>
  <% if @user.errors.any? %>
    <div id="error_explanation">
      <ul>
        <% @user.errors.each do |error| %>
          <li><%= error.full_message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>
  <div class="field">
    <%= f.label :email, "邮箱" %><br />
    <%= f.email_field :email, value: @user.email, readonly: true %>
  </div>
  <div class="field">
    <%= f.label :password, "密码" %><br />
    <%= f.password_field :password, autocomplete: "new-password" %>
  </div>
  <div class="field">
    <%= f.label :password_confirmation, "确认密码" %><br />
    <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
  </div>
  <!-- 此处可新增你需要的其他注册字段 -->
  <div class="actions">
    <%= f.submit "完成注册" %>
  </div>
<% end %>

注意事项

  • 生产环境需要先配置好邮件发送服务,避免确认邮件被识别为垃圾邮件
  • 可根据业务需求调整注册token的有效时长,以及增加更多注册字段校验

内容的提问来源于stack exchange,提问作者Hà Mai

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 15:00:05