【问题标题】:What does ":event => :authentication" do?":event => :authentication" 是做什么的?
【发布时间】:2012-02-10 01:10:10
【问题描述】:

这实际上是一个非常简单的问题,但我似乎找不到答案。在Omniauth Overview on Github里,其实有解释但是我没看懂:

We pass the :event => :authentication to the sign_in_and_redirect method
to force all authentication callbacks to be called.

我已经使用与此类似的操作进行身份验证:

def facebook
  authenticator = UserAuthenticator.new(request.env["omniauth.auth"], current_user)

  if authenticator.user_authenticated?
    sign_in_and_redirect authenticator.user, :event => :authentication
  else
    session["devise.oauth_data"] = request.env["omniauth.auth"]
    redirect_to new_user_registration_url
  end
end

我真正想知道的是:event => :authentication 有什么用处?

【问题讨论】:

    标签: ruby-on-rails-3 devise omniauth


    【解决方案1】:

    我只是想帮助找出答案。我自己跟踪了源代码,这有助于我理解:event => :authentication 参数的工作原理。希望对你也有帮助。

    所以你的问题是为什么

    将 :event => :authentication 传递给 sign_in_and_redirect 方法以强制调用所有身份验证回调。

    然后,我们可以跟踪定义。

    # Sign in a user and tries to redirect first to the stored location and
    # then to the url specified by after_sign_in_path_for. It accepts the same
    # parameters as the sign_in method.
    def sign_in_and_redirect(resource_or_scope, *args)
      options  = args.extract_options!
      scope    = Devise::Mapping.find_scope!(resource_or_scope)
      resource = args.last || resource_or_scope
      sign_in(scope, resource, options)
      redirect_to after_sign_in_path_for(resource)
    end
    

    然后sign_in在设计中定义:

    # All options given to sign_in is passed forward to the set_user method in warden.
    # The only exception is the :bypass option, which bypass warden callbacks and stores
    # the user straight in session. This option is useful in cases the user is already
    # signed in, but we want to refresh the credentials in session.
    #
    # Examples:
    #
    #   sign_in :user, @user                      # sign_in(scope, resource)
    #   sign_in @user                             # sign_in(resource)
    #   sign_in @user, :event => :authentication  # sign_in(resource, options)
    #   sign_in @user, :bypass => true            # sign_in(resource, options)
    #
    def sign_in(resource_or_scope, *args)
      options  = args.extract_options!
      scope    = Devise::Mapping.find_scope!(resource_or_scope)
      resource = args.last || resource_or_scope
    
      expire_session_data_after_sign_in!
    
      if options[:bypass]
        warden.session_serializer.store(resource, scope)
      elsif warden.user(scope) == resource && !options.delete(:force)
        # Do nothing. User already signed in and we are not forcing it.
        true
      else
        warden.set_user(resource, options.merge!(:scope => scope))
      end
    end
    

    好的,所以:event => :authentication 现在传递给warden#set_user,那么你的问题就变成了为什么

    将 :event => :authentication 传递给 sign_in_and_redirect 方法以强制调用所有身份验证回调。

    # Manually set the user into the session and auth proxy
    #
    # Parameters:
    #   user - An object that has been setup to serialize into and out of the session.
    #   opts - An options hash.  Use the :scope option to set the scope of the user, set the :store option to false to skip serializing into the session, set the :run_callbacks to false to skip running the callbacks (the default is true).
    #
    # :api: public
    def set_user(user, opts = {})
      scope = (opts[:scope] ||= @config.default_scope)
    
      # Get the default options from the master configuration for the given scope
      opts = (@config[:scope_defaults][scope] || {}).merge(opts)
      opts[:event] ||= :set_user
      @users[scope] = user
    
      if opts[:store] != false && opts[:event] != :fetch
        options = env[ENV_SESSION_OPTIONS]
        options[:renew] = true if options
        session_serializer.store(user, scope)
      end
    
      run_callbacks = opts.fetch(:run_callbacks, true)
      manager._run_callbacks(:after_set_user, user, self, opts) if run_callbacks
    
      @users[scope]
    end
    

    opts[:event] 可以是[:set_user, :fetch, :authentication]

    # Hook to _run_callbacks asserting for conditions.
    def _run_callbacks(kind, *args) #:nodoc:
      options = args.last # Last callback arg MUST be a Hash
    
      send("_#{kind}").each do |callback, conditions|
        invalid = conditions.find do |key, value|
          value.is_a?(Array) ? !value.include?(options[key]) : (value != options[key])
        end
    
        callback.call(*args) unless invalid
      end
    end
    
    # A callback hook set to run every time after a user is set.
    # This callback is triggered the first time one of those three events happens
    # during a request: :authentication, :fetch (from session) and :set_user (when manually set).
    # You can supply as many hooks as you like, and they will be run in order of decleration.
    #
    # If you want to run the callbacks for a given scope and/or event, you can specify them as options.
    # See parameters and example below.
    #
    # Parameters:
    # <options> Some options which specify when the callback should be executed
    #   scope  - Executes the callback only if it maches the scope(s) given
    #   only   - Executes the callback only if it matches the event(s) given
    #   except - Executes the callback except if it matches the event(s) given
    # <block> A block where you can set arbitrary logic to run every time a user is set
    #   Block Parameters: |user, auth, opts|
    #     user - The user object that is being set
    #     auth - The raw authentication proxy object.
    #     opts - any options passed into the set_user call includeing :scope
    #
    # Example:
    #   Warden::Manager.after_set_user do |user,auth,opts|
    #     scope = opts[:scope]
    #     if auth.session["#{scope}.last_access"].to_i > (Time.now - 5.minutes)
    #       auth.logout(scope)
    #       throw(:warden, :scope => scope, :reason => "Times Up")
    #     end
    #     auth.session["#{scope}.last_access"] = Time.now
    #   end
    #
    #   Warden::Manager.after_set_user :except => :fetch do |user,auth,opts|
    #     user.login_count += 1
    #   end
    #
    # :api: public
    def after_set_user(options = {}, method = :push, &block)
      raise BlockNotGiven unless block_given?
    
      if options.key?(:only)
        options[:event] = options.delete(:only)
      elsif options.key?(:except)
        options[:event] = [:set_user, :authentication, :fetch] - Array(options.delete(:except))
      end
    
      _after_set_user.send(method, [block, options])
    end
    

    所以,

    # after_authentication is just a wrapper to after_set_user, which is only invoked
    # when the user is set through the authentication path. The options and yielded arguments
    # are the same as in after_set_user.
    #
    # :api: public
    def after_authentication(options = {}, method = :push, &block)
      after_set_user(options.merge(:event => :authentication), method, &block)
    end
    

    【讨论】:

      【解决方案2】:

      传递 :event =&gt; :authentication 会导致 Warden(底层设计)触发任何定义的回调:

      Warden::Manager.after_authentication do |user,auth,opts|
        # Code triggered by authorization here, for example:
        user.last_login = Time.now
      end
      

      如果您没有使用任何 after_authentication 回调,并且您确信您的库也没有使用,那么它对您没有任何直接用途。因为它是一个身份验证事件,所以我会把它留在里面,因为你知道它可能有什么用处。

      【讨论】:

        猜你喜欢
        • 2010-10-29
        • 1970-01-01
        • 1970-01-01
        • 2016-01-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-15
        • 2020-04-26
        相关资源
        最近更新 更多