Rails 3 SSL路由重定向从https到http

43

这个问题涉及到这个SO问题和答案(rails-3-ssl-deprecation),建议在Rails 3中使用routes.rb和像以下的路由来处理ssl:

resources :sessions, :constraints => { :protocol => "https" }

# Redirect /foos and anything starting with /foos/ to https.
match "foos(/*path)", :to => redirect { |_, request|  "https://" + request.host_with_port + request.fullpath }

我的问题是链接使用相对路径(我认为这是正确的术语),一旦我在https页面上,所有指向网站上其他页面的链接都会使用https。

1) 如何最好地回到不需要https的页面?我必须为所有这些页面设置重定向(希望不是这样),还是有更好的方法。重定向会是这样吗:

match "foos(/*path)", :to => redirect { |_, request|  "http://" + request.host_with_port + request.fullpath }

2) 如果需要重定向回http,如何处理只有一个例外情况的情况,即我希望所有方法都是http,除了一个方法? 比如说foos(/ * path)将用于所有foos方法。 但是假设我想让foos / upload_foos使用ssl,我知道如何要求它。

scope :constraints => { :protocol => "https" } do
  match 'upload_foos' => 'foos#upload_foos', :via => :post, :as => :upload_foos 
end

但是如果我在 foos 路径上加入 HTTP 重定向,那么 HTTPS 的 upload_foos 会发生什么?

2个回答

44

如果你想让所有的链接都能在HTTP和HTTPS之间切换,那么你必须停止使用_path助手,并转而使用_url助手。

之后,使用强制协议和协议约束参数的范围可以自动切换URL。

routes.rb
scope :protocol => 'https://', :constraints => { :protocol => 'https://' } do
  resources :sessions
end

resources :gizmos

现在,在你的视图中:

<%= sessions_url # => https://..../sessions %>
<%= gizmos_url   # => http://..../gizmos %>

编辑

这并不能修复在 HTTPS 环境下返回 HTTP 的 URL。为了解决这个问题,你需要重写 url_for 方法。

在任何辅助函数中:
module ApplicationHelper
  def url_for(options = nil)
    if Hash === options
      options[:protocol] ||= 'http'
    end
    super(options)
  end
end

除非已经显式设置了协议(在路由或调用助手时),否则它将把协议设置为'http'。


找到了解决方案。从你的路由中删除 scope :protocol => 'http://' 并在我的编辑中添加帮助程序。 - Samuel
1
可以的,谢谢Samuel。我最后一个问题是当我转到我的主页(root:to =>“gizmos#index”)时,我被重定向到https登录页面,这正是我想要的。登录后,我会被带到根页面,但它是https而不是http。有没有办法编写根路由使其成为http? - Daniel
谢谢,我认为这就是我需要的,尽管从“_path”到“_url”的大规模更改让我有些紧张,因为需要更改的文件有很多。对于“form”和“redirect”,我是否需要使用“_path”进行相同的更改?我不认为需要,因为它们没有与“url_for”一起使用。此外,我将在某些供应商文件中进行更改,例如Devise视图。 - Bob
我又想到了一个问题,是否有可能重写 '_path' 方法将它们转换为 URL,从而无需进行全面更改就能实现相同的目标。 - Bob
这是目前我遇到的最好的答案。然而,它会破坏开发环境,因为那里没有证书;有没有办法在开发时做到这一点呢?或者安装本地自签名证书是唯一的选择吗? - rcd
显示剩余5条评论

0

这是很久以前的事情了,我相信它可以得到改进,但在一些旧版本的Rails中,我在应用程序控制器中有这段代码。不确定这对Rails 3仍然有效,但可能会有所帮助:

private
  SECURE_ACTIONS = {
    :login => ["login", "login_customer", "remind_password", "add_customer", "add_or_login_customer"], 
    :store => ["checkout", "save_order"],
    :order => ["show"] }

  # Called as a before_filter in controllers that have some https:// actions
  def require_ssl
    unless ENV['RAILS_ENV'] != 'production' or  @request.ssl?
      redirect_to :protocol => 'https://', :action => action_name
      # we don't want to continue with the action, so return false from the filter
      return false
    end
  end

def default_url_options(options)
    defaults = {}    

    if USE_EXPLICIT_HOST_IN_ALL_LINKS
      # This will OVERRIDE only_path => true, not just set the default.
      options[:only_path] = false
      # Now set the default protocol appropriately:
      if actions = SECURE_ACTIONS[ (options[:controller] || controller_name).to_sym ] and 
         actions.include? options[:action]

        defaults[:protocol] = 'https://'
        defaults[:host] = SECURE_SERVER if defined? SECURE_SERVER
      else
        defaults[:protocol] = 'http://'
        defaults[:host] = NON_SECURE_SERVER if defined? NON_SECURE_SERVER
      end
    end
    return defaults
  end

USE_EXPLICIT_HOST_IN_ALL_LINKS 是一些全局配置选项,但您可以忽略它。

在每个需要 https 的控制器中,我会添加 before_filter :require_ssl 并将该控制器名称及其方法添加到 SECURE_ACTIONS 中。这可能可以通过将操作名称传递给 before 过滤器或其他方式来改进。


感谢您的回复。我喜欢您可以在一个地方指定所有安全操作的方式。这使得清楚明了哪些是受保护的。如果其他答案行不通,我一定会在我的Rails 3应用程序中尝试一下。 - Daniel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接