☰
Devise 5 完整上手指南:基于 Warden 的 Rails 灵活认证方案(模块化架构、生成器、路由与测试实践)
2026/9/30 2:17:35 网站建设 项目流程
  • 认证鉴权
  • 后端

【免费下载链接】devise

Flexible authentication solution for Rails with Warden.

项目地址:https://gitcode.com/gh_mirrors/de/devise
点击查看免费下载

导读

本文以当前仓库(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 devise
rails 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 = true
  • allow_unconfirmed_access_for = 0.days(未确认默认完全不可访问)、confirm_within = nil(确认 token 无期限)、reconfirmable = true
  • timeout_in = 30.minutes
  • lock_strategy = :failed_attempts、unlock_strategy = :both、maximum_attempts = 20、unlock_in = 1.hour、last_attempt_warning = true
  • reset_password_within = 6.hours、sign_in_after_reset_password = true、sign_in_after_change_password = true
  • authentication_keys = [:email]、case_insensitive_keys = [:email]、strip_whitespace_keys = [:email]
  • sign_out_via = :delete、sign_out_all_scopes = true
  • navigational_formats = ["*/*", :html, :turbo_stream](其中"*/*"用于兼容旧版 Internet Explorer)
  • http_authenticatable = false、params_authenticatable = true
  • parent_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_inDevise::SessionsController#create仅认证键(如email)
sign_upDevise::RegistrationsController#create认证键 +password+password_confirmation
account_updateDevise::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 end

devise_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 end

permit方法还支持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: :manager
  • path_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' end

devise_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 end

Rails < 5 使用Devise::Test::ControllerHelpers:

class PostsControllerTest < ActionController::TestCase include Devise::Test::ControllerHelpers # Rails < 5 end

RSpec 用户可在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 end

11.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 :user

RSpec 的: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 end

Devise 邮件器的默认实现与邮件模板可分别在 app/mailers/devise/mailer.rb 与 app/views/devise/mailer 中查看(含确认指令、重置密码指令、解锁指令、邮箱变更、密码变更五类邮件)。

十五、密码重置 token 与 Rails 日志安全

启用 Recoverable 后需警惕:被盗的密码重置 token 可能让攻击者进入应用。Devise 会尽力生成随机、安全的 token,且数据库中只存 token 摘要、绝不存明文。但 Rails 默认日志行为可能把明文 token 泄漏进日志文件:

  1. Action Mailer 在 DEBUG 级别记录所有外发邮件全文——邮件中的密码重置 token 会随之泄漏;
  2. Active Job 在 INFO 级别记录每个入队任务的所有参数——若用deliver_later发送重置邮件,token 同样泄漏。

Rails 生产环境日志级别默认是 INFO。若想防止 token 泄漏,考虑将生产日志级别调到 WARN(config/environments/production.rb):

config.log_level = :warn

token 生成相关的实现可参考 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/test

Devise 同时支持 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/test

Devise 使用 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.

项目地址:https://gitcode.com/gh_mirrors/de/devise
点击查看免费下载

相关推荐

上一篇:百度网盘直链解析终极指南:告别限速,5分钟获取真实下载地址
下一篇:华硕笔记本性能调优终极指南:G-Helper轻量级控制工具完全教程

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询