【问题标题】:Rails redirect with httpsRails 使用 https 重定向
【发布时间】:2010-12-12 08:19:04
【问题描述】:

我正在维护一个 Ruby on Rails 站点,我对如何使用 https 协议执行到相对 URL 的重定向感到困惑。

我可以使用 http 成功创建到相对 URL 的重定向,例如:

redirect_to "/some_directory/"

但我无法辨别如何使用 https 协议创建指向 URL 的重定向。我只能通过使用绝对 URL 来做到这一点,例如:

redirect_to "https://mysite.com/some_directory/"

我想保持我的代码干净,使用相对 URL 似乎是个好主意。有谁知道如何在 Rails 中实现这一点?

【问题讨论】:

  • 我能澄清一下你的问题吗?您想强制人们在您的网站上始终使用 HTTPS 还是仅对某些 URL 使用 HTTPS?默认情况下,如果当前请求是 HTTPS,RAILS 将继续使用 HTTPS。

标签: ruby-on-rails redirect https relative-path


【解决方案1】:

protocol 添加到..._url

redirect_to your_url(protocol: 'https')

subdomain:

redirect_to your_url(protocol: 'https', subdomain: 'your_subdomain')

【讨论】:

    【解决方案2】:

    如果你想通过https强制所有流量,那么在Rails 6中最好的方法是配置production.rb

    config.force_ssl = false
    

    如果您需要更灵活的解决方案,可以使用简单的 before_action 过滤器来处理:

    class ApplicationController < ActionController::Base
    
      include SessionsHelper
      include LandingpageHelper
      include ApplicationHelper
      include UsersHelper
      include OrganisationHelper
    
      before_action :enforce_ssl, :except => [:health] 
    
      def enforce_ssl
        if ENV['ENFORCE_SSL'].to_s.eql?('true') && !request.ssl?
          redirect_to request.url.gsub(/http/i, "https")
        end
      end 
    
    
    end
    

    如果您在带有运行状况检查的 AWS ECS Fargate 上运行您的应用程序,那么您需要一个更灵活的解决方案,因为来自 AWS 目标组的运行状况检查不是通过 https 调用的。当然,您希望运行状况检查正常工作,同时您希望对所有其他控制器方法强制使用 SSL。

    ENFORCE_SSL 只是一个打开/关闭此功能的环境变量。

    【讨论】:

      【解决方案3】:

      这个答案与最初的问题有些相干,但我记录下来以防其他人在这里遇到与我类似的情况。

      我有一种情况,我需要让 Rails 在 url 帮助程序等中使用 https proto,即使所有请求的来源都是未加密的 (http)。

      现在,通常在这种情况下(当 Rails 位于反向代理或负载平衡器等之后,这很正常),x-forwarded-proto 标头由反向代理或其他设置,因此即使代理之间的请求未加密& rails(顺便说一句,在生产中可能不建议使用)rails 认为一切都在https 中。

      我需要跑到 ngrok tls 隧道后面。我想让 ngrok 用我指定的letsencrypt证书终止tls。但是,当它这样做时,ngrok 不提供自定义标头的功能,包括设置 x-forwarded-proto(尽管此功能计划在将来的某个时间点进行)。

      解决方案非常简单:Rails 不依赖于源协议或是否直接设置 x-forwarded-proto,而是依赖于 Rack env var rack.url_scheme。所以我只需要在开发中添加这个 Rack 中间件:

      class ForceUrlScheme
        def initialize(app)
          @app = app
        end
      
        def call(env)
          env['rack.url_scheme'] = 'https'
          @app.call(env)
        end
      end
      

      【讨论】:

        【解决方案4】:

        如果您希望您的整个应用程序通过 https 提供服务,那么从 Rails 4.0 开始,最好的方法是在配置文件中启用 force_ssl,如下所示:

        # config/environments/production.rb
        Rails.application.configure do
          # [..]
        
          # Force all access to the app over SSL, use Strict-Transport-Security,
          # and use secure cookies.
          config.force_ssl = true
        end
        

        默认情况下,此选项已存在于新生成的应用程序中的config/environments/production.rb 中,但已被注释掉。

        正如评论所说,这不仅会重定向到 https,还会设置 Strict-Transport-Security 标头 (HSTS) 并确保在所有 cookie 上设置安全标志。这两种措施都提高了应用程序的安全性,而没有明显的缺点。它使用ActionDispatch:SSL

        HSTS 过期设置默认设置为一年,不包括子域,这对于大多数应用程序来说可能没问题。您可以使用hsts 选项进行配置:

        config.hsts = {
          expires: 1.month.to_i,
          subdomains: false,
        }
        

        如果您正在运行 Rails 3 (>=3.1) 或者不想对整个应用程序使用 https,那么您可以在控制器中使用 force_ssl 方法: p>

        class SecureController < ApplicationController
          force_ssl
        end
        

        就是这样。您可以为每个控制器设置它,或者在您的ApplicationController 中设置它。您可以使用熟悉的 ifunless 选项有条件地强制 https;例如:

        # Only when we're not in development or tests
        force_ssl unless: -> { Rails.env.in? ['development', 'test'] }
        

        【讨论】:

        • 如果您在隧道后面开发,force_ssl 不是一个很好的选择。例如,ngrok tls 隧道允许您指定证书。然后 ngrok 终止与指定证书的 tls 连接并将未加密的内容转发到您的应用程序(因此您的应用程序认为一切都是 http)。这意味着您不必设置 nginx 代理并在您的开发盒上执行 https。
        • @MichaelJohnston 这就是为什么您可以使用unless 在开发人员中不强制使用 SSL(请参阅我的回答中的最后一行)。
        • @MichaelJohnston 这只能在生产环境中打开,不会影响您的本地开发设置
        • AWS 允许您在负载均衡器上终止 SSL。然后将未加密的数据转发到 EC2 实例(部署您的应用程序的位置)。不幸的是,force_ssl 在这里不是一个好的选择,即使上面指定了unless 条件
        【解决方案5】:

        在 Rails 4 中,可以使用 force_ssl_redirect before_action 为单个控制器强制执行 ssl。请注意,使用此方法不会将您的 cookie 标记为安全,也不会使用 HSTS

        【讨论】:

        • 你能详细说明最后一句话吗?对于不熟悉“将 cookie 标记为安全”和 HSTS 的人。
        【解决方案6】:

        您最好使用ssl_requirement,而不关心链接或重定向是否使用https。使用 ssl_requirement,您可以声明哪些操作需要 SSL,哪些可以使用 SSL,哪些不需要使用 SSL。

        如果您在 Rails 应用程序之外的某个地方进行重定向,那么按照 Olly 的建议指定协议即可。

        【讨论】:

        • 这些天来看看force_ssl,在rails 3.1及更高版本中可用。
        • Matt 如果您将您的评论作为单独的答案,我会投赞成票。
        【解决方案7】:

        ActionController::Base#redirect_to 方法接受一个选项哈希,其中一个参数是:protocol,它允许您调用:

        redirect_to :protocol => 'https://', 
                    :controller => 'some_controller', 
                    :action => 'index'
        

        有关选项的详细信息,请参阅 #redirect_to#url_for 的定义。


        或者,特别是如果要将 SSL 用于所有控制器操作,您可以使用before_filter 采取更具声明性的方法。在ApplicationController 中,您可以定义以下方法:

        def redirect_to_https
            redirect_to :protocol => "https://" unless (request.ssl? || request.local?)
        end
        

        然后,您可以在需要 SSL 的操作的控制器中添加过滤器,例如:

        class YourController
            before_filter :redirect_to_https, :only => ["index", "show"]
        end
        

        或者,如果您需要在整个应用中使用 SSL,请在 ApplicationController 中声明过滤器:

        class ApplicationController
            before_filter :redirect_to_https
        end
        

        【讨论】:

        • 您可以使用:protocol =&gt; 'https://',前提是您将哈希传递给redirect_to,如果您要传递上述问题中的相对URL,那将不起作用。我同意before_filter 通常是更好的选择。
        • 注意这一点,重定向会丢弃所有参数。如果你想重定向 JUST FOR 协议,你应该这样调用:redirect_to({:protocol =&gt; "https://"}.merge(request.parameters))
        【解决方案8】:

        如果您想全局控制控制器中生成的 url 协议,您可以覆盖应用程序控制器中的 url_options 方法。您可以根据 rails env 强制生成 url 的协议,如下所示:

         def url_options
            super
            @_url_options.dup.tap do |options|
              options[:protocol] = Rails.env.production? ? "https://" : "http://"
              options.freeze
            end
          end
        

        此示例适用于 rails 3.2.1,我不确定是否适用于早期或未来版本。

        【讨论】:

          【解决方案9】:

          根据定义,相对 URL 使用当前协议和主机。如果要更改正在使用的协议,则需要提供绝对 URL。我会听取Justice 的建议,并为您创建一种方法:

          def redirect_to_secure(relative_uri)
            redirect_to "https://" + request.host + relative_uri
          end
          

          【讨论】:

            【解决方案10】:

            打开具有redirect_to 的类并添加具有适当实现的方法redirect_to_secure_of。然后调用:

            redirect_to_secure_of "/some_directory/"
            

            将此方法放在lib 目录或其他有用的地方。

            【讨论】:

            • 谢谢,但我正在寻找比“适当的实现”更多的细节。
            • 对不起,我以为你问的是如何使 调用 代码干净。
            猜你喜欢
            • 2017-03-26
            • 1970-01-01
            • 1970-01-01
            • 2021-12-02
            • 2017-01-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-10-01
            相关资源
            最近更新 更多