RubyでのOAuth認証実装方法:APIとの安全な通信を解説

Rubyを使用したアプリケーション開発において、外部APIと通信する際にセキュリティ面で重要な役割を果たすのがOAuth認証です。OAuthは、ユーザーのパスワードを直接扱わずに安全に認証を行うためのプロトコルであり、APIとアプリケーション間でのデータ保護において欠かせない技術です。本記事では、Rubyを用いてOAuth認証を実装するための基本的な手順と、実際のAPIリクエストでの利用方法をわかりやすく解説していきます。OAuthの仕組みや利点、具体的なコード例を通じて、API認証のセキュリティを強化するための知識を身につけましょう。

目次

OAuth認証とは


OAuth認証は、ユーザーのパスワードをアプリケーションに直接共有することなく、セキュリティを確保しながら第三者アプリケーションがユーザーのリソースにアクセスできるようにするためのプロトコルです。この技術は、特にAPIを介した外部サービスとの通信で利用され、ユーザーの認証情報を保護しながらアクセス権を委譲できるため、安全で柔軟な認証が可能です。

OAuthの基本的な仕組み


OAuthは、リソースオーナー(ユーザー)、クライアント(アプリケーション)、リソースサーバー(API)、および認可サーバーの4つの主体が連携して動作します。OAuthの流れでは、認可サーバーを介してユーザーがクライアントに一時的なアクセス許可を付与し、クライアントはアクセストークンを使ってリソースサーバーにアクセスします。

なぜAPI認証にOAuthが適しているのか


OAuthがAPI認証でよく利用される理由は以下の通りです。

1. セキュリティの向上


OAuthを使うことで、ユーザーのパスワードがクライアントに渡らず、トークンベースで認証が行われるため、セキュリティリスクが軽減されます。

2. アクセス制御の柔軟性


OAuthは、アクセス範囲(スコープ)を制限できるため、特定のリソースや機能だけをアプリケーションに委譲することが可能です。

3. ユーザー体験の向上


OAuthはワンクリックでの認証や、既存のソーシャルメディアアカウントでのログイン機能を実現するため、ユーザー体験が向上します。

RubyでのOAuth設定の準備


OAuth認証をRubyで実装するためには、いくつかの準備が必要です。ここでは、OAuthをサポートするためのライブラリやGemの導入方法と、開発環境の設定手順について説明します。

1. 必要なGemのインストール


OAuth認証をRubyで扱うには、oauthoauth2といった専用のGemを利用するのが一般的です。また、APIによってはomniauthなどの追加Gemが必要になる場合もあります。以下のコマンドで必要なGemをインストールしましょう。

# OAuth認証に必要なGemをインストール
gem install oauth
gem install oauth2
gem install omniauth

Gemfileを使用している場合は、以下を追記してbundle installを実行します。

# Gemfile
gem 'oauth'
gem 'oauth2'
gem 'omniauth'

2. アプリケーションの設定とAPIキーの取得


OAuth認証を利用するには、まず外部APIに対してアプリケーションの登録を行い、クライアントIDとクライアントシークレットを取得する必要があります。これはAPI提供元のデベロッパーポータルなどで行います。

例:Twitter APIの場合

  1. TwitterのDeveloper Portalにアクセスし、アプリケーションを作成します。
  2. 作成したアプリケーションの設定画面で、クライアントIDとクライアントシークレットを取得します。
  3. 必要に応じて、コールバックURL(ユーザーが認証後にリダイレクトされるURL)を設定します。

3. OAuth認証の設定ファイルを作成


クライアントIDやシークレットなどの認証情報は、環境変数や設定ファイルに安全に保存します。以下は、YAMLファイルで設定を管理する方法の一例です。

# config/oauth.yml
twitter:
  client_id: "YOUR_CLIENT_ID"
  client_secret: "YOUR_CLIENT_SECRET"
  callback_url: "YOUR_CALLBACK_URL"

これで、RubyでのOAuth設定の準備が整いました。次は、実際に認証プロセスを実装していきます。

OAuth認証プロセスの流れ


OAuth認証にはいくつかのステップがあり、これらを順番に実行することで、ユーザーの許可を得て安全にAPIにアクセスすることができます。ここでは、OAuth 2.0の典型的な認証プロセスを手順ごとに解説します。

