如何使用Passenger(在Apache上)使用HTTP基本身份验证部署多个Rack / Sinatra应用程序?

时间:2012-02-12 19:01:21

标签: ruby sinatra passenger rack

这里的问题是在Passenger + Apache上使用HTTP基本身份验证部署在Passenger + Apache上的同一个Sinatra(Rack)应用程序的多个实例,以防止不必要的访问:

我在我的域上部署了4个Sinatra应用实例:

  1. example.com/private/foo
  2. example.com/private/moo
  3. ...
  4. ...
  5. 使用Rack::Auth::Basic中间件通过HTTP基本身份验证来保护对所有这些访问的访问。所有人都config.ru看起来像:

    # ...
    users = {'user' => 'password'}
    use Rack::Auth::Basic, 'realm' do |username, password|
        users.key?(username) && users[username] == password
    end
    
    run MyApp
    

    config.ru到另一个private/foo的唯一变化是“领域”参数。

    现在问题是,一旦我登录其中一个应用,比如private/moo,Chrome就不会提示我输入其他应用的用户名和密码(Rack::Auth::Basic等)。这是违反直觉的,因为所有实例都由其URL明确标识。为每个实例使用不同的凭据确实有效,但Chrome应该至少为每个实例请求一次凭证至少一次吗?我注意到的一件事是,当我第一次登录其中一个实例时,Chrome会说'example.com:80上的服务器需要用户名和密码'。我原以为'资源example.com/private/foo需要用户名和密码'。是不是应该如何工作?

    我检查了{{1}}源代码和维基百科关于HTTP Basic Auth的文章,并没有提供任何帮助我的案例:(。

1 个答案:

答案 0 :(得分:2)

在基本身份验证中,realm参数不会发送回服务器。因此,服务器无法真正检查客户端是否正在为同一领域发送授权标头。这取决于客户。 Rack的HTTP基本身份验证实现是正确的。所以:

  

现在问题是,一旦我登录其中一个应用程序,比如private / foo,Chrome就不会提示我输入其他应用程序的用户名和密码(私人/ moo等)。这是违反直觉的,因为所有实例都通过其URL进行了唯一识别。

正如Andrew所指出的那样,从RFC中可以清楚地看到,URL并没有在那里发挥作用。但如果'/ foo'受到保护,'/ foo / moo'在同一领域受到保护。

  

为每个实例使用不同的凭据确实有效,但Chrome应该至少为每个实例请求一次凭据吗?

在幕后发生的事情(在使用调试工具进行检查时)是,在我将其中一个应用程序记录为private / foo之后,Chrome会将相同的授权标头重新发送到其他应用程序,比如私有/ moo,没有先被挑战。

RFC表示客户端可以首先为域发送相应的授权头,而不会首先受到服务器的质询。

看起来Chrome要么将我的所有应用视为同一领域,要么在不同领域重新发送相同的授权标头。我不认为这是预期的行为,但我可能会遗漏一些东西。 Firefox表现相同。无论如何,这不是问题的本质。

问题的主题是“如何让Chrome为每个实例至少一次请求我的用户名和密码?基本身份验证不按照我预期的方式工作;为什么?”

使用摘要式身份验证(再次使用RFC 2617)。 Rack在Rack::Auth::Digest::MD5下实现MD5算法版本。为每个实例设置不同的opaque,您就可以了:

# ...
realm  = "Description of the protected area."
opaque = "Secret key that uniquely identifies a realm."

users = {'user' => 'password'}
use Rack::Auth::Digest::MD5, realm, opaque do |username|
  users[username]
end

opaque由客户端发回,并且可以在服务器端验证授权请求是否是正确的资源。 realm的工作本质上是描述性的 - 你想要保护哪个区域或资源?我闪光的是什么ID?

RFC:http://tools.ietf.org/html/rfc2617