如何在过滤之前添加身份validation到rails 3应用程序,并设计用户注册并登录?

鉴于我使用的是Rails 3.1,Ruby 1.9.2,其标准设置用于通过名称和密码登录用户(例如类似于https://github.com/RailsApps/rails3-devise-rspec-cucumber ):

创建新用户(注册) 用户登录之前 ,我需要添加一个层来对外部服务validation发布的用户名和密码。

(FWIW,我计划使用https://github.com/chicks/devise_aes_encryptable strategy / gem来加密敏感密码,并在使用本地密码登录时,解密远程服务的密码,进行validation,然后继续登录,即有两个密码,一个加密单向,另一个可逆…不要问为什么,反正)

在lib / util / authenticate.rb中,我有一个认证类,它返回一个针对该服务的布尔值,例如

Util::Authenticate.authenticate(username,password) 

但是,我无法弄清楚如何在身份validation继续之前添加filter以对其进行身份validation(用于注册或登录)。

我尝试过的:

我有一个用户模型,我想放一个

 before_filter :authenticate_against_my_service, :only => [:create, :new] 

在UserController但但没有用

所以,我尝试打开Devise Sessions控制器,它没有工作,也没有对它进行子类化(例如在自述文件中),

 class User::SessionsController  { :sessions => "users/sessions" } 

也没有对Devise Registrations控制器进行子类化(例如http://www.tonyamoyal.com/2010/07/28/rails-authentication-with-devise-and-cancan-customizing-devise-controllers/ )并添加before_filter(与以上)。

 # in app/controllers/users/registrations_controller.rb class Users::RegistrationsController  [:new, :create, :cancel] skip_before_filter :require_no_authentication def check_permissions authorize! :create, resource end end # and in config/routes.rb root :to => "home#index" # replace "devise_for :users" with the below devise_for :users, :controllers => { :registrations => "users/registrations" } # other related code devise_for :users do get 'logout' => 'devise/sessions#destroy' end # resources :users, :only => :show MUST be below devise_for :users resources :users, :only => :show 

我想我必须在控制器中执行此操作,因为一旦params到达模型,我将不会有未加密的密码发送到外部服务。

我查看了一些扩展的想法,例如https://raw.github.com/nbudin/devise_cas_authenticatable/master/lib/devise_cas_authenticatable/strategy.rb,例如

需要’设计/策略/基础’

 module Devise module Strategies class CasAuthenticatable  username) #fail!("The user #{ticket.response.user} is not registered with this site. Please use a different account.") else fail!(:invalid) end else fail!(:invalid) end end protected def read_ticket(params) #snip end end end end Warden::Strategies.add(:cas_authenticatable, Devise::Strategies::CasAuthenticatable) 

并阅读有关设计/看守认证策略,例如https://github.com/hassox/warden/wiki/Strategies,但想知道我是否需要实际创建一个新策略(如果我能弄清楚如何做到这一点)

编辑,可能的解决方案:

我喜欢alno的建议,并会尝试这一点,虽然它看起来更像是monkeypatch而不是如何使用devise / warden

 module Devise::Models::DatabaseAuthenticatable alias_method :original_valid_password?, :valid_password? def valid_password? if Util::Authenticate.authenticate(username,password) original_valid_password? else false end end end 

或者,我已经考虑添加一个warden身份validation策略,但很难找出所有可移动的部分,例如来自设计的自定义身份validation策略

initializers / authentication_strategy.rb:#这可能在initializers / devise.rb中,不是吗?

 Warden::Strategies.add(:custom_external_authentication) do def valid? # code here to check whether to try and authenticate using this strategy; return true # always use the strategy as only user's authenticate end def authenticate! # code here for doing authentication; if successful, call success! # whatever you've authenticated, eg user; if fail, call fail! if Util::Authenticate.authenticate(username, password) success!(true) # I don't think I want to return a user, as I'll let database authenticatable handle the rest of the authentication # I don't think I'm using success! correctly here: https://github.com/hassox/warden/wiki/Strategies # success!(User.find(someid)) else fail!("Username and password not valid for external service. Please ensure they are valid and try again.") end end end 

在initializers / devise.rb中添加以下内容

 Devise.setup do |config| config.warden do |manager| manager.default_strategies.unshift :custom_external_authentication # will this check before or after database authentication? I want before, I think end end 

或者如何向Devise添加策略

 class ExternalServiceStrategy def valid? true # always use this end def authenticate! # external boolean service call end end Warden::Strategies.add(:database_authenticatable, ExternalServiceStrategy) # will this work before the db authentication? 

如果您在设计源中看到,您会找到valid_password? 方法,它接受未加密的密码,因此您可以覆盖它以对某些外部服务进行身份validation。

就像是:

 def valid_password?(password) ExternalService.authenticate(email, password) end 

您应该在模型层中进行更改,而不是在控制器中进行更改。 事实上,我建议您在/lib/whatever创建一个文件来处理与外部服务的对话,然后修改您的User模型以便它检查外部服务。