要将我的网站集成到合作伙伴网站,我需要自定义参数名称:
默认情况下,设计会话#create parameters是:
user[email]
和user[password]
(这些参数转换为嵌套的哈希:用户)
对于我的伴侣,我需要能够回复
email
和password
我经历了很多尝试,最后我实现了自定义Devise::Strategy
和自定义user#find_for_database_authentication
,以便能够捕获这些参数。
设计::策略:: CustomSso.rb
require 'devise/strategies/database_authenticatable'
module Devise
module Strategies
class CustomSso < DatabaseAuthenticatable
def valid?
params[:sso]=="true"
end
def authenticate!
password = params[:password]
resource = password.present? && mapping.to.find_for_database_authentication(params)
hashed = false
if validate(resource){ hashed = true; resource.valid_password?(password) }
remember_me(resource)
resource.after_database_authentication
success!(resource)
end
mapping.to.new.password = password if !hashed && Devise.paranoid
fail(:not_found_in_database) unless resource
end
end
end
end
user.rb
class User < ActiveRecord::Base
....
def self.find_for_database_authentication(params)
User.find_by(email: "#{params[:email]}")
end
....
end
它有效,但有更好或更简单的方法吗?
答案 0 :(得分:2)
<强>原因强>
认证发生在&#34;原始&#34;参数不是因为ApplicationController
before_action
回调延迟运行或请求稍后被修改(恢复),而是因为devise
不是从控制器参数对象获取参数。
最初在Warden中,params将取自新的Rack::Request
,由env(环境)值创建:
# Convenience method to access the rack request.
# :api: public
def request
@request ||= Rack::Request.new(@env)
end # request
但在Devise
中,它已修补到ActionDispatch::Request
的新实例,因此更改routes.rb中的请求(请参阅下面的选项)将有效。<\ n / p>
module Warden::Mixins::Common
def request
@request ||= ActionDispatch::Request.new(env)
end
事实上,使用ActionDispatch请求允许在设计处理之前实际更改请求参数,请参阅下面的解决方案4.
然后从该请求中获取params
(来自Warden Base mixin的代码)以进一步提取用于身份验证的值:
# Convenience method to access the rack request params
# :api: public
def params
request.params
end # params
Devise的可验证策略params_auth_hash
帮助extract仅需要所有参数(范围是〜模型名称,例如用户)
# Extract the appropriate subhash for authentication from params.
def params_auth_hash
params[scope]
end
虽然您可以configure在基于设计的模型中从params_auth_hash
获取的关键名称,但您无法配置为不切换&#34;只有[scope]
部分的参数
# * +authentication_keys+: parameters used for authentication. By default [:email].
<强>解决方案强>
因此有几种选择:
1)重写参数方法(Warden common mixin中的公共,而不是请求或控制器的params方法!)在你的战略中#34; patch&#34;那里的参数,例如
class CustomSso < DatabaseAuthenticatable
def params
request.params[:user][:email] = request.params[:email]
request.params
end
2)在同一个地方重写params_auth_hash 方法(由Devise在authenticatable
策略中隐私):
def params_auth_hash
params[scope][:email] = params[:email]
params[scope]
# just return full params and not [scope] here in your case?
end
3)更改路线中的请求参数
通过修改rails路由自定义约束(config/routes.rb
)中的请求参数来进行肮脏的黑客攻击。在devise_for :<model_name>
post '/users/sign_in', constraints: lambda { |request|
if URL_IS_FOR_SSO? && request.params[:password] && request.params[:email]
request.params[:user] = {password: request.params[:password], email: request.params[:email]}
end
false #doesn't apply this route, proceed to further routing
}
4)(最好工作时)在应用程序控制器中更改请求参数 before_action
您还可以更改请求中传递的参数名称以满足Devise约定,然后使用默认的Devise策略。您可以使用某种重写方法,最简单的方法是将过滤器放在ApplicationController
中。
重要 - 作为混淆的来源,ActionController的params
方法与StrongParamaters concept相关,与request.params完全不同(基本上是只是哈希)。
class ApplicationController < ActionController::Base
...
before_action :rewrite_param_names
private
# !! important - use request.params and not just params!!
def rewrite_param_names
if URL_IS_FOR_SSO? && request.params[:password] && request.params[:email]
request.params[:user] = {password: request.params[:password], email: request.params[:email]}
end
end
end
这是有效的,因为ActionDispatch :: Request存储对env头中的参数哈希的引用,并允许更新此哈希。 source:
# Returns both GET and POST \parameters in a single hash.
def parameters
params = get_header("action_dispatch.request.parameters")
return params if params
...
set_header("action_dispatch.request.parameters", params)
params
end
为了使before_action更加专注,您可以为Devise创建自定义控制器,并在parent_controller setting中为Devise设置
# The parent controller all Devise controllers inherits from.
# Defaults to ApplicationController. This should be set early
# in the initialization process and should be set to a string.
mattr_accessor :parent_controller
@@parent_controller = "ApplicationController"