1. 認可リクエスト


まず、クライアント(アプリケーション)はユーザーに対して、認証を求めるURLへアクセスするよう促します。このURLには、クライアントID、リダイレクトURI、スコープ(アクセス範囲)、およびレスポンスタイプなどのパラメータが含まれます。例えば、Twitter APIでは以下のようなURLが生成されます。

https://api.twitter.com/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_CALLBACK_URL&response_type=code&scope=read

ユーザーがこのURLにアクセスすると、OAuth認証ページが表示され、ユーザーはアプリにアクセス許可を与えるかどうかを選択します。

2. 認可コードの受け取り


ユーザーがアクセスを許可すると、認可サーバーはリダイレクトURIを通じてアプリケーションに認可コードを返します。このコードは一時的なもので、すぐにアクセストークンの取得に使用されます。

3. アクセストークンの取得


認可コードを受け取ったクライアントは、このコードを使ってアクセストークンをリクエストします。リクエストは、クライアントID、クライアントシークレット、認可コード、およびリダイレクトURIとともにトークンエンドポイントに送信されます。

# アクセストークンのリクエスト例
response = RestClient.post('https://api.twitter.com/oauth2/token', {
  client_id: 'YOUR_CLIENT_ID',
  client_secret: 'YOUR_CLIENT_SECRET',
  code: 'AUTHORIZATION_CODE',
  redirect_uri: 'YOUR_CALLBACK_URL',
  grant_type: 'authorization_code'
})
access_token = JSON.parse(response.body)["access_token"]

このリクエストが成功すると、アクセストークンがクライアントに返され、これを利用してリソースサーバー(API)にアクセスできるようになります。

4. APIリクエストとアクセストークンの利用


取得したアクセストークンを使って、クライアントはAPIリクエストを送信し、リソースにアクセスします。リクエストには、通常Authorizationヘッダーにアクセストークンを含めます。

# APIリクエストの例
response = RestClient.get('https://api.twitter.com/2/tweets', {
  Authorization: "Bearer #{access_token}"
})

5. トークンの更新と有効期限管理


アクセストークンには有効期限があるため、期限が切れた場合はリフレッシュトークンを用いて新たなアクセストークンを取得します。これにより、ユーザーが再認証することなく、継続してAPIアクセスが可能となります。

この一連のプロセスによって、OAuthを通じた安全なAPI通信が実現します。次のステップでは、Rubyコードを使って具体的なリクエストの実装を行います。

RubyコードでのOAuthリクエスト送信


ここでは、Rubyコードを使用してOAuthリクエストを実装し、認可コードを取得してアクセストークンを取得する方法について具体的に説明します。この手順により、ユーザーがOAuth認証を完了し、APIにアクセスするためのアクセストークンを取得できます。

1. 認可リクエストの送信


まず、ユーザーが認証を行うためのURLを生成し、ユーザーにアクセスしてもらいます。このURLには、クライアントID、リダイレクトURI、スコープ、レスポンスタイプなどを含めます。

# 認可リクエストURLの生成
client_id = "YOUR_CLIENT_ID"
redirect_uri = "YOUR_CALLBACK_URL"
scope = "read"
auth_url = "https://api.example.com/oauth/authorize?client_id=#{client_id}&redirect_uri=#{redirect_uri}&response_type=code&scope=#{scope}"

# ユーザーに認証ページへのリンクを提供する
puts "Please authorize the application by visiting the following URL:"
puts auth_url

このURLにユーザーがアクセスし、認可を許可すると、リダイレクトURIに認可コードが付与されます。この認可コードを次のステップで使用します。

2. 認可コードからアクセストークンを取得


ユーザーから認可コードを受け取ったら、これを使ってアクセストークンをリクエストします。以下のコードは、認可コードを使用してアクセストークンを取得する方法を示しています。

require 'rest-client'
require 'json'

# 取得した認可コードを使用してアクセストークンをリクエスト
auth_code = "RECEIVED_AUTHORIZATION_CODE"
client_secret = "YOUR_CLIENT_SECRET"
token_url = "https://api.example.com/oauth/token"

response = RestClient.post(token_url, {
  client_id: client_id,
  client_secret: client_secret,
  code: auth_code,
  redirect_uri: redirect_uri,
  grant_type: 'authorization_code'
})

