にゃんぱすー
Open-uri による認証と問題点
Open-uri を使用する場合、open
に :http_basic_authentication
オプションでユーザ名とパスワードを与えることで認証付のURLにアクセスすることができる。
fh = open(
uri,
:http_basic_authentication => [user, password],
)
その際、アクセス先がリダイレクト (3xx) のステータスを返し、リダイレクト先URLが認証を要求する場合には、常に "401 Unauthorized" の例外 (OpenURI::HTTPError) が発生する。これは同一の認証情報でアクセス可能な場合でも同様である。
原因
open-uri ではリダイレクトが行われた場合には、無条件で :http_basic_authentication
を削除しているため。(Perl の LWP とは動作が異なる。)
open-uri.rb
if redirect
...
if options.include? :http_basic_authentication
# send authentication only for the URI directly specified.
options = options.dup
options.delete :http_basic_authentication
end
解決策
リダイレクトされる可能性のあるURIに認証情報を渡す場合は、リダイレクトを無効化 (:redirect => false
) した上で、自前でリダイレクトを処理する必要がある。
※ redirectable?
は必要に応じて修正すること。
require "open-uri"
def redirectable?(orig_uri, new_uri)
orig_uri.scheme == new_uri.scheme and
orig_uri.host == new_uri.host and
orig_uri.port == new_uri.port
end
uri = URI("http://www.example.com/")
user = "fate"
password = "nanoha"
max_redirects = 10
num_redirects = 0
begin
fh = open(
uri,
:http_basic_authentication => [user, password],
:redirect => false
)
rescue OpenURI::HTTPRedirect => redirect
num_redirects += 1
if num_redirects <= max_redirects and redirectable?(uri, redirect.uri)
uri = redirect.uri
retry
else
raise redirect
end
end