使用密钥 HTTP_AUTHORIZATION 而不是授权访问 Ruby on Rails 中的授权标头?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2311883/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 22:19:14  来源:igfitidea点击:

Authorization header in Ruby on Rails accessed with key HTTP_AUTHORIZATION instead of Authorization?

ruby-on-railshttp

提问by Paul Osman

I'm hoping someone can clear something up for me. I'm using Rails 2.3.5, and I can access request headers in a controller action like this:

我希望有人可以为我澄清一些事情。我正在使用 Rails 2.3.5,我可以在控制器操作中访问请求标头,如下所示:

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

Or something similar. request.headers is an instance of ActionController::Http::Headerswhich appears to be a Hash. I would expect, therefore, that headers are keyed on the name I send. If I send a request however, with an Authorizationheader, like so:

或者类似的东西。request.headers 是ActionController::Http::Headers 的一个实例,它似乎是一个哈希。因此,我希望标题以我发送的名称为键。但是,如果我发送一个带有授权标头的请求,如下所示:

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

The following code in the action returns false:

操作中的以下代码返回 false:

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

Whereas the following echos out the value I send in the header:

而以下内容回显了我在标头中发送的值:

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

The following check returns true, interestingly enough:

有趣的是,以下检查返回 true:

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

And similarly, the following echoes out the value I send in the header:

同样,以下内容呼应了我在标头中发送的值:

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

Seems like there is some magic happening that I'm unaware of. I'm completely confused as to why checking for the key 'Authorization' fails, but rendering the value of request.headers['Authorization'] succeeds. I'm also confused as to where 'HTTP_AUTHORIZATION' is coming from as that is notthe name of the header I'm sending with the request. Anyone know what's going on exactly?

似乎发生了一些我不知道的魔法。我完全不明白为什么检查密钥 'Authorization' 失败,但呈现 request.headers['Authorization'] 的值成功。我也对 'HTTP_AUTHORIZATION' 的来源感到困惑,因为那不是我随请求发送的标头的名称。有谁知道具体是怎么回事?

回答by Brent Dillingham

You are correct - the headersmethod of ActionController::Requestreturns an instance of ActionController::Http::Headers, which is inherits from Hash. If we crack open the source, we see this:

您是对的 -返回 的实例的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

So when accessing the Hash via [], there's a second check to see if value from env_name(which just upcases the key and prepends HTTP_) exists.

因此,当通过 访问 Hash 时[],会进行第二次检查以查看值 from env_name(它只是将键大写并添加 prepends HTTP_)是否存在。

This is why you can't get a true value from request.headers.include?('Authorization')-- include?is not overridden in the subclass to check for both the normal and upcased version of the header. I imagine you could follow suit and implement it yourself like this:

这就是为什么您无法从request.headers.include?('Authorization')-- 中获得真实值的原因include?,在子类中未覆盖以检查标头的正常版本和大写版本。我想你可以效仿并像这样自己实现它:

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

Throw that into lib/extensions/action_controller.rbor something, require it in environment.rbif necessary, and you should be good to go. I'd recommend just modifying your controller code to use []and present?to do the check, though :)

把它扔进lib/extensions/action_controller.rb或其他东西,environment.rb必要时需要它,你应该很高兴。不过,我建议只修改您的控制器代码以使用[]present?进行检查:)

The reasonthat the headers are upcased and prefixed with HTTP_, I believe, stems from Rack, Rails' HTTP middleware. It probably does this to remain impartial about case, additionally prepending HTTP_to avoid conflicts with other non-header environment stuff that comes in.

我相信,标头大写并以 为前缀的原因HTTP_源于 Rack,Rails 的 HTTP 中间件。这样做可能是为了对案例保持公正,另外还HTTP_避免与其他非标头环境内容发生冲突。

So, yes, a bit magical, but not too hard to understand after glancing at the source, which I'd always recommend :) Rails has some very nice source that I've learned a lot from over the years.

所以,是的,有点神奇,但在浏览源代码后并不太难理解,我总是推荐它:) Rails 有一些非常好的源代码,我多年来从中学到了很多东西。

回答by warvariuc

According to The Common Gateway Interface RFC:

根据通用网关接口 RFC

Meta-variables with names beginning with "HTTP_"contain values read from the client request header fields, if the protocol used is HTTP. The HTTP header field name is converted to upper case, has all occurrences of "-"replaced with "_"and has "HTTP_"prepended to give the meta-variable name.

"HTTP_"如果使用的协议是 HTTP,则名称以 开头的元变量包含从客户端请求标头字段读取的值。HTTP 标头字段名称被转换为大写,所有出现的 都"-"被替换为"_"并已"HTTP_"预先给出元变量名称。