# レスポンスからアクセストークンを抽出
access_token = JSON.parse(response.body)["access_token"]
puts "Access token: #{access_token}"

これで、アクセストークンが取得できました。このトークンを使うことで、ユーザーの許可を得た範囲内でAPIにアクセスできます。

3. アクセストークンを使ったAPIリクエスト


アクセストークンを取得したら、次にこれを用いてAPIリクエストを送信します。Authorizationヘッダーにアクセストークンを含めてリクエストすることで、認証が通った状態でAPIを利用できます。

# 取得したアクセストークンを使ってAPIリクエストを送信
api_url = "https://api.example.com/endpoint"
response = RestClient.get(api_url, {
  Authorization: "Bearer #{access_token}"
})

# APIレスポンスを表示
puts "API response: #{response.body}"

この手順で、Rubyコードを使用してOAuth認証を通じたAPIアクセスが可能になります。このコード例をベースに、必要な情報を動的に取得することで、他のOAuth対応APIにも対応できます。

アクセストークンの取得と管理


OAuth認証を用いたAPIとの通信において、アクセストークンはユーザーの認可を得てリソースにアクセスするための重要なキーとなります。ここでは、アクセストークンの取得方法と、安全で効率的な管理方法について説明します。

1. アクセストークンの取得


アクセストークンは、認可コードを取得した後にトークンエンドポイントへのリクエストを通じて発行されます。アクセストークンの有効期間は通常短いため、リフレッシュトークンと共に取得して管理することが推奨されます。以下にアクセストークンとリフレッシュトークンの取得例を示します。

require 'rest-client'
require 'json'

# 認可コードを使用してアクセストークンとリフレッシュトークンを取得
token_url = "https://api.example.com/oauth/token"
response = RestClient.post(token_url, {
  client_id: 'YOUR_CLIENT_ID',
  client_secret: 'YOUR_CLIENT_SECRET',
  code: 'RECEIVED_AUTHORIZATION_CODE',
  redirect_uri: 'YOUR_CALLBACK_URL',
  grant_type: 'authorization_code'
})

# トークン情報を解析
token_data = JSON.parse(response.body)
access_token = token_data["access_token"]
refresh_token = token_data["refresh_token"]
expires_in = token_data["expires_in"]  # 有効期限(秒単位)

puts "Access token: #{access_token}"
puts "Refresh token: #{refresh_token}"
puts "Expires in: #{expires_in} seconds"

2. トークンの保存と管理


取得したアクセストークンとリフレッシュトークンは、セキュアな方法で保存・管理することが重要です。一般的には、以下のような方法でトークンを安全に保持します。

環境変数に保存


環境変数にアクセストークンを保存し、プログラムで参照する方法です。環境変数はセキュアな保存場所として推奨されます。

# 環境変数に保存例
ENV['ACCESS_TOKEN'] = access_token
ENV['REFRESH_TOKEN'] = refresh_token

データベースに保存


複数ユーザーのトークンを管理する場合、データベースに保存して管理するのが効果的です。データベース内では暗号化して保存するのが望ましいです。

YAMLファイルやJSONファイルに保存(開発環境での利用)


開発段階や個人の利用に限り、トークンをYAMLファイルやJSONファイルに保存することもありますが、本番環境ではセキュリティリスクを伴うため避けるべきです。

3. アクセストークンの更新(リフレッシュ)


アクセストークンには有効期限があるため、有効期限が切れた際にはリフレッシュトークンを使用して新しいアクセストークンを取得する必要があります。以下は、リフレッシュトークンを使ってアクセストークンを更新するコード例です。

# リフレッシュトークンを使ってアクセストークンを更新
refresh_url = "https://api.example.com/oauth/token"
response = RestClient.post(refresh_url, {
  client_id: 'YOUR_CLIENT_ID',
  client_secret: 'YOUR_CLIENT_SECRET',
  refresh_token: refresh_token,
  grant_type: 'refresh_token'
})

# 新しいアクセストークンを取得
new_token_data = JSON.parse(response.body)
new_access_token = new_token_data["access_token"]
new_refresh_token = new_token_data["refresh_token"]
puts "New access token: #{new_access_token}"
puts "New refresh token: #{new_refresh_token}"

4. セキュリティにおけるベストプラクティス


