Devise 是一个基于 Warden 的灵活的 Rails 身份验证解决方案。它:
它由10个模块组成:
Devise Wiki 有大量有关 Devise 的附加信息,包括许多“操作方法”文章以及最常见问题的解答。完成本 README 后请浏览 Wiki:
https://github.com/heartcombo/devise/wiki
如果您发现 Devise 存在问题,我们很想知道。但是,我们要求您在提交错误报告之前先查看这些指南:
https://github.com/heartcombo/devise/wiki/Bug-reports
如果您发现了与安全相关的错误,请不要使用 GitHub 问题跟踪器。发送电子邮件至 [email protected]。
如果您有任何问题、意见或疑虑,请使用 StackOverflow 而不是 GitHub 问题跟踪器:
http://stackoverflow.com/questions/tagged/devise
仍然可以阅读已弃用的邮件列表
https://groups.google.com/group/plataformatec-devise
您可以在此处查看 RDoc 格式的 Devise 文档:
http://rubydoc.info/github/heartcombo/devise/main/frames
如果您需要将 Devise 与以前版本的 Rails 一起使用,您始终可以在安装 gem 后从命令行运行“gem server”来访问旧文档。
GitHub 上提供了一些示例应用程序,它们演示了 Devise 与不同版本的 Rails 的各种功能。您可以在这里查看它们:
https://github.com/heartcombo/devise/wiki/Example-Applications
我们的社区创建了许多扩展,这些扩展添加了超出 Devise 所包含功能的功能。您可以在此处查看可用扩展程序列表并添加您自己的扩展程序:
https://github.com/heartcombo/devise/wiki/Extensions
我们希望您考虑为 Devise 做出贡献。请阅读此简短概述,了解有关如何开始的一些信息:
https://github.com/heartcombo/devise/wiki/Contributing
您通常需要为您的更改编写测试。要运行测试套件,请进入 Devise 的顶级目录并运行bundle install
和bin/test
。 Devise 可与多个 Ruby 和 Rails 版本以及 ActiveRecord 和 Mongoid ORM 配合使用,这意味着您可以使用一些修饰符运行测试套件: DEVISE_ORM
和BUNDLE_GEMFILE
。
由于 Devise 同时支持 Mongoid 和 ActiveRecord,因此我们依靠此变量来为每个 ORM 运行特定代码。 DEVISE_ORM
的默认值为active_record
。要运行 Mongoid 测试,您可以传递mongoid
:
DEVISE_ORM=mongoid bin/test
==> Devise.orm = :mongoid
运行 Mongoid 测试时,您的系统上需要运行 MongoDB 服务器(2.0 版或更高版本)。
请注意,命令输出将显示正在使用的变量值。
我们可以使用此变量告诉捆绑程序应该使用什么 Gemfile(而不是当前目录中的 Gemfile)。在 gemfiles 目录中,我们支持的每个版本的 Rails 都有一个。当您向我们发送拉取请求时,测试套件可能会因使用其中某些请求而中断。如果是这种情况,您可以使用BUNDLE_GEMFILE
变量模拟相同的环境。例如,如果使用 Ruby 3.0.0 和 Rails 6.0 的测试失败,您可以执行以下操作:
rbenv shell 3.0.0 # or rvm use 3.0.0
BUNDLE_GEMFILE=gemfiles/Gemfile-rails-6-0 bundle install
BUNDLE_GEMFILE=gemfiles/Gemfile-rails-6-0 bin/test
如果 Mongoid 的测试失败,您还可以将它们结合起来:
BUNDLE_GEMFILE=gemfiles/Gemfile-rails-6-0 bundle install
BUNDLE_GEMFILE=gemfiles/Gemfile-rails-6-0 DEVISE_ORM=mongoid bin/test
Devise 使用 Mini Test 作为测试框架。
bin/test
bin/test test/models/trackable_test.rb
bin/test test/models/trackable_test.rb:16
如果您正在构建第一个 Rails 应用程序,我们建议您不要使用 Devise。 Devise 需要对 Rails 框架有很好的理解。在这种情况下,我们建议您从头开始一个简单的身份验证系统。以下是一些可以帮助您入门的资源:
一旦您巩固了对 Rails 和身份验证机制的理解,我们向您保证与 Devise 合作将会非常愉快。 ?
Devise 4.0 可与 Rails 6.0 及以上版本一起使用。跑步:
bundle add devise
接下来,您需要运行生成器:
rails generate devise:install
此时,控制台中会出现一些指令。在这些说明中,您需要为每个环境中的 Devise 邮件程序设置默认 URL 选项。这是config/environments/development.rb
的可能配置:
config . action_mailer . default_url_options = { host : 'localhost' , port : 3000 }
生成器将安装一个初始化程序,该初始化程序描述了 Devise 的所有配置选项。你有必要看一下它。完成后,您就可以使用生成器将 Devise 添加到任何模型中。
在以下命令中,您将用应用程序用户使用的类名替换MODEL
(通常是User
但也可以是Admin
)。这将创建一个模型(如果不存在)并使用默认的 Devise 模块对其进行配置。生成器还会配置您的config/routes.rb
文件以指向 Devise 控制器。
rails generate devise MODEL
接下来,检查模型以了解您可能想要添加的任何其他配置选项,例如可确认或可锁定。如果添加选项,请务必检查迁移文件(如果您的 ORM 支持,则由生成器创建)并取消注释相应的部分。例如,如果您在模型中添加可确认选项,则需要在迁移中取消注释可确认部分。
然后运行rails db:migrate
更改 Devise 的配置选项(包括停止 spring)后,您应该重新启动应用程序。否则,您将遇到奇怪的错误,例如,用户无法登录和路由助手未定义。
Devise 将创建一些帮助器以在控制器和视图中使用。要设置具有用户身份验证的控制器,只需添加此 before_action (假设您的设备模型是“用户”):
before_action :authenticate_user!
对于 Rails 5,请注意, protect_from_forgery
不再添加到before_action
链之前,因此,如果您在protect_from_forgery
之前设置了authenticate_user
,则您的请求将导致“无法验证 CSRF 令牌的真实性”。要解决此问题,请更改调用它们的顺序,或使用protect_from_forgery prepend: true
。
如果您的设备型号不是 User,请将“_user”替换为“_yourmodel”。相同的逻辑适用于以下说明。
要验证用户是否已登录,请使用以下帮助程序:
user_signed_in?
对于当前登录的用户,可以使用此帮助程序:
current_user
您可以访问此范围的会话:
user_session
用户登录、确认帐户或更新密码后,Devise 将查找要重定向到的作用域根路径。例如,当使用:user
资源时,如果user_root_path
存在,则将使用它;否则,将使用默认的root_path
。这意味着您需要在路由中设置根:
root to : 'home#index'
您还可以覆盖after_sign_in_path_for
和after_sign_out_path_for
来自定义重定向挂钩。
请注意,例如,如果您的 Devise 模型名为Member
而不是User
,则可用的帮助程序为:
before_action :authenticate_member!
member_signed_in?
current_member
member_session
模型中的 Devise 方法还接受一些选项来配置其模块。例如,您可以通过以下方式选择哈希算法的成本:
devise :database_authenticatable , :registerable , :confirmable , :recoverable , stretches : 13
除了:stretches
之外,您还可以定义:pepper
、 :encryptor
、 :confirm_within
、 :remember_for
、 :timeout_in
、 :unlock_in
等选项。有关更多详细信息,请参阅调用上述“devise:install”生成器时创建的初始化程序文件。该文件通常位于/config/initializers/devise.rb
。
Devise 4 的 Parameter Sanitizer API 已更改
对于以前的 Devise 版本,请参阅 https://github.com/heartcombo/devise/tree/3-stable#strong-parameters
当您自定义自己的视图时,您最终可能会向表单添加新属性。 Rails 4 将参数清理从模型移至控制器,使 Devise 也在控制器上处理此问题。
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
如果您想允许其他参数(惰性方式™),您可以在ApplicationController
中使用简单的 before 操作来实现:
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
以上适用于参数为简单标量类型的任何其他字段。如果您有嵌套属性(假设您正在使用accepts_nested_attributes_for
),那么您需要告诉 devise 这些嵌套和类型:
class ApplicationController < ActionController :: Base
before_action :configure_permitted_parameters , if : :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer . permit ( :sign_up , keys : [ :first_name , :last_name , address_attributes : [ :country , :state , :city , :area , :postal_code ] ] )
end
end
Devise 允许您完全更改 Devise 默认值或通过传递块来调用自定义行为:
要允许用户名和电子邮件使用简单的标量值,请使用此
def configure_permitted_parameters
devise_parameter_sanitizer . permit ( :sign_in ) do | user_params |
user_params . permit ( :username , :email )
end
end
如果您有一些复选框表示用户在注册时可能扮演的角色,则浏览器会将这些选定的复选框作为数组发送。数组不是强参数允许的标量之一,因此我们需要按以下方式配置 Devise:
def configure_permitted_parameters
devise_parameter_sanitizer . permit ( :sign_up ) do | user_params |
user_params . permit ( { roles : [ ] } , :email , :password , :password_confirmation )
end
end
有关允许的标量列表以及如何在嵌套哈希和数组中声明允许的键,请参阅
https://github.com/rails/strong_parameters#nested-parameters
如果您有多个 Devise 模型,您可能需要为每个模型设置不同的参数消毒器。在这种情况下,我们建议继承Devise::ParameterSanitizer
并添加您自己的逻辑:
class User :: ParameterSanitizer < Devise :: ParameterSanitizer
def initialize ( * )
super
permit ( :sign_up , keys : [ :username , :email ] )
end
end
然后配置您的控制器以使用它:
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
上面的示例将用户允许的参数覆盖为:username
和:email
。配置参数的非惰性方法是在自定义控制器中定义上面的 before 过滤器。我们在下面的某些部分详细介绍了如何配置和自定义控制器。
我们构建 Devise 是为了帮助您快速开发使用身份验证的应用程序。但是,当您需要自定义它时,我们不想妨碍您。
由于 Devise 是一个引擎,因此它的所有视图都封装在 gem 内。这些视图将帮助您入门,但一段时间后您可能想要更改它们。如果是这种情况,您只需调用以下生成器,它会将所有视图复制到您的应用程序:
rails generate devise:views
如果您的应用程序中有多个 Devise 模型(例如User
和Admin
),您会注意到 Devise 对所有模型使用相同的视图。幸运的是,Devise 提供了一种自定义视图的简单方法。您需要做的就是在config/initializers/devise.rb
文件中设置config.scoped_views = true
。
执行此操作后,您将能够根据users/sessions/new
和admins/sessions/new
等角色查看视图。如果在范围内找不到视图,Devise 将使用devise/sessions/new
处的默认视图。您还可以使用生成器生成范围视图:
rails generate devise:views users
如果您只想生成几组视图,例如registerable
和confirmable
模块的视图,则可以使用-v
标志将视图列表传递给生成器。
rails generate devise:views -v registrations confirmations
如果视图级别的自定义还不够,您可以按照以下步骤自定义每个控制器:
使用需要范围的生成器创建自定义控制器:
rails generate devise:controllers [scope]
如果您指定users
作为范围,控制器将在app/controllers/users/
中创建。会话控制器将如下所示:
class Users :: SessionsController < Devise :: SessionsController
# GET /resource/sign_in
# def new
# super
# end
...
end
使用-c
标志指定一个或多个控制器,例如: rails generate devise:controllers users -c sessions
告诉路由器使用这个控制器:
devise_for :users , controllers : { sessions : 'users/sessions' }
建议但不要求:将视图从devise/sessions
复制(或移动)到users/sessions
。如果您跳过此步骤,Rails 将由于继承而继续使用devise/sessions
中的视图,但让视图与控制器匹配可以保持一致。
最后,更改或扩展所需的控制器操作。
您可以完全覆盖控制器操作:
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 使用闪现消息来让用户知道登录是否成功。 Devise 希望您的应用程序适当地调用flash[:notice]
和flash[:alert]
。不要打印整个闪存哈希,仅打印特定键。在某些情况下,Devise 会向 flash 哈希添加一个:timedout
键,该键不用于显示。如果您打算打印整个散列,请从散列中删除此键。
Devise 还附带默认路由。如果您需要自定义它们,您应该能够通过 devise_for 方法来完成。它接受多个选项,如 :class_name、:path_prefix 等,包括更改 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
文档以了解详细信息。
如果您需要更深入的自定义,例如除了“/users/sign_in”之外还允许“/sign_in”,您所需要做的就是正常创建路由并将它们包装在路由器中的devise_scope
块中:
devise_scope :user do
get 'sign_in' , to : 'devise/sessions#new'
end
这样,您就可以告诉 Devise 在访问“/sign_in”时使用作用域:user
。请注意, devise_scope
也具有与路由器中as
别名。
请注意:您仍然需要在路由中添加devise_for
才能使用 helper 方法,例如current_user
。
devise_for :users , skip : :all
Devise 与 Hotwire/Turbo 集成,将此类请求视为导航,并配置某些错误响应和重定向以匹配预期行为。默认情况下,使用以下响应配置生成新应用程序,现有应用程序可以通过将配置添加到其 Devise 初始值设定项来选择加入:
Devise . setup do | config |
# ...
# When using Devise with Hotwire/Turbo, the http status for error responses
# and some redirects must match the following. The default in Devise for existing
# apps is `200 OK` and `302 Found` respectively, but new apps are generated with
# these new defaults that match Hotwire/Turbo behavior.
# Note: These might become the new default in future versions of Devise.
config . responder . error_status = :unprocessable_entity
config . responder . redirect_status = :see_other
end
重要提示:这些自定义响应要求responders
gem 版本为3.1.0
或更高版本,如果要使用此配置,请确保更新它。查看此升级指南以获取更多信息。
注意:上述状态配置可能会成为未来版本中 Devise 的默认配置。
如果您要从 Rails-ujs 迁移,则可能需要在应用程序中进行一些其他更改才能使用 Hotwire/Turbo:
data-confirm
选项需要更改为data-turbo-confirm
,以便 Turbo 正确处理这些内容。data-method
选项需要更改为data-turbo-method
。这对于button_to
或form
来说不是必需的,因为 Turbo 可以处理这些。如果您将 Devise 设置为通过:delete
注销,并且您使用链接(而不是表单中包含的按钮)通过method: :delete
选项注销,则需要按照上述方式更新它们。 (Devise 不在其共享视图中提供注销链接/按钮。)
请务必检查您的观点,寻找这些观点,并进行适当的更改。
Devise 将 Flash 消息与 I18n 结合使用,并结合 Flash 键 :notice 和 :alert。要自定义您的应用程序,您可以设置区域设置文件:
en :
devise :
sessions :
signed_in : ' Signed in successfully. '
您还可以使用路由中给出的单数名称根据您配置的资源创建不同的消息:
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 '
查看我们的区域设置文件以检查所有可用的消息。您可能还对我们的 wiki 上提供的众多翻译之一感兴趣:
https://github.com/heartcombo/devise/wiki/I18n
注意:设计控制器继承自ApplicationController。如果您的应用程序使用多个区域设置,则应确保在 ApplicationController 中设置 I18n.locale。
Devise 包括一些用于控制器和集成测试的测试助手。为了使用它们,您需要在测试用例/规范中包含相应的模块。
控制器测试要求您在测试用例或其父ActionController::TestCase
超类中包含Devise::Test::IntegrationHelpers
。对于 5 之前的 Rails 版本,请改为包含Devise::Test::ControllerHelpers
,因为控制器测试的超类已更改为 ActionDispatch::IntegrationTest(有关更多详细信息,请参阅集成测试部分)。
class PostsControllerTest < ActionController :: TestCase
include Devise :: Test :: IntegrationHelpers # Rails >= 5
end
class PostsControllerTest < ActionController :: TestCase
include Devise :: Test :: ControllerHelpers # Rails < 5
end
如果您使用的是 RSpec,则可以将以下内容放入名为spec/support/devise.rb
的文件或spec/spec_helper.rb
中(如果您使用的是rspec-rails
则将其放入spec/rails_helper.rb
中):
RSpec . configure do | config |
config . include Devise :: Test :: ControllerHelpers , type : :controller
config . include Devise :: Test :: ControllerHelpers , type : :view
end
只需确保此包含是在require 'rspec/rails'
指令之后进行的。
现在您可以在控制器测试中使用sign_in
和sign_out
方法:
sign_in @user
sign_in @user , scope : :admin
如果您正在测试 Devise 内部控制器或从 Devise 继承的控制器,则需要在请求之前告诉 Devise 应使用哪个映射。这是必要的,因为 Devise 从路由器获取此信息,但由于控制器测试不通过路由器,因此需要明确说明。例如,如果您正在测试用户范围,只需使用:
test 'GET new' do
# Mimic the router behavior of setting the Devise scope through the env.
@request . env [ 'devise.mapping' ] = Devise . mappings [ :user ]
# Use the sign_in helper to sign in a fixture `User` record.
sign_in users ( :alice )
get :new
# assert something
end
通过包含Devise::Test::IntegrationHelpers
模块可以使用集成测试助手。
class PostsTests < ActionDispatch :: IntegrationTest
include Devise :: Test :: IntegrationHelpers
end
现在您可以在集成测试中使用以下sign_in
和sign_out
方法:
sign_in users ( :bob )
sign_in users ( :bob ) , scope : :admin
sign_out :user
RSpec 用户可以在其:feature
规范中包含IntegrationHelpers
模块。
RSpec . configure do | config |
config . include Devise :: Test :: IntegrationHelpers , type : :feature
end
与控制器测试不同,集成测试不需要提供devise.mapping
env
值,因为可以通过测试中执行的路由推断映射。
您可以在 wiki 中阅读有关使用 RSpec 测试 Rails 控制器的更多信息:
Devise 提供开箱即用的 OmniAuth 支持,以便与其他提供商进行身份验证。要使用它,只需在config/initializers/devise.rb
中指定您的 OmniAuth 配置:
config . omniauth :github , 'APP_ID' , 'APP_SECRET' , scope : 'user,public_repo'
您可以在 wiki 中阅读有关 OmniAuth 支持的更多信息:
Devise 允许您设置任意数量的 Devise 模型。如果您想要一个仅具有身份验证和超时功能的管理模型,除了上面的用户模型之外,只需运行:
# Create a migration with the required fields
create_table :admins do | t |
t . string :email
t . string :encrypted_password
t . timestamps null : false
end
# Inside your Admin model
devise :database_authenticatable , :timeoutable
# Inside your routes
devise_for :admins
# Inside your protected controller
before_action :authenticate_admin!
# Inside your controllers and views
admin_signed_in?
current_admin
admin_session
或者,您可以简单地运行 Devise 生成器。
请记住,这些模型将具有完全不同的路线。他们不会也不可能共享同一个控制器来进行登录、注销等操作。如果您希望不同的角色共享相同的操作,我们建议您使用基于角色的方法,通过提供角色列或使用专用 gem 进行授权。
如果您使用 Active Job 通过队列后端在后台传递 Action Mailer 消息,则可以通过覆盖模型中的send_devise_notification
方法,通过现有队列发送 Devise 电子邮件。
def send_devise_notification ( notification , * args )
devise_mailer . send ( notification , self , * args ) . deliver_later
end
如果启用可恢复模块,请注意,被盗的密码重置令牌可能会让攻击者访问您的应用程序。 Devise 努力生成随机、安全的令牌,并且仅将令牌摘要存储在数据库中,而不是纯文本。然而,Rails 中的默认日志记录行为可能会导致纯文本标记泄漏到日志文件中:
deliver_later
发送密码重置电子邮件,则密码重置令牌将会泄露。默认情况下,Rails 将生产记录器级别设置为 INFO。如果您希望防止令牌泄漏到日志中,请考虑将生产记录器级别更改为 WARN。在config/environments/production.rb
中:
config . log_level = :warn
Devise 支持 ActiveRecord(默认)和 Mongoid。要选择另一个 ORM,只需在初始化程序文件中需要它即可。
Rails 5+ 有一个内置的 API 模式,它优化 Rails 以用作 API(仅)。 Devise在某种程度上能够处理以此模式构建的应用程序,而无需进行额外的修改,因为它不应该引发异常等。但在development
/ testing
过程中仍然可能会出现一些问题,因为我们仍然不知道这种兼容性的全部范围。 (有关更多信息,请参阅问题#4947)
仅 API 应用程序不支持通过 cookie 进行基于浏览器的身份验证,这是设计的默认设置。然而,在这些情况下,devise 仍然可以使用http_authenticatable
策略提供开箱即用的身份验证,该策略使用 HTTP 基本身份验证并对每个请求对用户进行身份验证。 (有关详细信息,请参阅此 wiki 文章“如何:使用 HTTP 基本身份验证”)
HTTP 身份验证的设计默认处于禁用状态,因此需要在数据库策略的设计初始值设定项中启用它:
config . http_authenticatable = [ :database ]
此限制不会限制您在应用程序中或通过基于 gem 的设计扩展来实现自定义 Warden 策略。 API 的常见身份验证策略是基于令牌的身份验证。有关扩展设备以支持此类身份验证和其他身份验证的更多信息,请参阅有关简单令牌身份验证示例和替代方案的 wiki 文章或有关使用 Devise 的自定义身份验证方法的博客文章。
API 模式会更改中间件堆栈的顺序,这可能会导致Devise::Test::IntegrationHelpers
出现问题。当使用集成测试助手(例如#sign_in
)时,此问题通常表现为undefined method `[]=' for nil:NilClass
解决方案只是通过将以下内容添加到 test.rb 来重新排序中间件:
Rails . application