自定义会话#create参数

时间:2016-06-27 07:13:30

标签: ruby-on-rails devise

要将我的网站集成到合作伙伴网站,我需要自定义参数名称:

默认情况下,设计会话#create parameters是:

user[email]user[password]

(这些参数转换为嵌套的哈希:用户)

对于我的伴侣,我需要能够回复

emailpassword

我经历了很多尝试,最后我实现了自定义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

它有效,但有更好或更简单的方法吗?

1 个答案:

答案 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"