アクセストークンとリフレッシュトークンの管理では、以下のセキュリティ対策を講じることが推奨されます。

トークンの暗号化


トークンはデータベースやファイルに保存する際に暗号化し、第三者から不正に取得されないようにします。

アクセス制御の強化


トークンの使用を必要最小限に限定し、トークンが不正利用されないようにアクセス制御を行います。

リフレッシュトークンの定期的な更新


長期間同じリフレッシュトークンを使い続けないように、定期的に更新を行い、セキュリティを強化します。

これらの方法でアクセストークンを管理することで、OAuth認証の安全性を確保しつつ、効率的にAPIへのアクセスを継続することが可能です。

APIリクエストでのトークン利用


アクセストークンを取得した後は、それを使用して実際にAPIリクエストを行い、保護されたリソースにアクセスします。ここでは、APIリクエストにおけるトークンの利用方法について具体的なコード例を示します。

1. Authorizationヘッダーにアクセストークンを追加


APIリクエストを送信する際、Authorizationヘッダーにアクセストークンを追加します。このヘッダーにトークンを含めることで、APIサーバーはユーザーが認証済みであることを認識し、リクエストを受け入れます。

require 'rest-client'
require 'json'

# 取得済みのアクセストークンを用意
access_token = "YOUR_ACCESS_TOKEN"

# APIエンドポイントにリクエストを送信
api_url = "https://api.example.com/protected/resource"
response = RestClient.get(api_url, {
  Authorization: "Bearer #{access_token}"
})

# APIレスポンスを表示
data = JSON.parse(response.body)
puts "API response: #{data}"

この例では、Authorization: Bearer YOUR_ACCESS_TOKENという形式でAuthorizationヘッダーにトークンを設定しています。サーバー側はこのトークンを検証し、正当なリクエストであればデータを返します。

2. リクエストにおけるエラー対応


アクセストークンが無効または期限切れの場合、APIはエラーレスポンスを返します。この際の対応方法として、エラーコードをチェックし、必要に応じてリフレッシュトークンを用いて新しいアクセストークンを取得する処理を追加します。

begin
  # APIリクエストを送信
  response = RestClient.get(api_url, { Authorization: "Bearer #{access_token}" })
  data = JSON.parse(response.body)
  puts "API response: #{data}"

rescue RestClient::Unauthorized => e
  # トークンが無効または期限切れの場合の処理
  puts "Access token expired or invalid. Attempting to refresh the token..."

  # トークンリフレッシュ処理を呼び出す(a6のコード参照)
  new_access_token = refresh_access_token(refresh_token)

  # 再度APIリクエストを送信
  response = RestClient.get(api_url, { Authorization: "Bearer #{new_access_token}" })
  data = JSON.parse(response.body)
  puts "API response after refreshing token: #{data}"
end

3. トークンの自動更新と再試行処理


トークンが期限切れとなる場合に備え、自動的にリフレッシュトークンを用いてアクセストークンを更新し、再試行する仕組みを取り入れることで、シームレスなAPIアクセスを実現します。

def refresh_access_token(refresh_token)
  # リフレッシュトークンを使用して新しいアクセストークンを取得(a6のコード参照)
  response = RestClient.post('https://api.example.com/oauth/token', {
    client_id: 'YOUR_CLIENT_ID',
    client_secret: 'YOUR_CLIENT_SECRET',
    refresh_token: refresh_token,
    grant_type: 'refresh_token'
  })
  new_token_data = JSON.parse(response.body)
  new_token_data["access_token"]
end

4. 複数リソースへのアクセスにおけるアクセストークンの再利用


アクセストークンはAPIのリクエストごとに再利用可能です。一度取得したアクセストークンを使い回すことで、パフォーマンスを向上させつつ、リソースへのアクセスを効率化します。

このように、アクセストークンを利用してAPIリクエストを行うことで、セキュアかつ効率的にリソースにアクセスできるようになります。エラー対応やトークンの自動更新を組み込むことで、より信頼性の高いAPI通信が実現します。

エラーハンドリングと再認証処理


APIリクエストにおいて、アクセストークンの期限切れや無効なトークンが原因でエラーが発生することがあります。ここでは、これらのエラーに対するハンドリング方法と、再認証を通じてスムーズにAPI通信を継続する方法について説明します。

1. APIリクエストエラーの基本的な対応


