32

I'm maintaining a Ruby on Rails site and I'm confused as to how to perform redirects to relative URLs using the https protocol.

I can successfully create a redirect to a relative URL using http, for example:

redirect_to "/some_directory/"

But I cannot discern how to create a redirect to a URL using the https protocol. I have only been able to do so by using absolute URLS, for example:

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

I would like to keep my code clean, and using relative URLs seems like a good idea. Does anyone know how to achieve this in Rails?

4

10 回答 10

39

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
于 2009-11-03T00:03:28.280 回答
34

如果您希望通过 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在控制器中使用该方法:

class SecureController < ApplicationController
  force_ssl
end

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

# Only when we're not in development or tests
force_ssl unless: -> { Rails.env.in? ['development', 'test'] }
于 2014-10-15T12:06:17.937 回答
11

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

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

于 2009-11-03T02:38:04.967 回答
3

如果要全局控制控制器中生成的 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,我不确定早期或未来的版本。

于 2012-03-06T11:19:46.000 回答
3

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

我有一种情况,我需要让 Railshttps在 url 助手等中使用 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
于 2017-04-24T19:51:52.810 回答
2

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

于 2015-04-14T10:59:22.740 回答
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只是一个打开/关闭此功能的环境变量。

于 2020-10-25T15:38:40.197 回答
0

添加protocol..._url

redirect_to your_url(protocol: 'https')

或与subdomain

redirect_to your_url(protocol: 'https', subdomain: 'your_subdomain')
于 2021-08-16T17:38:22.523 回答
-2

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

def redirect_to_secure(relative_uri)
  redirect_to "https://" + request.host + relative_uri
end
于 2009-11-02T17:48:44.387 回答
-3

Open the class that has redirect_to and add a method redirect_to_secure_of with an appropriate implementation. Then call:

redirect_to_secure_of "/some_directory/"

Put this method in the lib directory or somewhere useful.

于 2009-11-02T16:30:28.060 回答