2009-11-02 39 views
23

我保持了Ruby on Rails的網站,我很困惑,如何執行重定向到使用HTTPS協議相對URL。Rails的重定向以https

我可以成功地創建一個重定向到使用http相對URL,例如:

redirect_to "/some_directory/" 

但我不能辨別如何創建一個重定向到使用HTTPS協議的URL。我只能夠通過使用絕對URL這樣做,例如:

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

我想保持我的代碼清潔,並使用相對URL似乎是一個不錯的主意。有誰知道如何在Rails中實現這一點?

+0

我可以澄清一下你的問題。你想迫使人們總是在你的網站上使用HTTPS,或者只是爲了某些URL?如果當前請求是HTTPS,則默認RAILS將繼續使用HTTPS。 – scottd 2009-11-02 21:59:24

回答

8

你可能最好使用ssl_requirement和不關心,如果一個鏈接或redirec t是或不在使用https。隨着ssl_requirement,聲明其行動需要SSL,哪些是能夠SSL,哪些需要不使用SSL的。

如果您將您的Rails應用程序之外的某個地方重定向,那麼將該協議指定爲Olly建議將會起作用。

+14

現在看看'force_ssl',在rails 3.1及更高版本中可用。 – 2013-08-12 15:22:34

+3

馬特如果你提出你的評論一個單獨的答案,我會投它。 – Spundun 2014-08-12 23:21:42

-3

打開具有redirect_to並添加的方法redirect_to_secure_of與適當的實現的類。然後請撥打:

redirect_to_secure_of "/some_directory/" 

將此方法放在lib目錄或其他有用的地方。

+0

謝謝,但我正在尋找比「合適的實施」更多的細節。 – 2009-11-02 16:56:17

+0

對不起,我以爲你在問如何使*調用*代碼乾淨。 – yfeldblum 2009-11-02 17:17:48

-1

相對URL,根據定義,使用當前的協議和主機。如果您想更改正在使用的協議,則需要提供絕對URL。我會採取公正的意見和創造,這是否給你一個方法:

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

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 
+0

如果你正在傳遞一個像上面問題那樣的相對URL,那麼你可以使用':protocol =>'https://''如果你正在傳遞一個散列到'redirect_to',那就行不通了。我同意'before_filter'是一般更好的方法。 – 2009-11-03 15:13:49

3

如果要全局控制在控制器中生成的URL的協議,可以覆蓋應用程序控制器中的url_options方法。你可能會迫使取決於軌道ENV像這樣生成的URL的協議:

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

這個例子中軌3.2.1工作,我不是爲早期或未來版本完全確定。

23

如果你希望你整個應用程序要通過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。您可以強制HTTPS有條件地使用熟悉的ifunless選項;例如:

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

如果您在隧道後面開發,force_ssl不是一個好選擇。例如,一個ngrok tls隧道允許你指定一個證書。然後,ngrok終止tls與指定證書的連接,並將未加密轉發給你的應用程序(所以你的應用程序認爲一切都是http)。這意味着你不必設置一個nginx代理,並在你的開發箱上進行https操作。 – 2017-04-21 23:30:56

+0

@MichaelJohnston這就是爲什麼你可以使用'除非'不強制在開發SSL(請參閱我的答案中的最後一行)。 – Carpetsmoker 2017-04-22 06:04:41

2

在Rails 4中的一個可以使用force_ssl_redirect before_action實施SSL爲單個控制器。請注意,通過使用這種方法,您的cookie不會被標記爲安全,不使用HSTS

+0

你能否詳細說明最後一句話?對於不熟悉'將Cookie標記爲安全'和HSTS的人。 – 2016-01-13 23:28:44

1

這個答案與原始問題有些接近,但我記錄下來,以防其他人在類似的情況下最終到達我自己。

我有,我需要有一個情況的Rails使用URL傭工等https原,即使所有請求的來源是未加密(http)。

現在,通常在這種情況下(這是正常的時Rails是後面的反向代理或負載平衡器等)時,x-forwarded-proto報頭由反向代理或任何設定,因此即使請求是代理&之間未加密軌(可能不是的方式生產最好)鋼軌認爲一切都在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