【问题标题】:OpenID for rails app behind ApacheApache 背后的 Rails 应用程序的 OpenID
【发布时间】:2011-05-02 09:10:51
【问题描述】:

我正在尝试通过 Google 帐户集成简单的 OpenID 身份验证。我正在使用omniauth gem,并且在我的本地开发系统(Win7、ruby 1.8.7-p302、rails 2.3.8、omniauth 0.1.5)上一切正常。

当我将它部署到我的主机 (HostGator) 时,问题就显现出来了。应用程序(mongrel)从端口 12002 开始,并通过 HostGator 的 cPanel 配置为从子域之一重写:

RewriteCond %{HTTP_HOST} ^subdomain.mycompany.com$ [OR]
RewriteCond %{HTTP_HOST} ^www.subdomain.mycompany.com$
RewriteRule ^(.*)$ "http\:\/\/127\.0\.0\.1\:12002\/$1" [P,L]

当浏览器进入 /auth/open_id 时,它会被重定向到 Google 进行授权,但它的 return_to 地址是 subdomain.mycompany.com:12002 这当然是不正确的,因为 Apache 不会在 12002 上侦听。我已经设法对 OmniAuth 进行猴子补丁::Strategies::OpenID 和 Rack::OpenID 因此它们在没有端口的情况下组装地址,但现在在用户访问确认后的最后阶段,如果落入:

Routing Error
No route matches "/403.shtml" with {:method=>:get}

我想问题是我的网站地址在其他授权请求参数中被编码(某种哈希)以避免

【问题讨论】:

    标签: ruby-on-rails mod-rewrite openid omniauth


    【解决方案1】:

    端口问题是因为 RACK 请求计算端口的方式。这是补丁

    require 'rack/utils'
    module Rack
      class Request
        def port
          if host_with_port =~ /:(\d+)$/
            $1.to_i
          else
            standard_port
          end
        end
    
        # Returns the standard \port number for this request's protocol.
        def standard_port
          case protocol
            when 'https://' then 443
            else 80
          end
        end
    
        # Returns 'https://' if this is an SSL request and 'http://' otherwise.
        def protocol
          ssl? ? 'https://' : 'http://'
        end
    
        # Is this an SSL request?
        def ssl?
          @env['HTTPS'] == 'on' || @env['HTTP_X_FORWARDED_PROTO'] == 'https'
        end
      end
    end
    

    这是 rails ActionController::Request 计算端口的方式。

    希望对你有帮助

    【讨论】:

      【解决方案2】:

      嗯……

      403 错误是因为托管安全规则非常严格,以至于 Google 的 OpenID 响应(长到足以令人怀疑)被拒绝了。已通过托管支持解决。

      为了解决 return_to 参数中的端口号问题(由freiwillen 解决),我制作了以下(猴子路径)初始化程序:

      module OmniAuth
        module Strategies
          # OmniAuth strategy for connecting via OpenID. This allows for connection
          # to a wide variety of sites, some of which are listed [on the OpenID website](http://openid.net/get-an-openid/).
          class OpenID
            protected
            def callback_url
              uri = URI.parse(request.url)
              uri.path += '/callback'
      
              # by KirylP: to overcome hosting subdomain forwarding to rails port        
              uri.port = '' if request.env.has_key? 'HTTP_X_FORWARDED_SERVER'
      
              uri.to_s
            end
          end
        end
      end
      
      module Rack
        class OpenID
          SERVER_PORT_TO_AVOID = 12002
      
          private
          def realm_url(req)
            url = req.scheme + "://"
            url << req.host
      
            scheme, port = req.scheme, req.port
            if scheme == "https" && port != 443 ||
                scheme == "http" && port != 80
              url << ":#{port}" if port != SERVER_PORT_TO_AVOID # KirylP
            end
      
            url
          end
        end
      end
      
      module OpenID
        class Consumer
          def complete(query, current_url)
            message = Message.from_post_args(query)
      
            current_url.sub!(":#{Rack::OpenID::SERVER_PORT_TO_AVOID}", '') # KirylP
      
            mode = message.get_arg(OPENID_NS, 'mode', 'invalid')
            begin
              meth = method('complete_' + mode)
            rescue NameError
              meth = method(:complete_invalid)
            end
            response = meth.call(message, current_url)
            cleanup_last_requested_endpoint
            if [SUCCESS, CANCEL].member?(response.status)
              cleanup_session
            end
            return response
          end    
        end
      end
      

      希望有人提出更优雅的解决方案。

      【讨论】:

        【解决方案3】:

        在最新版本的 Omniauth (0.2.0.beta4) 中,您可以full_host 指定为配置选项,并将其设置为在每次请求时进行评估,或者仅使用固定值.

        示例(导轨):

        ActionController::Dispatcher.middleware.use OmniAuth::Builder do
            store = Rails.env.development? ? OpenID::Store::Filesystem.new("#{Rails.root}/tmp") : nil
            provider :open_id, store, :name => 'google', :identifier => 'https://www.google.com/accounts/o8/id', :scope => 'email'
        
            if Rails.env.production?
                configure do |config|
                    config.full_host = lambda  do |env|
                        req = Rack::Request.new(env)
                        "#{req.scheme}://#{req.host}"  # req.host strips port number
                    end
                end
            end
        end
        

        【讨论】:

        • 因为 Rack::OpenID 从 request.host 生成 openid.realm 参数,这对于 google 的 openid 实际上是失败的,尽管它适用于其他提供商:facebook、twitter、...
        猜你喜欢
        • 1970-01-01
        • 2010-09-06
        • 2021-09-30
        • 2014-02-07
        • 2017-03-16
        • 2012-12-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多