本文介绍了使用密钥HTTP_AUTHORIZATION而不是Authorization访问Ruby on Rails中的授权标头?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

或类似的东西。 request.headers是的一个实例似乎是一个哈希。因此,我希望标题键在我发送的名称上。但是,如果我发送请求,请使用标题,像这样:

  curl -H'授权:OAuth realm =MyRealm,...'http:// app / path 

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

 如果request.headers.include?('授权')... 

以下回显我在标题中发送的值:

  render:text => ; request.headers ['授权'] 

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

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

同样,以下内容回显了我在标题中发送的值:

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

似乎有一些我不知道的神奇事件。我完全混淆为什么检查密钥'授权'失败,但渲染request.headers ['Authorization']的值成功。我也很困惑'HTTP_AUTHORIZATION'来自何处,因为我正在通过请求发送的标题的名称。有谁知道究竟发生了什么?

解决方案

你是对的 - 的方法 ActionController :: Request 返回 ActionController :: Http :: Headers 的实例,它继承自Hash。如果我们打开源代码,我们会看到:

  class Headers< ::哈希
扩展ActiveSupport :: Memoizable

def initialize(* args)
如果args.size == 1&& args [0] .is_a?(哈希)
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
#将HTTP标头名称转换为环境变量名称。
def env_name(header_name)
HTTP _#{header_name.upcase.gsub(/ - /,'_')}
end
memoize:env_name
end

所以当通过 [] 访问哈希时,还有第二次检查是否存在来自 env_name 的值(它只是提升了密钥并预先设置 HTTP _ )。 / p>

这就是为什么你无法从 request.headers.include获得真值?('授权') - include?未在子类中重写,以检查标头的正常版本和更新版本。我想你可以跟风并自己实现它:

 模块ActionController 
模块Http
class标题< ::哈希
def include?(header_name)
self [header_name] .present?
结束
结束
结束
结束

投掷进入 lib / extensions / action_controller.rb 或其他东西,如果需要,在 environment.rb 中需要它,你应该好好去。我建议只修改你的控制器代码,使用 [] present?进行检查,但:)



原因标题是大写的,并且前缀为 HTTP _ ,我相信,来自Rack,Rails的HTTP中间件。这可能是为了保持对案件的公正,另外在 HTTP _ 之前,以避免与其他非标题环境内容冲突。



所以,是的,有点神奇,但是在浏览源代码后不太难理解,我总是建议这样做:Rails有一些非常好的资源我多年来从中学到了很多东西。


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:

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

Or something similar. request.headers is an instance of ActionController::Http::Headers which 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 Authorization header, like so:

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

The following code in the action returns 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:

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 not the name of the header I'm sending with the request. Anyone know what's going on exactly?

解决方案

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

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.

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:

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.rb or something, require it in environment.rb if 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 :)

The reason that 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.

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.

这篇关于使用密钥HTTP_AUTHORIZATION而不是Authorization访问Ruby on Rails中的授权标头?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-15 13:57