覆盖设计控制器——只有具有管理员角色的用户才能登录?
Override Devise controller -- only user with admin role to log in?
如何覆盖 Devise 控制器以仅允许 'admins' 登录?
这是我想出的:
class SessionsController < Devise::SessionsController
def create
if current_user.admin?
# tell the user "you can't do that"
else
super
end
end
end
但用户能够登录(可能是因为 'current_admin' 尚未定义?)。这是原始设计控制器操作:
class Devise::SessionsController < DeviseController
prepend_before_filter :require_no_authentication, only: [:new, :create]
prepend_before_filter :allow_params_authentication!, only: :create
prepend_before_filter :verify_signed_out_user, only: :destroy
prepend_before_filter only: [:create, :destroy] { request.env["devise.skip_timeout"] = true }
...
# POST /resource/sign_in
def create
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_flashing_format?
sign_in(resource_name, resource)
yield resource if block_given?
respond_with resource, location: after_sign_in_path_for(resource)
end
...
end
编辑:我认为我不应该更改会话控制器,我认为我应该向 Warden 添加一个策略。我试过了,它仍然登录非管理员用户:
config/initializers/custom_warden_strategies.rb:
Warden::Strategies.add(:admin_only) do
def authenticate!
resource = password.present? && mapping.to.find_for_database_authentication(authentication_hash)
encrypted = false
if validate(resource) { encrypted = true; resource.valid_password?(password) }
if resource.admin?
remember_me(resource)
resource.after_database_authentication
success!(resource)
end
end
mapping.to.new.password = password if !encrypted && Devise.paranoid
fail(:not_found_in_database) unless resource
end
end
config\initializers\devise.rb
config.warden do |manager|
manager.default_strategies.unshift :admin_only
end
我找到了一个解决方案,但它在我的测试套件中失败了(虽然我手动测试它时有效)。
config/initializers/admin_only_initializer.rb
require 'devise/strategies/authenticatable'
module Devise
module Strategies
# Default strategy for signing in a user, based on their email and password in the database.
class AdminOnly < Authenticatable
def authenticate!
resource = password.present? && mapping.to.find_for_database_authentication(authentication_hash)
encrypted = false
if validate(resource){ encrypted = true; resource.valid_password?(password) }
if resource.admin?
success!(resource)
else
fail!(:not_permitted)
end
end
mapping.to.new.password = password if !encrypted && Devise.paranoid
fail(:not_found_in_database) unless resource
end
end
end
end
Warden::Strategies.add(:admin_only, Devise::Strategies::AdminOnly)
config/initializers/devise.rb
config.warden do |manager|
manager.default_strategies(:scope => :user).unshift :admin_only
end
和 I18n 字符串 (config/locales/devise.en.yml):
en:
devise:
failure:
not_permitted: "You are not permitted to complete this action."
试一试:
class SessionsController < Devise::SessionsController
def create
super do
if !resource.admin?
signed_out = (Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name))
set_flash_message :notice, :signed_out if signed_out && is_flashing_format?
respond_to_on_destroy
end
end
end
end
为什么要阻止非管理员登录而不只是阻止非管理员用户的某些操作?那么,您如何区分管理员和普通用户?
请记住,一个真正的安全原则是拒绝然后允许。因此,为了确保您的应用程序在您不断添加内容(例如 AGILE 开发)时保持安全,我建议采用以下方法
application_controller.rb
class ApplicationController
before_action :authenticate_user!
# Security policy deny then access
before_filter :access_denied
# Actually I have refactorised below code in a separate security.rb module that I include in ApplicationController, but as you wish
def access_denied
if @denied and not @authorized
flash[:alert] = 'Unauthorized access'
flash[:info] = "Authorized entities : #{@authorized_entities.join(', ')}" if @authorized_entities
flash[:warning] = "Restricted to entities : #{@restricted_entities.join(', ')}" if @restricted_entities
render 'static_pages/home', :status => :unauthorized and return
false
end
end
def allow_access_to_administrators
(@authorized_entities ||= []) << "Administrators"
@authorized = true if administrateur_logged_in?
end
def administrateur_signed_in?
user_signed_in? and current_user.administrator? # Or whatever method you use to authenticate admins
end
end
请注意,我同时使用了@authorized 和@denied。
我通常对 class 的用户(如管理员)使用 @authorized,而我设置 @denied 如果对于 class 的用户,我想限制到一个子集。
那我用
your_controller_reserved_for_admins.rb
prepend_before_filter :allow_access_to_administrators
如何覆盖 Devise 控制器以仅允许 'admins' 登录?
这是我想出的:
class SessionsController < Devise::SessionsController
def create
if current_user.admin?
# tell the user "you can't do that"
else
super
end
end
end
但用户能够登录(可能是因为 'current_admin' 尚未定义?)。这是原始设计控制器操作:
class Devise::SessionsController < DeviseController
prepend_before_filter :require_no_authentication, only: [:new, :create]
prepend_before_filter :allow_params_authentication!, only: :create
prepend_before_filter :verify_signed_out_user, only: :destroy
prepend_before_filter only: [:create, :destroy] { request.env["devise.skip_timeout"] = true }
...
# POST /resource/sign_in
def create
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_flashing_format?
sign_in(resource_name, resource)
yield resource if block_given?
respond_with resource, location: after_sign_in_path_for(resource)
end
...
end
编辑:我认为我不应该更改会话控制器,我认为我应该向 Warden 添加一个策略。我试过了,它仍然登录非管理员用户:
config/initializers/custom_warden_strategies.rb:
Warden::Strategies.add(:admin_only) do
def authenticate!
resource = password.present? && mapping.to.find_for_database_authentication(authentication_hash)
encrypted = false
if validate(resource) { encrypted = true; resource.valid_password?(password) }
if resource.admin?
remember_me(resource)
resource.after_database_authentication
success!(resource)
end
end
mapping.to.new.password = password if !encrypted && Devise.paranoid
fail(:not_found_in_database) unless resource
end
end
config\initializers\devise.rb
config.warden do |manager|
manager.default_strategies.unshift :admin_only
end
我找到了一个解决方案,但它在我的测试套件中失败了(虽然我手动测试它时有效)。
config/initializers/admin_only_initializer.rb
require 'devise/strategies/authenticatable'
module Devise
module Strategies
# Default strategy for signing in a user, based on their email and password in the database.
class AdminOnly < Authenticatable
def authenticate!
resource = password.present? && mapping.to.find_for_database_authentication(authentication_hash)
encrypted = false
if validate(resource){ encrypted = true; resource.valid_password?(password) }
if resource.admin?
success!(resource)
else
fail!(:not_permitted)
end
end
mapping.to.new.password = password if !encrypted && Devise.paranoid
fail(:not_found_in_database) unless resource
end
end
end
end
Warden::Strategies.add(:admin_only, Devise::Strategies::AdminOnly)
config/initializers/devise.rb
config.warden do |manager|
manager.default_strategies(:scope => :user).unshift :admin_only
end
和 I18n 字符串 (config/locales/devise.en.yml):
en:
devise:
failure:
not_permitted: "You are not permitted to complete this action."
试一试:
class SessionsController < Devise::SessionsController
def create
super do
if !resource.admin?
signed_out = (Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name))
set_flash_message :notice, :signed_out if signed_out && is_flashing_format?
respond_to_on_destroy
end
end
end
end
为什么要阻止非管理员登录而不只是阻止非管理员用户的某些操作?那么,您如何区分管理员和普通用户?
请记住,一个真正的安全原则是拒绝然后允许。因此,为了确保您的应用程序在您不断添加内容(例如 AGILE 开发)时保持安全,我建议采用以下方法
application_controller.rb
class ApplicationController
before_action :authenticate_user!
# Security policy deny then access
before_filter :access_denied
# Actually I have refactorised below code in a separate security.rb module that I include in ApplicationController, but as you wish
def access_denied
if @denied and not @authorized
flash[:alert] = 'Unauthorized access'
flash[:info] = "Authorized entities : #{@authorized_entities.join(', ')}" if @authorized_entities
flash[:warning] = "Restricted to entities : #{@restricted_entities.join(', ')}" if @restricted_entities
render 'static_pages/home', :status => :unauthorized and return
false
end
end
def allow_access_to_administrators
(@authorized_entities ||= []) << "Administrators"
@authorized = true if administrateur_logged_in?
end
def administrateur_signed_in?
user_signed_in? and current_user.administrator? # Or whatever method you use to authenticate admins
end
end
请注意,我同时使用了@authorized 和@denied。
我通常对 class 的用户(如管理员)使用 @authorized,而我设置 @denied 如果对于 class 的用户,我想限制到一个子集。
那我用
your_controller_reserved_for_admins.rb
prepend_before_filter :allow_access_to_administrators