Posted at

Open-uriの認証とリダイレクト

More than 5 years have passed since last update.

にゃんぱすー


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