Help us understand the problem. What is going on with this article?

Railsアプリで、force_sslでないアクションではHTTPSからHTTPへのリダイレクトを強制する

More than 3 years have passed since last update.

前提

  • Rails 4.2.x
  • Ruby 2.3

本題

RailsアプリでSSLを強制させたい場合、つまり HTTP -> HTTPS のリダイレクトをさせたい場合は、
force_sslを使うことができます。

class HogeController < ApplicationController
  force_ssl only: :new
end

ここで、やんごとなき理由で HTTP -> HTTPS のリダイレクトが不要なアクションは必ずHTTPでアクセスさせたい、という要求があったとしましょう。
このときHTTPのページのみを行き来していれば何も問題はないのですが、一度HTTPSのページを経由すると、以降相対パスによる遷移はすべてHTTPSとなってしまいます。
つまり、明示的に HTTPS -> HTTP のリダイレクトを行わないといけません。

force_sslを使っているという前提で、どうすればよいかちょっと考えてみました。
普通に考えるとrequest.ssl? #=> trueのときにHTTPへリダイレクトする、という処理を書けば良いのですが、force_sslの対象となるアクションはその処理を実行したくないです(リダイレクトループになってしまうので)。

すぐに思いついたのはforce_sslが追加するCallbackの中身をCallbackChainから探して判定するという方法ですが、これはCallbackChainに入るときにはすでにlambdaに変換されていてあとからそのメソッド名とオプションを判定しづらい(できない?)のと、思わぬバグを仕込んでしまいそうで怖いので見送りました。

そこで、ひとつの実装として下記のようにしてみました。

class ApplicationController < ActionController::Base
  include SslRedirectable
end

class HogeController < ApplicationController
  force_ssl only: :new
end

module SslRedirectable
  extend ActiveSupport::Concern

  included do
    before_action :force_non_ssl, if: :force_non_ssl?

    class_attribute :ssl_actions

    SSL_ACTIONS_ALL = [:all]

    def self.force_ssl(options = {})
      self.ssl_actions = options[:only] || SSL_ACTIONS_ALL
      super
    end
  end

  def force_non_ssl?
    request.ssl? && request.get? && !ssl_required?
  end

  def ssl_required?
    return false unless ssl_actions
    ssl_actions.include?(action_name.to_sym) || ssl_actions == SSL_ACTIONS_ALL
  end

  def force_non_ssl
    redirect_to non_ssl_url
    flash.keep
  end

  def non_ssl_url
    URI(request.original_url).tap { |u| u.scheme = 'http' }.to_s
  end
end

force_sslを呼び出したときの対象のアクションをclass_attributeとして保持しておき、あとで当該アクションが呼ばれた時に参照する、というアプローチにしてみました。
force_sslをオーバーライドして拡張していますが、このくらいなら変更に弱くないだろうし、既存コードに手を加えずに実現できるメリットが大きいのでありかな、という感じです。
ここおかしくね?とか、もっとスマートな方法があるとか、ご意見あればぜひお願いします!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした