force_ssl.rb 4.1 KB
Newer Older
1 2
require "active_support/core_ext/hash/except"
require "active_support/core_ext/hash/slice"
3

4
module ActionController
5
  # This module provides a method which will redirect the browser to use HTTPS
6
  # protocol. This will ensure that user's sensitive information will be
7
  # transferred safely over the internet. You _should_ always force the browser
8 9 10
  # to use HTTPS when you're transferring sensitive information such as
  # user authentication, account information, or credit card information.
  #
11 12
  # Note that if you are really concerned about your application security,
  # you might consider using +config.force_ssl+ in your config file instead.
13
  # That will ensure all the data transferred via HTTPS protocol and prevent
14 15
  # the user from getting their session hijacked when accessing the site over
  # unsecured HTTP protocol.
16 17 18 19
  module ForceSSL
    extend ActiveSupport::Concern
    include AbstractController::Callbacks

20 21 22 23
    ACTION_OPTIONS = [:only, :except, :if, :unless]
    URL_OPTIONS = [:protocol, :host, :domain, :subdomain, :port, :path]
    REDIRECT_OPTIONS = [:status, :flash, :alert, :notice]

24 25 26 27
    module ClassMethods
      # Force the request to this particular controller or specified actions to be
      # under HTTPS protocol.
      #
28 29 30 31
      # If you need to disable this for any reason (e.g. development) then you can use
      # an +:if+ or +:unless+ condition.
      #
      #     class AccountsController < ApplicationController
A
AvnerCohen 已提交
32
      #       force_ssl if: :ssl_configured?
33 34 35 36 37
      #
      #       def ssl_configured?
      #         !Rails.env.development?
      #       end
      #     end
38
      #
39 40 41 42 43 44 45 46 47 48 49 50
      # ==== URL Options
      # You can pass any of the following options to affect the redirect url
      # * <tt>host</tt>       - Redirect to a different host name
      # * <tt>subdomain</tt>  - Redirect to a different subdomain
      # * <tt>domain</tt>     - Redirect to a different domain
      # * <tt>port</tt>       - Redirect to a non-standard port
      # * <tt>path</tt>       - Redirect to a different path
      #
      # ==== Redirect Options
      # You can pass any of the following options to affect the redirect status and response
      # * <tt>status</tt>     - Redirect with a custom status (default is 301 Moved Permanently)
      # * <tt>flash</tt>      - Set a flash message when redirecting
N
namusyaka 已提交
51
      # * <tt>alert</tt>      - Set an alert message when redirecting
52 53 54 55 56 57
      # * <tt>notice</tt>     - Set a notice message when redirecting
      #
      # ==== Action Options
      # You can pass any of the following options to affect the before_action callback
      # * <tt>only</tt>       - The callback should be run only for this action
      # * <tt>except</tt>     - The callback should be run for all actions except this action
58 59 60 61
      # * <tt>if</tt>         - A symbol naming an instance method or a proc; the
      #   callback will be called only when it returns a true value.
      # * <tt>unless</tt>     - A symbol naming an instance method or a proc; the
      #   callback will be called only when it returns a false value.
62
      def force_ssl(options = {})
63
        action_options = options.slice(*ACTION_OPTIONS)
64
        redirect_options = options.except(*ACTION_OPTIONS)
65
        before_action(action_options) do
66
          force_ssl_redirect(redirect_options)
67 68 69
        end
      end
    end
70 71 72 73

    # Redirect the existing request to use the HTTPS protocol.
    #
    # ==== Parameters
74 75
    # * <tt>host_or_options</tt> - Either a host name or any of the url &
    #   redirect options available to the <tt>force_ssl</tt> method.
76
    def force_ssl_redirect(host_or_options = nil)
77
      unless request.ssl?
78
        options = {
79 80 81 82
          protocol: "https://",
          host: request.host,
          path: request.fullpath,
          status: :moved_permanently
83 84 85 86 87
        }

        if host_or_options.is_a?(Hash)
          options.merge!(host_or_options)
        elsif host_or_options
88
          options[:host] = host_or_options
89
        end
90

91
        secure_url = ActionDispatch::Http::URL.url_for(options.slice(*URL_OPTIONS))
92
        flash.keep if respond_to?(:flash) && request.respond_to?(:flash)
93
        redirect_to secure_url, options.slice(*REDIRECT_OPTIONS)
94 95
      end
    end
96
  end
97
end