Rails 使用 https 重定向

发布于 2024-08-09 21:56:13 字数 361 浏览 6 评论 0原文

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

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

redirect_to "/some_directory/"

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

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

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

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

似狗非友 2024-08-16 21:56:13

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,请在 中声明过滤器>应用程序控制器

class ApplicationController
    before_filter :redirect_to_https
end

The ActionController::Base#redirect_to method takes an options hash, one of the parameters of which is :protocol which allows you to call:

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

See the definition for #redirect_to and #url_for for more info on the options.


Alternatively, and especially if SSL is to be used for all your controller actions, you could take a more declarative approach using a before_filter. In ApplicationController you could define the following method:

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

You can then add filters in your those controllers which have actions requiring SSL, e.g:

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

Or, if you require SSL across your entire app, declare the filter in ApplicationController:

class ApplicationController
    before_filter :redirect_to_https
end
转角预定愛 2024-08-16 21:56:13

如果您希望整个应用程序通过 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< /a> 控制器中的方法:

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'] }

If you want your entire application to be served over https then since Rails 4.0 the best way to do this is to enable force_ssl in the configuration file like so:

# 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

By default this option is already present in config/environments/production.rb in in newly generated apps, but is commented out.

As the comment says, this will not just redirect to https, but also sets the Strict-Transport-Security header (HSTS) and makes sure that the secure flag is set on all cookies. Both measures increase the security of your application without significant drawbacks. It uses ActionDispatch:SSL.

The HSTS expire settings are set to a year by default and doesn't include subdomains, which is probably fine for most applications. You can configure this with the hsts option:

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

If you're running Rails 3 (>=3.1) or don't want to use https for the entire application, then you can use the force_ssl method in a controller:

class SecureController < ApplicationController
  force_ssl
end

That's all. You can set it per controller, or in your ApplicationController. You can force https conditionally using the familiar if or unless options; for example:

# Only when we're not in development or tests
force_ssl unless: -> { Rails.env.in? ['development', 'test'] }
北风几吹夏 2024-08-16 21:56:13

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

如果您要重定向到 Rails 应用程序之外的某个位置,那么按照 Olly 的建议指定协议就可以了。

You're probably better off using ssl_requirement and not caring if a link or redirect is or isn't using https. With ssl_requirement, you declare which actions require SSL, which ones are capable of SSL and which ones are required not to use SSL.

If you're redirecting somewhere outside of your Rails app, then specifying the protocol as Olly suggests will work.

禾厶谷欠 2024-08-16 21:56:13

如果您想全局控制控制器中生成的 url 协议,您可以覆盖应用程序控制器中的 url_options 方法。您可以根据 Rails 环境强制生成的 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,我不太确定是否适用于早期或未来版本。

If you want to globally controll the protocol of urls generated in controllers, you can override the url_options method in you application controller. You could force the protocol of the generated urls depending on the rails env like so :

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

this example works in rails 3.2.1, i'm not exactly sure for earlier or future versions.

〃安静 2024-08-16 21:56:13

这个答案与原来的问题有点无关,但我记录下来,以防其他人遇到与我类似的情况。

我遇到过这样的情况:我需要让 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 环境变量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

This answer is somewhat tangential to the original question, but I record it in case others end up here in similar circumstances to myself.

I had a situation where I needed to have Rails use https proto in url helpers etc. even though the origin of all requests is unencrypted (http).

Now, ordinarily in this situation (which is normal when Rails is behind a reverse proxy or load balancer etc.), the x-forwarded-proto header is set by the reverse proxy or whatever, so even though requests are unencrypted between the proxy & rails (probably not advisable in production by the way) rails thinks everything is in https.

I needed to run behind an ngrok tls tunnel. I wanted to have ngrok terminate the tls with letsencrypt certificates I specified. However when it does so, ngrok does not offer the ability to customize headers, including setting x-forwarded-proto (although this feature is planned at some point in the future).

The solution turned out to be quite simple: Rails does not depend on either the protocol of the origin or whether x-forwarded-proto is set directly, but on the Rack env var rack.url_scheme. So I just needed to add this Rack middleware in development:

class ForceUrlScheme
  def initialize(app)
    @app = app
  end

  def call(env)
    env['rack.url_scheme'] = 'https'
    @app.call(env)
  end
end
半寸时光 2024-08-16 21:56:13

如果您想通过 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 只是一个打开/关闭此功能的环境变量。

If you want to force ALL traffic via https, then the best way in Rails 6 is to configure production.rb with:

config.force_ssl = false

If you need a more flexible solution, you can handle it with a simple before_action filter:

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

If you run your application on AWS ECS Fargate with health checks, then you need a more flexible solution because the health check from the AWS target group is not invoked via https. Of course, you want the health check to work and at the same time, you want to force SSL for all other controller methods.

The ENFORCE_SSL is just an environment variable that turns this feature on/off.

街角卖回忆 2024-08-16 21:56:13

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

In Rails 4 one can use the force_ssl_redirect before_action to enforce ssl for a single controller. Please note that by using this method your cookies won't be marked as secure and HSTS is not used.

东走西顾 2024-08-16 21:56:13

protocol 添加到 ..._url

redirect_to your_url(protocol: 'https')

或使用 subdomain

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

Add protocol to ..._url:

redirect_to your_url(protocol: 'https')

or with subdomain:

redirect_to your_url(protocol: 'https', subdomain: 'your_subdomain')
生寂 2024-08-16 21:56:13

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

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

Relative URLs, by definition, use the current protocol and host. If you want to change the protocol being used, you need to supply the absolute URL. I would take Justice's advice and create a method that does this for you:

def redirect_to_secure(relative_uri)
  redirect_to "https://" + request.host + relative_uri
end
眼波传意 2024-08-16 21:56:13

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

redirect_to_secure_of "/some_directory/"

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文