Подтвердить что ты не робот

Заголовок авторизации в Ruby on Rails, доступ к которому осуществляется с помощью HTTP_AUTHORIZATION вместо авторизации?

Я надеюсь, что кто-то сможет что-то прояснить для меня. Я использую Rails 2.3.5, и я могу получить доступ к заголовкам запроса в действии контроллера следующим образом:

def index
  if request.headers['...'] == '...'
    ...
  end
end

Или что-то подобное. request.headers - это экземпляр ActionController:: Http:: Headers, который выглядит как хэш. Поэтому я ожидал бы, что заголовки будут введены на имя, которое я отправляю. Если я отправляю запрос, но с заголовком Authorization, например:

curl -H 'Authorization: OAuth realm="MyRealm",...' http://app/path

Следующий код в действии возвращает false:

if request.headers.include?('Authorization') ... 

В то время как следующее эхо-сообщение выдает значение, которое я посылаю в заголовке:

render :text => request.headers['Authorization']

Следующая проверка возвращает true, достаточно интересно:

if request.headers.include?('HTTP_AUTHORIZATION') ... 

И аналогичным образом, следующее отгоняет значение, которое я отправляю в заголовке:

render :text => request.headers['HTTP_AUTHORIZATION']

Кажется, что есть какая-то магия, о которой я не знаю. Я совершенно смущен тем, почему проверка ключа "Авторизация" терпит неудачу, но получение значения request.headers ['Authorization'] преуспевает. Я также смущен относительно того, откуда происходит "HTTP_AUTHORIZATION", поскольку это не имя заголовка, который я отправляю с запросом. Кто-нибудь знает, что именно происходит?

4b9b3361

Ответ 1

Вы правы - метод headers ActionController::Request возвращает экземпляр ActionController::Http::Headers, который наследуется от Hash. Если мы раскроем исходный код, мы увидим следующее:

class Headers < ::Hash
  extend ActiveSupport::Memoizable

  def initialize(*args)
     if args.size == 1 && args[0].is_a?(Hash)
       super()
       update(args[0])
     else
       super
     end
   end

  def [](header_name)
    if include?(header_name)
      super
    else
      super(env_name(header_name))
    end
  end

  private
    # Converts a HTTP header name to an environment variable name.
    def env_name(header_name)
      "HTTP_#{header_name.upcase.gsub(/-/, '_')}"
    end
    memoize :env_name
end

Итак, при доступе к хэшу через [], есть вторая проверка, чтобы увидеть, существует ли значение из env_name (которое только увеличивает ключ и prepends HTTP_).

Вот почему вы не можете получить истинное значение из request.headers.include?('Authorization') - include? не переопределяется в подклассе, чтобы проверить как нормальную, так и верхнюю версию заголовка. Я полагаю, вы могли бы последовать этому примеру и реализовать его так:

module ActionController
  module Http
    class Headers < ::Hash
      def include?(header_name)
        self[header_name].present?
      end
    end
  end
end

Выбросьте это в lib/extensions/action_controller.rb или что-то в этом случае, при необходимости, в environment.rb, и вам должно быть хорошо идти. Я бы рекомендовал только изменить код вашего контроллера, чтобы использовать [] и present?, чтобы выполнить проверку, хотя:)

Причина, по которой заголовки имеют верхний префикс и префикс HTTP_, я полагаю, проистекает из промежуточного программного обеспечения Rack, Rails. Вероятно, это делает так, чтобы он оставался беспристрастным по отношению к делу, дополнительно добавляя HTTP_, чтобы избежать конфликтов с другими материалами среды без заголовка, которые входят.

Итак, да, немного волшебный, но не слишком сложно понять, взглянув на источник, который я всегда рекомендую:) У Rails есть очень хороший источник, который я многому научился с годами.

Ответ 2

В соответствии с RFC общего интерфейса шлюза:

Мета-переменные с именами, начинающимися с "HTTP_", содержат значения прочитайте из полей заголовка запроса клиента, если используемый протокол HTTP. Имя поля заголовка HTTP преобразуется в верхний регистр, имеет все вхождения "-" заменены на "_" и имеют "HTTP_" preended для получения имени мета-переменной.