You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Rails中Devise多角色重置密码邮件配置及实现方案问询

解决Rails Devise中管理员与普通用户重置密码邮件的角色区分问题

我来一步步帮你搞定这个问题——核心就是让Devise能根据用户角色(admin字段)正确匹配资源、生成对应子域名的重置链接,并且分发不同的邮件内容。

一、先确保@resource正确对应管理员:路由与控制器分层

首先要从路由和控制器层面隔离普通用户与管理员的密码重置流程,避免交叉匹配。

1. 拆分Devise路由

config/routes.rb里分别配置主域名(普通用户)和admin子域名(管理员)的Devise路由,指定不同的控制器和路由别名:

# 普通用户密码重置(主域名)
devise_for :users, 
           controllers: { passwords: 'users/passwords' },
           path: 'passwords'

# 管理员密码重置(admin子域名)
constraints subdomain: 'admin' do
  devise_for :users,
             as: 'admin',  # 生成管理员专属路由辅助方法,比如admin_edit_password_path
             controllers: { passwords: 'admin/passwords' },
             path: 'admins/passwords'
end

2. 自定义Passwords控制器,精准匹配资源

分别创建两个Passwords控制器,重写find_resource方法,确保只返回对应角色的用户:

普通用户控制器(app/controllers/users/passwords_controller.rb)

class Users::PasswordsController < Devise::PasswordsController
  private

  # 只匹配非管理员用户
  def find_resource
    User.find_by(email: params[:email], admin: false) || super
  end
end

管理员控制器(app/controllers/admin/passwords_controller.rb)

class Admin::PasswordsController < Devise::PasswordsController
  private

  # 只匹配管理员用户
  def find_resource
    User.find_by(email: params[:email], admin: true) || super
  end
end

这样一来,当用户在admin子域名提交重置请求时,只会找到admin: true的用户,主域名下只会匹配普通用户,从根源上保证了@resource的正确性。

二、实现角色专属的重置密码邮件

接下来要让Devise根据用户角色发送不同内容、不同链接的邮件,需要自定义Devise邮件器。

1. 创建自定义Devise邮件器

app/mailers/custom_devise_mailer.rb里继承Devise默认邮件器,重写重置密码方法:

class CustomDeviseMailer < Devise::Mailer
  helper :application
  include Devise::Controllers::UrlHelpers
  default template_path: 'devise/mailer'

  def reset_password_instructions(record, token, opts = {})
    @token = token

    if record.admin?
      # 管理员邮件配置
      opts[:subject] = '管理员账户密码重置指引'
      # 强制设置admin子域名和路径
      opts[:url_options] = { subdomain: 'admin', script_name: '/admins' }
      # 指定管理员专属模板
      mail(to: record.email, subject: opts[:subject], template_name: 'reset_password_instructions_admin')
    else
      # 普通用户邮件配置
      opts[:subject] = '账户密码重置指引'
      # 主域名,清除子域名
      opts[:url_options] = { subdomain: nil }
      # 指定普通用户专属模板
      mail(to: record.email, subject: opts[:subject], template_name: 'reset_password_instructions_user')
    end
  end
end

2. 配置Devise使用自定义邮件器

config/initializers/devise.rb里修改邮件器配置:

config.mailer = 'CustomDeviseMailer'

3. 创建角色专属邮件模板

app/views/devise/mailer/下创建两个模板文件:

管理员模板(reset_password_instructions_admin.html.erb)

<p>您好,<%= @resource.email %> 管理员!</p>
<p>您提交了密码重置请求,请点击下方链接设置新密码:</p>
<p><%= link_to '重置管理员密码', admin_edit_password_url(@resource, reset_password_token: @token) %></p>
<p>如果您未发起此请求,请忽略本邮件。</p>

普通用户模板(reset_password_instructions_user.html.erb)

<p>您好,<%= @resource.email %>!</p>
<p>您提交了密码重置请求,请点击下方链接设置新密码:</p>
<p><%= link_to '重置用户密码', edit_password_url(@resource, reset_password_token: @token) %></p>
<p>如果您未发起此请求,请忽略本邮件。</p>

三、开发环境测试注意事项

开发环境要测试子域名的话,需要在本地hosts文件里添加:

127.0.0.1 admin.lvh.me

之后用http://admin.lvh.me:3000访问管理员后台,http://lvh.me:3000访问普通用户页面,就能正常测试两种角色的重置流程了。

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

火山引擎 最新活动