APIリクエストがエラーを返す場合、そのステータスコードを確認し、適切なエラーハンドリングを行う必要があります。特に、401 Unauthorizedエラーは、トークンの期限切れや無効なトークンを示しており、再認証が必要となるケースです。

require 'rest-client'
require 'json'

def make_api_request(api_url, access_token)
  begin
    # APIリクエストを送信
    response = RestClient.get(api_url, { Authorization: "Bearer #{access_token}" })
    JSON.parse(response.body)

  rescue RestClient::Unauthorized => e
    puts "Access token expired or invalid."
    nil  # 再認証やリトライ処理のため、nilを返す
  rescue RestClient::ExceptionWithResponse => e
    puts "API request failed: #{e.response}"
    nil
  end
end

このコードでは、Unauthorizedエラーが発生した場合にメッセージを表示し、リトライを試みるためにnilを返しています。

2. トークンのリフレッシュによる再認証処理


期限切れのトークンを取得した場合、リフレッシュトークンを利用して新しいアクセストークンを取得し、APIリクエストを再試行します。以下は、アクセストークンのリフレッシュとリクエストの再試行処理を自動化する例です。

def refresh_access_token(refresh_token)
  token_url = "https://api.example.com/oauth/token"
  response = RestClient.post(token_url, {
    client_id: 'YOUR_CLIENT_ID',
    client_secret: 'YOUR_CLIENT_SECRET',
    refresh_token: refresh_token,
    grant_type: 'refresh_token'
  })

  new_token_data = JSON.parse(response.body)
  new_token_data["access_token"]
end

def make_api_request_with_retry(api_url, access_token, refresh_token)
  response_data = make_api_request(api_url, access_token)

  # トークンが無効な場合、リフレッシュして再試行
  if response_data.nil?
    new_access_token = refresh_access_token(refresh_token)
    response_data = make_api_request(api_url, new_access_token)
  end

  response_data
end

このコードでは、最初のリクエストが失敗した場合、自動的にトークンをリフレッシュし、新しいアクセストークンを用いて再試行します。

3. エラー発生時の再認証プロセス


リフレッシュトークンが無効になるなど、再認証が必要な場合には、ユーザーに対して再度ログイン操作を促し、新たにアクセストークンとリフレッシュトークンを取得します。これには、最初の認可リクエスト手順(a4で説明)に戻って新しい認可コードを取得する方法があります。

4. API応答のロギングと監視


エラーハンドリングと再認証処理の実装に加え、APIの応答内容やエラー情報を記録・監視することで、通信エラーの傾向を分析し、より堅牢なエラーハンドリングを実現できます。

def log_error(error_message)
  File.open("api_errors.log", "a") do |f|
    f.puts("[#{Time.now}] #{error_message}")
  end
end

エラーの原因を迅速に特定し、必要に応じて改善を行うために、エラー内容のロギングは重要な役割を果たします。

5. セキュリティを考慮したエラーハンドリング


エラー処理において、ユーザーに見せるエラーメッセージやログに保存する情報には、アクセストークンや認証情報が含まれないように注意が必要です。これにより、万が一情報が漏洩した場合にも、セキュリティリスクを最小限に抑えることができます。

これらのエラーハンドリングと再認証処理により、API通信を中断することなくスムーズに運用することが可能となり、エラー発生時にも迅速に対応できる柔軟なシステムを構築できます。

実用例:Twitter APIを使用したOAuth認証


ここでは、Twitter APIを例にして、RubyでOAuth認証を実装する具体的な方法を解説します。Twitter APIはOAuth 1.0aを利用して認証を行うため、一般的なOAuth 2.0とは少し異なりますが、APIとの認証およびリクエストの基本的な流れを学ぶには最適な例です。

1. Twitter APIアプリの登録とクレデンシャルの取得


Twitter APIを利用するためには、Twitter Developer Portalでアプリを登録し、クライアントID(APIキー)とクライアントシークレット(APIシークレットキー)を取得する必要があります。

  1. Twitter Developer Portalにアクセスしてアプリを作成します。
  2. アプリ作成後、APIキーとAPIシークレットキーを取得します。
  3. 必要に応じて、認証コールバックURLも設定します。

2. 必要なGemのインストール


