![我如何强制 mod_rewrite 发送 URI 的用户信息组件?](https://linux22.com/image/663810/%E6%88%91%E5%A6%82%E4%BD%95%E5%BC%BA%E5%88%B6%20mod_rewrite%20%E5%8F%91%E9%80%81%20URI%20%E7%9A%84%E7%94%A8%E6%88%B7%E4%BF%A1%E6%81%AF%E7%BB%84%E4%BB%B6%EF%BC%9F.png)
一位同事建议我将这篇文章从 StackOverflow 转发到 ServerFault:
这让我抓狂不已。我有一个通过 Apache Web Server 提供服务的 Web 应用程序。支持该应用程序的数据库服务器是 Apache CouchDB,它公开了一个 HTTP API 来检索文档和流式传输附件。
我通过提供安全对象来保护 CouchDB 数据库的安全,该对象只允许特定用户访问数据库中的数据,并对 HTTP 端点的匿名请求返回 401。
我希望能够将公共 URL 映射到存储在此数据库中的文档附件。因此,我尝试在 .htaccess 文件中创建一个重写规则,将来自某些 URL 的请求直接代理到 CouchDB,同时对用户凭据进行硬编码,如下所示:
## DOWNLOAD STREAM:
RewriteCond %{HTTP_HOST} ^example.com$
RewriteRule download/(.*) http://user:[email protected]:5984/database/$1 [P]
在理想情况下,上述示例将采用以下 URL:
http://example.com/download/UUID/attachment.ext
并将其代理至:
http://user:[email protected]:5984/database/UUID/attachment.ext
此方法确实将请求代理到 CouchDB,但省略了 URI 方案的用户信息组件。因此,该请求被视为匿名请求,我收到 401 错误。仅当我从数据库中删除安全性时,附件才会流式传输。
我花了几个小时研究 Apache 配置,并进行了多次尝试,但毫无结果。由于所有相关查询都带有类似的关键字,因此网络搜索毫无结果。
当 mod_rewrite 代理到 CouchDB 时,如何确保它包含重写规则中提供的用户名和密码?
答案1
我找到了答案!Authorization 标头需要单独设置,而不是将 username:password 作为 URI 方案的一部分。以下解决方案完全在文件中运行.htaccess
,这一点很重要,因为 OS X 会定期清除 VirtualHost 站点内的设置:
SetEnvIf Request_URI ^/download/* ADD_COUCH_BASIC_AUTH
RequestHeader set Authorization "Basic XXXXXXXXXXXX" env=ADD_COUCH_BASIC_AUTH
## DOWNLOAD STREAM
RewriteCond %{HTTP_HOST} ^example.com$
RewriteRule download/(.*) http://127.0.0.1:5984/database/$1 [P]
它的工作方式:我们用来SetEnvIf
检查请求路径是否与我们想要代理的路径匹配,如果是,则设置任意环境变量ADD_COUCH_BASIC_AUTH
在下一行中,我们向传出请求添加一个 Basic Auth 标头,除非我们设置的环境变量是存在的。因此,只有在通过 请求资源时才会添加基本身份验证标头/download/
,从而将身份验证凭据发送到 CouchDB。
注意:您必须对您的用户名:密码凭据进行 Base64 编码,然后将其替换XXXXXXXXXXX
为编码值。在 Mac 上,一种简单的方法可以做到这一点:
echo -n 'user:pass' | openssl base64
希望这也能帮助到我以外的人!
答案2
除了 @StickByAtlas 的答案之外,您还可以通过其RewriteRule
本身设置环境变量。下面是一个示例,说明如果文件在本地不存在(.htaccess
在/download
文件夹内),我如何使用它从外部存储加载文件:
RewriteEngine on
# rewrite to external source if local file does not exist
RequestHeader set Authorization "Basic XXXXXXXXXXXX" env=EXTERNAL_SOURCE
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ https://external.example.com/download/$1 [P,E=EXTERNAL_SOURCE]