这里的问题是在Passenger + Apache上使用HTTP基本身份验证部署在Passenger + Apache上的同一个Sinatra(Rack)应用程序的多个实例,以防止不必要的访问:
我在我的域上部署了4个Sinatra应用实例:
example.com/private/foo
example.com/private/moo
...
...
使用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的文章,并没有提供任何帮助我的案例:(。
答案 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?