TwitterのOAuth 1.0a認証には、oauth Gemを使用します。このGemを使うことで、OAuth 1.0aの署名プロセスを簡単に実装できます。

# oauth gemのインストール
gem install oauth

Gemfileを使用している場合は、以下を追加してbundle installを実行してください。

# Gemfile
gem 'oauth'

3. リクエストトークンの取得


まず、Twitterの認可画面にユーザーを誘導するためのリクエストトークンを取得します。このトークンにより、ユーザーがアプリに対して認証を許可するページへ遷移します。

require 'oauth'

# クライアントの設定
consumer = OAuth::Consumer.new("YOUR_API_KEY", "YOUR_API_SECRET", {
  :site => "https://api.twitter.com",
  :request_token_path => "/oauth/request_token",
  :authorize_path => "/oauth/authorize",
  :access_token_path => "/oauth/access_token"
})

# リクエストトークンの取得
request_token = consumer.get_request_token
puts "Please authorize the application by visiting the following URL:"
puts request_token.authorize_url

ユーザーがこのURLにアクセスし、アプリへのアクセスを許可すると、TwitterはユーザーにPINコードを表示します。ユーザーからこのPINコードを受け取り、次のステップで使用します。

4. アクセストークンの取得


ユーザーから受け取ったPINコードを使用して、アクセストークンを取得します。このトークンを使用して、Twitter APIへの認証付きリクエストを行うことができます。

# ユーザーからPINコードを取得
puts "Enter the PIN provided by Twitter:"
pin = gets.chomp

# アクセストークンの取得
access_token = request_token.get_access_token(:oauth_verifier => pin)
puts "Access token: #{access_token.token}"
puts "Access token secret: #{access_token.secret}"

取得したアクセストークンとシークレットは、今後のAPIリクエストで使用されます。

5. APIリクエストの送信


アクセストークンを利用して、Twitter APIに認証付きリクエストを送信します。例えば、認証されたユーザーのタイムラインを取得するリクエストを以下のように送信できます。

# Twitter APIに認証リクエストを送信
response = access_token.get("https://api.twitter.com/1.1/statuses/home_timeline.json")
timeline = JSON.parse(response.body)

# タイムラインの表示
timeline.each do |tweet|
  puts "#{tweet['user']['name']}: #{tweet['text']}"
end

このコードでは、access_token.getメソッドを使用して、Twitter APIのエンドポイントに対してGETリクエストを送信しています。認証が成功していれば、Twitter APIはタイムラインデータをJSON形式で返し、その内容を表示します。

6. エラーハンドリングとトークンの再利用


Twitter APIのリクエストが失敗した場合、適切にエラーを処理するために、ステータスコードやエラーメッセージを確認することが重要です。また、取得したアクセストークンとシークレットを安全な場所に保存し、再利用することで、ユーザーが再認証する必要がなくなります。

# APIリクエストのエラーハンドリング
begin
  response = access_token.get("https://api.twitter.com/1.1/statuses/home_timeline.json")
  timeline = JSON.parse(response.body)

  timeline.each do |tweet|
    puts "#{tweet['user']['name']}: #{tweet['text']}"
  end

rescue OAuth::Unauthorized => e
  puts "Error: Unauthorized - #{e.message}"
  # 再認証が必要な場合の処理を追加
end

このように、Twitter APIを例にしたOAuth認証の流れを理解することで、他のサービスでも同様のプロセスを応用することができます。この手順を踏むことで、Rubyを使ったOAuth認証が実現し、セキュアにTwitter APIにアクセスできるようになります。

まとめ


本記事では、Rubyを使用してAPIと通信する際のOAuth認証の実装手順について解説しました。OAuthの基本概念から、具体的なセットアップ、認証プロセス、そしてTwitter APIを例にした実用的な実装方法までを順を追って紹介しました。アクセストークンとリフレッシュトークンの取得と管理、エラーハンドリングの方法についても詳しく説明し、セキュアなAPIアクセスの実現方法を学びました。

OAuth認証を正しく実装することで、セキュリティが強化され、APIを通じた通信の信頼性が向上します。RubyでのOAuth認証は、他のAPI認証システムにも応用できるため、開発者にとって価値のあるスキルです。今回の知識を活かして、様々なサービスと安全に連携できるアプリケーション開発を目指しましょう。

コメント

コメントする

目次