- 认证鉴权
- 后端
【免费下载链接】devise
Flexible authentication solution for Rails with Warden.
导读
本文以当前仓库(GitHub 加速计划 / de / devise,版本 5.0.4)的 README 与核心源码为对象,系统讲解 Devise 的模块化认证架构、安装与生成器用法、控制器/视图/路由/参数清洗的定制方案,以及测试辅助、OmniAuth、多模型登录、Active Job 邮件投递和 Rails API 模式等进阶主题。读完本文,你将能够从零在一个 Rails 7+ 应用中接入 Devise,按需挑选 10 个认证模块,并基于源码理解每个配置项背后的真实行为。
Devise 是一个基于 Warden)。它同时具备以下特性:完全基于 Rack;基于 Rails 引擎的完整 MVC 解决方案;支持同一时刻多个模型同时登录(如 User 与 Admin);采用模块化理念——只使用你真正需要的部分。
一、架构总览:10 个模块各司其职
Devise 由 10 个模型模块组成,它们通过 lib/devise/modules.rb 统一注册。注册表中可以看到每个模块绑定的控制器、路由与策略:例如database_authenticatable绑定sessions控制器与session路由,registerable绑定registrations控制器与registration路由(含cancel),confirmable绑定confirmations,lockable绑定unlocks,recoverable绑定passwords,omniauthable绑定omniauth_callbacks。
各模块职责如下:
| 模块 | 核心职责 |
|---|---|
| Database Authenticatable | 在数据库中对密码进行哈希并存储,用于登录时校验;既支持 POST 请求,也支持 HTTP Basic Authentication |
| Omniauthable | 为模型添加 OmniAuth 支持,可通过 GitHub、Google 等第三方提供商登录 |
| Confirmable | 发送确认指令邮件,并在登录时校验账号是否已确认 |
| Recoverable | 重置用户密码并发送重置指令(找回密码) |
| Registerable | 处理注册流程(sign up),并允许用户编辑、注销自己的账号 |
| Rememberable | 生成与清除"记住我"的 cookie token,实现免登录 |
| Trackable | 记录登录次数、登录时间戳与 IP 地址 |
| Timeoutable | 会话在指定时间内无活动则过期 |
| Validatable | 提供邮箱与密码的校验;该模块是可选且可定制的,你可以定义自己的校验规则 |
| Lockable | 在连续多次登录失败后锁定账号,可通过邮件或指定时间后解锁 |
模块的注册顺序是有讲究的:注册表源码注释明确说明trackable放在最后("Stats for last, so we make sure the user is really signed in"),而database_authenticatable、rememberable这类带策略的模块排在最前,rememberable还被标记为no_input: true(无需用户输入即可参与认证)。
二、快速起步:安装与生成器
2.1 安装与初始化
Devise 5 从 Rails 7 起可用。安装并运行初始化生成器:
bundle add deviserails generate devise:install生成器会在控制台输出一系列指引,其中必须完成的一步是为 Devise 邮件器在每个环境配置默认 URL 选项。例如config/environments/development.rb:
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }初始化生成器还会安装一个 initializer,其中描述了 Devise 的全部配置选项(模板见 lib/generators/templates/devise.rb),官方强调必须通读它。
2.2 为模型启用 Devise
将MODEL替换为应用用户模型的类名(通常是User,也可能是Admin),运行:
rails generate devise MODEL该命令会创建模型(若不存在)并为其配置默认模块,同时改写config/routes.rb指向 Devise 控制器。随后检查模型里是否需要追加额外配置(如confirmable、lockable);若追加,务必检查生成器创建的迁移文件并取消对应注释段。例如给模型加上confirmable,就需在迁移中取消 Confirmable 段的注释。最后运行rails db:migrate。
注意:修改 Devise 配置选项后应重启应用(包括停止 Spring),否则可能出现用户无法登录、路由辅助方法未定义等诡异错误。
在模型内部,devise方法由 lib/devise/models.rb 实现:它按Devise::ALL定义的顺序对所选模块排序,为每个模块extend ClassMethods并include模块本身,最后把选项以send(:"#{key}=", value)的形式落到模型类上。这也是"模块配置可在模型上按实例覆盖全局默认值"的机制来源。
三、控制器过滤器与辅助方法
Devise 会向控制器和视图注入一批辅助方法,生成逻辑见 lib/devise/controllers/helpers.rb 的define_helpers:每个 mapping 都会动态生成authenticate_#{mapping}!、#{mapping}_signed_in?、current_#{mapping}、#{mapping}_session四个方法,并在 Action Controller / Action View 加载时注册为helper_method。
假设模型是User:
before_action :authenticate_user!user_signed_in? # 是否已登录 current_user # 当前登录用户 user_session # 该 scope 的会话数据登录成功、确认账号或更新密码后,Devise 会查找 scope 级 root 路径跳转:例如:user资源会优先使用user_root_path,否则回退到root_path。因此路由中需要设置根路径:
root to: 'home#index'也可以覆写after_sign_in_path_for与after_sign_out_path_for定制跳转钩子。默认逻辑(lib/devise/controllers/helpers.rb)是:先尝试resource_root_path,再回退root_path,最后回退"/"。
若模型叫Member,则对应方法自动变为:
before_action :authenticate_member! member_signed_in? current_member member_session另一个值得一提的扩展是devise_group(见 lib/devise/controllers/helpers.rb),它可以把多个 scope 组成一组,一次性生成authenticate_blogger!、blogger_signed_in?、current_blogger、current_bloggers等方法,适合"用户或管理员任一登录即可访问"的场景。
四、模型级配置:模块选项与哈希算法成本
devise方法接受若干选项来配置模块。例如调整哈希算法的拉伸次数:
devise :database_authenticatable, :registerable, :confirmable, :recoverable, stretches: 13除:stretches外,还可定义:pepper、:encryptor、:confirm_within、:remember_for、:timeout_in、:unlock_in等。全部选项与默认值都写在devise:install生成的 initializer 中(通常位于config/initializers/devise.rb),其模板见 lib/generators/templates/devise.rb。
从 lib/devise.rb 源码可以确认这些全局默认值:
stretches = 12(bcrypt 成本,测试环境模板建议设为 1 加速测试;官方强烈建议其他环境不低于 10,且成本随数字指数增长)pepper = nil(配合哈希加盐,建议用rails secret生成)password_length = 6..128、email_regexp = /\A[^@\s]+@[^@\s]+\z/remember_for = 2.weeks、extend_remember_period = false、expire_all_remember_me_on_sign_out = trueallow_unconfirmed_access_for = 0.days(未确认默认完全不可访问)、confirm_within = nil(确认 token 无期限)、reconfirmable = truetimeout_in = 30.minuteslock_strategy = :failed_attempts、unlock_strategy = :both、maximum_attempts = 20、unlock_in = 1.hour、last_attempt_warning = truereset_password_within = 6.hours、sign_in_after_reset_password = true、sign_in_after_change_password = trueauthentication_keys = [:email]、case_insensitive_keys = [:email]、strip_whitespace_keys = [:email]sign_out_via = :delete、sign_out_all_scopes = truenavigational_formats = ["*/*", :html, :turbo_stream](其中"*/*"用于兼容旧版 Internet Explorer)http_authenticatable = false、params_authenticatable = trueparent_controller = "ApplicationController"、parent_mailer = "ActionMailer::Base"paranoid = false(开启后,确认/找回密码/解锁流程无论邮箱是否存在都返回相同结果,防用户枚举)clean_up_csrf_token_on_authentication = true(登录成功后清理 CSRF token,防止 token 固定攻击)
模型上的取值优先级由 lib/devise/models.rb 的Devise::Models.config机制保证:实例/类上显式赋值 > 父类 >Devise.*全局默认值。
五、强参数(Strong Parameters):三个需要清洗的动作
自 Devise 4 起参数清洗 API 发生变化:Rails 4 将参数清洗从模型移入控制器,因此 Devise 在控制器层处理。只有三个动作会把参数透传给模型,需要清洗,其默认允许的参数为:
| 动作 | 控制器动作 | 默认允许参数 |
|---|---|---|
sign_in | Devise::SessionsController#create | 仅认证键(如email) |
sign_up | Devise::RegistrationsController#create | 认证键 +password+password_confirmation |
account_update | Devise::RegistrationsController#update | 认证键 +password+password_confirmation+current_password |
这些默认值在 lib/devise/parameter_sanitizer.rb 的DEFAULT_PERMITTED_ATTRIBUTES常量中定义,认证键则从resource_class.authentication_keys动态提取。
5.1 简单标量字段
在ApplicationController中加一个 before action("lazy way"):
class ApplicationController < ActionController::Base before_action :configure_permitted_parameters, if: :devise_controller? protected def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up, keys: [:username]) end enddevise_controller?用于判断当前控制器是否为 Devise 控制器(见 lib/devise/controllers/helpers.rb),保证该钩子只作用于 Devise 流程。
5.2 嵌套属性
若使用accepts_nested_attributes_for,需要把嵌套结构和类型一并告知:
devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name, :last_name, address_attributes: [:country, :state, :city, :area, :postal_code]])5.3 块形式:完全自定义
传入块可以完全改变 Devise 的默认行为。允许username与email两个简单标量:
def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_in) do |user_params| user_params.permit(:username, :email) end end注册表单中的角色复选框会以数组形式提交,而数组不属于 Strong Parameters 的标量类型,需要这样配置:
def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up) do |user_params| user_params.permit({ roles: [] }, :email, :password, :password_confirmation) end endpermit方法还支持except:关键字移除默认参数,例如devise_parameter_sanitizer.permit(:account_update, except: [:password])(源码注释示例,见 lib/devise/parameter_sanitizer.rb)。
5.4 多模型:自定义 ParameterSanitizer
不同 Devise 模型可用不同清洗器。推荐继承Devise::ParameterSanitizer:
class User::ParameterSanitizer < Devise::ParameterSanitizer def initialize(*) super permit(:sign_up, keys: [:username, :email]) end end然后在ApplicationController中覆写devise_parameter_sanitizer方法按资源类分派:
class ApplicationController < ActionController::Base protected def devise_parameter_sanitizer if resource_class == User User::ParameterSanitizer.new(User, :user, params) else super # Use the default one end end end自定义清洗器会替换默认Devise::ParameterSanitizer.new(resource_class, resource_name, params)(见 lib/devise/controllers/helpers.rb)的构造调用。
六、视图定制:从复制默认视图到 scoped views
Devise 是 Rails 引擎,所有视图打包在 gem 内部(默认 ERB 视图位于 app/views/devise)。复制视图到应用:
rails generate devise:views当应用存在多个 Devise 模型(如User与Admin)时,默认所有模型共用同一套视图。开启 scope 化视图即可按角色区分:
# config/initializers/devise.rb config.scoped_views = true开启后支持users/sessions/new与admins/sessions/new这类按角色命名的视图;若 scope 内找不到视图,仍回退使用默认的devise/sessions/new。对应回退逻辑由 lib/devise/controllers/scoped_views.rb 实现。按 scope 生成视图:
rails generate devise:views users只生成部分模块的视图(如registerable与confirmable),使用-v标志:
rails generate devise:views -v registrations confirmations视图生成器的实现见 lib/generators/devise/views_generator.rb:它支持-b(form builder,默认在检测到 SimpleForm 时用 simple_form_for,否则 form_for)、-v(选择视图目录)等选项,SimpleForm 模板位于 lib/generators/templates/simple_form_for。
七、控制器定制:继承并扩展
视图级定制不够时,可以按 scope 定制控制器:
1. 用生成器创建自定义控制器(需指定 scope):
rails generate devise:controllers [scope]以users为 scope,控制器会生成到app/controllers/users/,sessions 控制器形如:
class Users::SessionsController < Devise::SessionsController # GET /resource/sign_in # def new # super # end ... end用-c标志只生成指定控制器:rails generate devise:controllers users -c sessions。Devise 内置控制器的源码可在 app/controllers/devise 中查看。
2. 告知路由使用该控制器:
devise_for :users, controllers: { sessions: 'users/sessions' }3.(推荐但非必须)将视图从devise/sessions复制到users/sessions,保持控制器与视图一致(跳过也不会出错,Rails 会因继承继续使用devise/sessions下的视图)。
4. 修改或扩展控制器动作。完全覆写:
class Users::SessionsController < Devise::SessionsController def create # custom sign-in code end end或在原有行为之上叠加新逻辑:
class Users::SessionsController < Devise::SessionsController def create super do |resource| BackgroundWorker.trigger(resource) end end end这对在登录/注册等动作时触发后台任务、记录事件非常有用。
经验提示:Devise 用 flash 消息告知用户登录成功或失败,应用应恰当使用
flash[:notice]与flash[:alert],不要整体打印 flash hash,只打印特定 key。某些场景 Devise 会向 flash 加入:timedout键(仅内部使用、不可展示),若需打印整个 hash 应先移除该键。
八、路由配置:devise_for 的完整选项
Devise 自带默认路由,通过devise_for方法定制。该方法在 lib/devise/rails/routes.rb 中实现,支持:class_name、:path、:path_names等选项,例如同时改路径名(可用于 I18n 场景):
devise_for :users, path: 'auth', path_names: { sign_in: 'login', sign_out: 'logout', password: 'secret', confirmation: 'verification', unlock: 'unblock', registration: 'register', sign_up: 'cmon_let_me_in' }devise_for会在解析路由后触发 Devise::RouteSet#finalize!:完成 Warden 配置(Devise.configure_warden!)并重新生成 URL 辅助方法(Devise.regenerate_helpers!)。
devise_for支持的主要选项(源码注释均附有示例):
class_name:——指定映射的类,如devise_for :users, class_name: 'Account'path:——替换 URL 前缀,如devise_for :users, path: 'accounts'singular:——自定义单数名,影响 helper 方法名(authenticate_#{singular}!、current_#{singular}等)与 scope 名,如devise_for :admins, singular: :managerpath_names:——覆写:sign_in、:sign_out、:sign_up、:password、:confirmation、:unlock等默认路径名controllers:——指定控制器,如devise_for :users, controllers: { sessions: "users/sessions" }failure_app:——失败时调用的 Rack 应用sign_out_via:——签出接受的 HTTP 方法(默认:delete),可传数组如[:get, :post]module:——控制器命名空间(默认"devise"),如module: "users"可整体改名空间skip:——跳过某些控制器路由,skip: :all表示不生成任何路由only:——skip的反面,只生成指定控制器路由skip_helpers:——跳过生成 Devise URL 辅助方法(默认 false)format:——是否包含(.:format)(默认 true,可设 false 禁用)constraints:、defaults:、router_name:——与 Rails 对应 DSL 行为一致
源码还说明了devise_for的嵌套行为:放在namespace中会自动嵌套控制器(如publisher/sessions),且 helpers 会变为current_publisher_account、authenticate_publisher_account!等。
需要更深的定制——例如在/users/sign_in之外再开放/sign_in——可以把自定义路由包在devise_scope块中:
devise_scope :user do get 'sign_in', to: 'devise/sessions#new' enddevise_scope在路由中别名as(源码 lib/devise/rails/routes.rb)。注意devise_scope :user用单数形式,而devise_for :users用复数形式。
关键提醒:若要使用
current_user等辅助方法,路由中仍需存在devise_for,即使不想生成任何路由也应写成devise_for :users, skip: :all。
此外,路由 DSL 还提供authenticate/authenticated/unauthenticated三个约束方法(lib/devise/rails/routes.rb),可基于登录状态约束路由块,例如:
authenticated :admin do root to: 'admin/dashboard#show', as: :admin_root end root to: 'landing#show'完整的路由配置示例可参考测试应用的 test/rails_app/config/routes.rb,其中覆盖了class_name、path、constraints、format: false、skip_helpers、singular、自定义sign_out_via、namespace嵌套、engine 路由(router_name)等真实组合。
九、Hotwire/Turbo 集成
Devise 将 Hotwire/Turbo 发起的请求视为导航请求,并为错误响应与重定向配置了匹配的状态码。新应用默认生成以下响应配置,已有应用可在 initializer 中追加:
Devise.setup do |config| # ... # 使用 Hotwire/Turbo 时,错误响应与部分重定向的 http 状态码必须如下。 # Devise 对存量应用的默认是 `200 OK` 与 `302 Found`,新应用使用与 # Hotwire/Turbo 行为匹配的新默认值。 config.responder.error_status = :unprocessable_content # Rack 3.1 或更高 # config.responder.error_status = :unprocessable_entity # Rack 3.0 或更低 config.responder.redirect_status = :see_other end重要:这些自定义响应要求respondersgem 版本不低于 3.1.0,使用前请务必升级。
若正从 rails-ujs 迁移,还需检查视图中的两处改动:
- 按钮/表单提交前的确认弹窗选项
data-confirm需改为data-turbo-confirm; - 设置链接请求方法的
data-method需改为data-turbo-method(button_to和form无需改动,Turbo 可自行处理)。
若以:delete方式登出且用链接(非 form 包裹的按钮)配合method: :delete登出,同样需要按上述规则更新。(Devise 的共享视图本身不提供登出链接/按钮。)
十、I18n:flash 消息与邮件主题
Devise 通过 I18n 配合:notice、:alert两个 flash key 输出消息。自定义 locale 文件:
en: devise: sessions: signed_in: 'Signed in successfully.'还可在sessions下按路由中配置的资源单数名区分消息:
en: devise: sessions: user: signed_in: 'Welcome user, you are signed in.' admin: signed_in: 'Hello admin!'Devise 邮件器也使用类似模式定制主题:
en: devise: mailer: confirmation_instructions: subject: 'Hello everybody!' user_subject: 'Hello User! Please confirm your email' reset_password_instructions: subject: 'Reset instructions'仓库自带的 locale 文件 config/locales/en.yml 列出了全部可用消息;测试辅助 locale(德语、葡萄牙语等)位于 test/support/locale。
注意:Devise 控制器继承自 ApplicationController。若应用使用多语言,务必在 ApplicationController 中设置
I18n.locale。
十一、测试辅助:控制器测试与集成测试
Devise 为控制器测试和集成测试内置辅助方法,需要在测试类中引入对应模块(源码位于 lib/devise/test/controller_helpers.rb 与 lib/devise/test/integration_helpers.rb)。
11.1 控制器测试
Rails >= 5 的控制器测试超类为ActionDispatch::IntegrationTest,引入Devise::Test::IntegrationHelpers:
class PostsControllerTest < ActionController::TestCase include Devise::Test::IntegrationHelpers # Rails >= 5 endRails < 5 使用Devise::Test::ControllerHelpers:
class PostsControllerTest < ActionController::TestCase include Devise::Test::ControllerHelpers # Rails < 5 endRSpec 用户可在spec/support/devise.rb、spec/spec_helper.rb或spec/rails_helper.rb中配置(务必放在require 'rspec/rails'之后):
RSpec.configure do |config| config.include Devise::Test::ControllerHelpers, type: :controller config.include Devise::Test::ControllerHelpers, type: :view end然后即可使用sign_in/sign_out:
sign_in @user sign_in @user, scope: :admin测试 Devise 内部控制器或继承自 Devise 的控制器时,需在请求前显式指定 mapping(因为控制器测试不经过路由,而 Devise 本应从路由获取该信息):
test 'GET new' do # 模拟路由通过 env 设置 Devise scope 的行为 @request.env['devise.mapping'] = Devise.mappings[:user] # 用 sign_in helper 登录 fixture 中的 User 记录 sign_in users(:alice) get :new # assert something end11.2 集成测试
引入Devise::Test::IntegrationHelpers:
class PostsTests < ActionDispatch::IntegrationTest include Devise::Test::IntegrationHelpers end可用的方法(实现见 lib/devise/test/integration_helpers.rb,底层委托给 Warden 的login_as/logout):
sign_in users(:bob) sign_in users(:bob), scope: :admin sign_out :userRSpec 的:featurespec 可引入IntegrationHelpers:
RSpec.configure do |config| config.include Devise::Test::IntegrationHelpers, type: :feature end与控制器测试不同,集成测试无需设置devise.mappingenv,因为 mapping 可从测试执行的路由推断。
十二、OmniAuth:第三方提供商登录
Devise 开箱即用地支持 OmniAuth。在config/initializers/devise.rb中指定提供商:
config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'config.omniauth的实现见 lib/devise.rb:它为每个 provider 创建Devise::OmniAuth::Config并注册到omniauth_configs。模型需要启用:omniauthable模块(注册表 lib/devise/modules.rb 将其绑定到omniauth_callbacks控制器与omniauth_callback路由)。对应回调控制器源码见 app/controllers/devise/omniauth_callbacks_controller.rb,模型实现见 lib/devise/models/omniauthable.rb,OmniAuth 路径前缀配置见 lib/devise/omniauth/config.rb。
十三、配置多模型:User 与 Admin 并行登录
Devise 支持任意多个 Devise 模型。例如在 User 之外增加一个只需认证与超时的 Admin:
# 创建含所需字段的迁移 create_table :admins do |t| t.string :email t.string :encrypted_password t.timestamps null: false end # Admin 模型内部 devise :database_authenticatable, :timeoutable # 路由内部 devise_for :admins # 受保护控制器内部 before_action :authenticate_admin! # 控制器与视图中 admin_signed_in? current_admin admin_session也可以直接运行 Devise 生成器完成上述配置。
重要设计约束:这些模型拥有完全不同的路由,登录/登出等不能共享同一控制器。若希望不同角色共享相同动作,官方建议采用基于角色的方案——为模型提供角色列,或使用专门的授权 gem。
十四、Active Job 集成:后台投递 Devise 邮件
若用 Active Job 通过队列后端异步投递 Action Mailer 消息,可在模型中覆写send_devise_notification让 Devise 邮件走现有队列:
def send_devise_notification(notification, *args) devise_mailer.send(notification, self, *args).deliver_later endDevise 邮件器的默认实现与邮件模板可分别在 app/mailers/devise/mailer.rb 与 app/views/devise/mailer 中查看(含确认指令、重置密码指令、解锁指令、邮箱变更、密码变更五类邮件)。
十五、密码重置 token 与 Rails 日志安全
启用 Recoverable 后需警惕:被盗的密码重置 token 可能让攻击者进入应用。Devise 会尽力生成随机、安全的 token,且数据库中只存 token 摘要、绝不存明文。但 Rails 默认日志行为可能把明文 token 泄漏进日志文件:
- Action Mailer 在 DEBUG 级别记录所有外发邮件全文——邮件中的密码重置 token 会随之泄漏;
- Active Job 在 INFO 级别记录每个入队任务的所有参数——若用
deliver_later发送重置邮件,token 同样泄漏。
Rails 生产环境日志级别默认是 INFO。若想防止 token 泄漏,考虑将生产日志级别调到 WARN(config/environments/production.rb):
config.log_level = :warntoken 生成相关的实现可参考 lib/devise/token_generator.rb,其中Devise.friendly_token默认生成 20 字符的 URL-safe 随机串(实现见 lib/devise.rb),而Devise.secure_compare使用ActiveSupport::SecurityUtils.secure_compare做常数时间比较、防时序攻击(lib/devise.rb)。
十六、其他 ORM 与 Rails API 模式
16.1 其他 ORM
Devise 支持 ActiveRecord(默认)与 Mongoid。切换 ORM 只需在 initializer 中 require 对应文件:
require 'devise/orm/mongoid' # 或默认的 'devise/orm/active_record'ORM 适配层位于 lib/devise/orm/active_record.rb 与 lib/devise/orm/mongoid.rb。
16.2 Rails API 模式
Rails 5+ 的 API 模式(仅 API 的 Rails 应用)下,Devise 大体可用:不会抛异常。但由于 API 应用不支持基于 cookie 的浏览器认证(这正是 Devise 的默认方式),可启用http_authenticatable策略——它基于 HTTP Basic Auth,在每个请求上认证用户。Devise 的 HTTP Auth 默认关闭,需在 initializer 中为 database 策略开启:
config.http_authenticatable = [:database]这并不限制你实现自定义 warden 策略(应用内或基于 gem 的扩展均可)。API 常见的认证方式是 token 认证。
API 模式下的测试:API 模式改变了中间件栈顺序,可能影响Devise::Test::IntegrationHelpers,通常表现为集成测试 helper(如#sign_in)报undefined method '[]=' for nil:NilClass。解决办法是在test.rb中重排中间件:
Rails.application.config.middleware.insert_before Warden::Manager, ActionDispatch::Cookies Rails.application.config.middleware.insert_before Warden::Manager, ActionDispatch::Session::CookieStore另外注意:由于不支持视图,Confirmable、Recoverable、Lockable 中依赖邮件的流程目前无法在 API 模式下直接支持。
十七、Warden:Devise 的底层认证框架
Devise 基于 Warden——一个由 Daniel Neighman 创建的通用 Rack 认证框架。Devise 通过 lib/devise.rb 的configure_warden!完成与 Warden 的对接:设置failure_app = Devise::Delegator、default_scope、intercept_401 = false,并为每个 mapping 注册策略与 session 序列化。自定义 warden 策略或 failure app 可在 initializer 的config.warden块中配置:
config.warden do |warden_config| warden_config.intercept_401 = false warden_config.default_strategies(scope: :user).unshift :some_external_strategy end十八、开发与测试:跑 Devise 自身测试套件
贡献者通常需要为改动编写测试。进入仓库顶层目录后:
bundle install bin/testDevise 同时支持 ActiveRecord 与 Mongoid,可通过两个环境变量切换测试组合:
DEVISE_ORM——指定 ORM,默认active_record,切到 Mongoid:
DEVISE_ORM=mongoid bin/test ==> Devise.orm = :mongoid运行 Mongoid 测试需要本机有 MongoDB 服务器(2.0 或更新版本)。命令输出会显示当前使用的变量值。
BUNDLE_GEMFILE——指定 bundler 使用的 Gemfile(代替当前目录下的那个)。仓库的 gemfiles 目录为每个受支持的 Rails 版本准备了一份 Gemfile(Gemfile-rails-7-0、Gemfile-rails-7-1、Gemfile-rails-7-2、Gemfile-rails-8-0、Gemfile-rails-main)。例如复现 Ruby 3.4 + Rails 8.0 的失败环境:
chruby 3.4.0 # 或 rbenv shell 3.4.0,或 rvm use 3.4.0 等 BUNDLE_GEMFILE=gemfiles/Gemfile-rails-8-0 bundle install BUNDLE_GEMFILE=gemfiles/Gemfile-rails-8-0 bin/test两者可组合(Mongoid 场景):
BUNDLE_GEMFILE=gemfiles/Gemfile-rails-8-0 bundle install BUNDLE_GEMFILE=gemfiles/Gemfile-rails-8-0 DEVISE_ORM=mongoid bin/testDevise 使用 minitest 作为测试框架(仓库 Gemfile 与 Rakefile 可确认):
bin/test # 运行全部测试 bin/test test/models/trackable_test.rb # 运行指定文件 bin/test test/models/trackable_test.rb:16 # 按行号运行单个测试 bin/test test/models/trackable_test.rb -n '/update.*record/' # 按正则运行各模块测试用例分布在 test/models、test/controllers、test/integration、test/mailers、test/generators 等目录,是学习模块行为的最佳范例。
十九、其他实用信息
- 从零学 Rails?官方明确建议:初次构建 Rails 应用时不要使用 Devise——它要求对 Rails 框架有较好理解。可以先从零手写一个简单认证系统巩固基础,再回来使用 Devise。
- 版本支持策略:Devise 旨在维护所有未达生命周期终点(EOL)的 Ruby / Rails 版本,具体版本兼容性以 Ruby 与 Rails 的维护策略及仓库测试矩阵为准。
- Bug 与安全报告:安全相关 bug 不要走 issue tracker,应邮件联系维护者(heartcombo.oss@gmail.com);一般问题建议使用 StackOverflow 的
devise标签,而非 issue tracker。 - 许可:Devise 采用 MIT License(见 MIT-LICENSE),版权归 Rafael França、Carlos Antonio da Silva(2020-至今)与 Plataformatec(2009-2019)。
- 认证鉴权
- 后端
【免费下载链接】devise
Flexible authentication solution for Rails with Warden.
相关推荐
Devise核心模块详解:构建灵活的认证系统
Devise核心模块详解:构建灵活的认证系统 本文深入解析了Devise认证框架的四个核心模块:Database Authenticatable(密码哈希与验证
认证鉴权后端2025最强双因素认证方案:基于Devise-Two-Factor的Rails应用安全加固指南
2025最强双因素认证方案:基于Devise Two Factor的Rails应用安全加固指南 你还在为用户账号安全发愁吗? 当用户密码被拖库、撞库攻击肆虐,传
后端应用安全Nuxt 模块测试指南:单元测试、E2E 测试与手动验证的完整实践
Nuxt 模块测试指南:单元测试、E2E 测试与手动验证的完整实践 测试是保证 Nuxt 模块在各种应用环境中稳定工作的关键环节。本文基于 Nuxt 官方模块开
前端后端Web框架SSR
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考