D イメージ・ギャラリの作成

このチュートリアルでは、拡張的な例として、イメージを格納および取得するためのイメージ・ギャラリ・サービスの構築を説明します。このチュートリアルでは、Oracle Application Expressを使用します。

トピック:

D.1 はじめに

この項では、APIエントリ・ポイントに関するベスト・プラクティスに加え、この例で使用するいくつかの一般的な規則についても説明します。

トピック:

D.1.1 URIについて

この例全体にわたって、URIおよびURIテンプレートは、ホスト名、コンテキスト・ルートおよびワークスペースのパス接頭辞を省略した形で表記しています。次に例を示します。

gallery/images/

WebブラウザでこのURIにアクセスするには、次の形式でURIを使用します。

https://<host>:<port>/ords/<workspace>/gallery/images/

説明:

  • <host>は、Oracle REST Data Servicesが実行されるホストです。

  • <port>は、Oracle REST Data Servicesがリスニング中のポートです。

  • /ordsは、Oracle REST Data Servicesがデプロイされるコンテキスト・ルートです。

  • /<workspace>/は、RESTfulサービスが定義されるOracle Application Expressワークスペースのワークスペース・パス接頭辞です。

D.1.2 ブラウザのサポートについて

この例では、HTML5および関連する仕様で定義されている多くの最新機能を使用しています。Mozilla FirefoxおよびGoogle Chromeでのみテストされています。Microsoft Internet Explorerまたはスマートフォン/タブレットのWebブラウザではテストされていません。この例では、Mozilla FirefoxまたはGoogle Chromeのいずれかの最新バージョンを使用してください。

D.1.3 Application Expressワークスペースの作成

ギャラリの例のアプリケーションおよび関連オブジェクトの作成は、次の指示に従い、まずOracle Application Expressのワークスペースを作成します(完全開発モードで)。詳細は、Oracle Application Expressのドキュメントを参照してください。

ワークスペースresteasyを呼出し、このワークスペースの管理者ユーザーresteasy_adminを呼び出します。resteasy_adminユーザーがRESTful Servicesユーザー・グループのメンバーであることを確認します。

D.2 ギャラリ・データベース表の作成

ギャラリ・データベース表を作成するには、次のステップを実行します。

  1. resteasyワークスペースにログインします。
  2. 「SQLワークショップ」に移動して、「SQLコマンド」に移動します。
  3. 次のSQLを入力するかコピーして貼り付けます。
    CREATE SEQUENCE GALLERY_SEQ
    /
    CREATE TABLE GALLERY (
      ID NUMBER NOT NULL ENABLE,
      TITLE VARCHAR2(1000) NOT NULL ENABLE,
      CONTENT_TYPE VARCHAR2(1000) NOT NULL ENABLE,
      IMAGE BLOB NOT NULL ENABLE,
      CONSTRAINT GALLERY_PK PRIMARY KEY (ID) ENABLE
    )
    /
    CREATE OR REPLACE TRIGGER BI_GALLERY
     before insert on GALLERY for each row
     begin 
      if :NEW.ID is null then 
       select GALLERY_SEQ.nextval into :NEW.ID from sys.dual;
      end if;
     end;
     /
     ALTER TRIGGER BI_GALLERY ENABLE
     /

D.3 ギャラリRESTfulサービス・モジュールの作成

ギャラリRESTfulサービス・モジュールを作成するには、次のステップを実行します。

  1. 「SQLワークショップ」に移動し、「RESTfulサービス」に移動します。

  2. 右側の「作成」をクリックし、次の情報を入力します。

    • 名前: gallery.example

    • URI接頭辞: gallery/

    • URIテンプレート: images/

    • メソッド: POST

    • ソース: 次のように入力するかコピーして貼り付けます。

      declare 
       image_id integer;
      begin
       insert into gallery (title,content_type,image) 
                   values  (:title,:content_type,:body)
                   returning id into image_id;
       :status := 201;
       :location := image_id;
      end;
      
  3. 「モジュールの作成」をクリックします

  4. images/の下のPOSTハンドラをクリックします。

  5. 「セキュア・アクセスが必要」で、「いいえ」を選択します。

  6. 「パラメータの作成」をクリックし、次にように入力します。

    • 名前: Slug

    • バインド変数名: title

  7. 「作成」をクリックします。

  8. 右下の「パラメータの作成」をクリックし、次の情報を入力します。

    • 名前: X-APEX-FORWARD

    • バインド変数名: location

    • アクセス方法: OUT

  9. 「作成」をクリックします。

  10. 右下の「パラメータの作成」をクリックし、次の情報を入力します。

    • 名前: X-APEX-STATUS-CODE

    • バインド変数名: status

    • アクセス方法: OUT

    • パラメータ・タイプ: Integer

  11. 「作成」をクリックします。

この時点で、新規イメージを格納できる単一のサービスを持つモジュールが作成できました。次に、格納されているイメージの一覧を表示するサービスを追加します。

  1. 「SQLワークショップ」に移動し、「RESTfulサービス」に移動します。

  2. gallery.exampleという名前のモジュールをクリックします。

  3. images/の下の「ハンドラの作成」をクリックし、次の情報を入力します。

    • メソッド: GET

    • ソース・タイプ: Feed

    • セキュア・アクセスが必要: No

    • ソース: 次のように入力するかコピーして貼り付けます。

      select id,title,content_type from gallery order by id desc
      
  4. 「作成」をクリックします。

この時点で、イメージを格納およびリストするサービスが作成できました。次に、個別のイメージを表示するサービスを追加します。

  1. 「SQLワークショップ」に移動し、「RESTfulサービス」に移動します。

  2. gallery.exampleという名前のモジュールをクリックします。

  3. gallery.exampleの下の「テンプレートの作成」をクリックし、次の情報を入力します。

    • URIテンプレート: images/{id}

  4. 「作成」をクリックします。

  5. images/{id}の下の「ハンドラの作成」をクリックし、次の情報を入力します。

    • メソッド: GET

    • ソース・タイプ: Media Resource

    • セキュア・アクセスが必要: No

    • ソース: 次のように入力するかコピーして貼り付けます。

      select content_type, image from gallery where id = :id
      
  6. 「作成」をクリックします。

D.4 ギャラリRESTfulサービスの試用

ギャラリRESTfulサービスを試用するには、次のステップを実行します。

  1. 「SQLワークショップ」に移動し、「RESTfulサービス」に移動します。
  2. gallery.exampleという名前のモジュールをクリックします。
  3. images/の下のGETハンドラをクリックします。
  4. 「テスト」をクリックします。

    次のURIがブラウザに表示されるはずです。

    https://<host>:<port>/ords/resteasy/gallery/images/
    

    次のような内容が表示されるはずです。

    {"next":
     {"$ref":
      "http://localhost:8080/ords/resteasy/gallery/images/?page=1"
     },
     "items":[]
    }
    
    • 内容はギャラリ内の各イメージの場所を示すJSONドキュメントですが、まだイメージを追加していないのでリスト(items[]の要素)は空です。

    • JSONはそのサイズを最小化するために余分な空白がなく、解読が困難なので、JSONの閲覧を容易にするため、ブラウザにJSON表示プラグインを追加することをお薦めします。

ユーザーがギャラリにイメージを追加および表示できるOracle Application Expressアプリケーションを作成するには、「ギャラリ・アプリケーションの作成」を参照してください。

D.5 ギャラリ・アプリケーションの作成

ギャラリRESTfulサービスを使用するOracle Application Expressアプリケーションを作成するには、次のステップを実行します。

  1. Application Builderに移動します。
  2. 「作成」をクリックします。
  3. 「データベース」を選択し、「次へ」をクリックします。
  4. 「名前」フィールドにImage Galleryと入力し、「次へ」をクリックします。
  5. 「アプリケーションの作成」をクリックし、アプリケーションの作成を確認するため再び「アプリケーションの作成」をクリックします。
  6. 「1ページ」、「ホーム」をクリックします。
  7. 「リージョン」の下で、「+(プラス記号)」アイコンをクリックして新規リージョンを作成します。
  8. 「リージョン・タイプ」「HTML」を選択し、「次へ」をクリックし、次のページで「次へ」をクリックします。
  9. リージョン・テンプレートで、「テンプレートなし」を選択します。
  10. 「タイトル」Tasksと入力し、「次へ」をクリックします。
  11. リージョン・ソースのHTMLテキストの入力で、次のように入力します。
    <a class="button" id="upload-btn">Upload Image</a>
    
  12. 「リージョンの作成」をクリックします。
  13. 「リージョン」の下で、「+(プラス記号)」アイコンをクリックして新規リージョンを作成します。
  14. 「リージョン・タイプ」「HTML」を選択し、「次へ」をクリックし、再び「次へ」をクリックします。
  15. リージョン・テンプレートで、DIVリージョンとIDを選択します。
  16. 「タイトル」Imagesと入力します。
  17. 「リージョンの作成」をクリックします。
  18. 「Images」リージョンをクリックし、「属性」タブをクリックします。
  19. 「静的ID」imagesと入力し、「変更の適用」をクリックします。
  20. 「ページ」の下で、編集アイコンをクリックし、「JavaScript」タブをクリックします。
  21. 関数およびグローバル変数の宣言で、次のように入力するかコピーして貼り付けます。
    var workspace_path_prefix = 'resteasy';
     var gallery_url = './' + workspace_path_prefix + '/gallery/images/'; 
     function uploadFiles(url, fileOrBlob, onload) {   
      var name = 'unspecified';
      if ( fileOrBlob['name'] ) {
       name = fileOrBlob.name;
      }   
      var xhr = new XMLHttpRequest();
      xhr.open('POST', url, true);
      xhr.setRequestHeader('Slug',name);
      xhr.onload = onload;   
      xhr.send(fileOrBlob);  
     }
    
     function createUploader() {
      var $upload = $('<div id="uploader" title="Image Upload"\
       style="display:none">\
       <form>\
        <fieldset>\
         <label for="file">File</label>\
         <input type="file" name="file" id="file"\
          class="text ui-widget-content ui-corner-all"/>\
        </fieldset>\
       </form>\
      </div>');
      $(document.body).append($upload);
      $upload.dialog({ 
       autoOpen:false,
       modal: true,
       buttons: {
        "Upload": function() {
         var file = document.querySelector('input[type="file"]');
         uploadFiles(gallery_url,file.files[0],function() {
          $('#uploader').dialog("close");
          getImages();
         });
        },
        "Cancel": function() {
         $('#uploader').dialog("close");
        }
       }  
      });
      $('#upload-btn').click(function() {    
       $('#uploader').dialog("open");  
      }); 
     }  
    
     function getImages() {  
      var xhr = new XMLHttpRequest();
      xhr.open('GET', gallery_url);
      xhr.onload = function(e) {
       var data = JSON.parse(this.response);
       $('#image-list').remove();
       var $images = $('<ol id="image-list"></ol>');
       for ( i in data.items ) {
        var item = data.items[i];
        var uri = item.uri['$ref'];
        var $image = $('<li></li>')                   
                     .append('<a href="' + uri + '" + title="' + 
                             item.title + '"><img src="graphics/'+ uri + 
                             '"></a>');
        $images.append($image);
       }
       $('#images').append($images);
      }  
      xhr.send(); 
     }
     
    
  22. 「ページ・ロード時に実行」では、次の中に入力するかコピーして貼り付けます。
    createUploader();
    getImages();
    
  23. 「変更の適用」をクリックします。
  24. 「ページ」の下で、編集アイコンをクリックし、「CSS」タブをクリックします。
  25. 「インライン」で、次のように入力するかコピーして貼り付けます。
    a img { border:none; } 
    #images ol { margin: 1em auto; width: 100%; } 
    #images li { display: inline; } 
    #images a { background: #fff; display: inline; float: left; 
                margin: 0 0 27px 30px; width: auto; padding: 10px 10px 15px; 
                textalign: center; text-decoration: none; color: #333; 
                font-size: 18px; -webkit-box-shadow: 0 3px 6px rgba(0,0,0,.25);
                -moz-boxshadow: 0 3px 6px rgba(0,0,0,.25); }
    #images img { display: block; width: 190px; margin-bottom: 12px; } 
    label {font-weight: bold; text-align: right;float: left;
           width: 120px; margin-right: 0.625em; }
    label :after {content(":")} 
    input, textarea { width: 250px; margin-bottom: 5px;textalign: left}
    textarea {height: 150px;}
    br { clear: left; }
    #images a:after { content: attr(title); }
    .button {
      border-top: 1px solid #96d1f8; 
      background: #65a9d7;
      background: 
       -webkit-gradient(linear,left top,left bottom,
                        from(#3e779d),to(#65a9d7));    
      background: 
       -webkit-linear-gradient(top, #3e779d, #65a9d7);
      background: 
       -moz-linear-gradient(top, #3e779d, #65a9d7);
      background: -ms-linear-gradient(top, #3e779d, #65a9d7);
      background: -o-linear-gradient(top, #3e779d, #65a9d7);
      padding: 5px 10px;    
      -webkit-border-radius: 8px;
      -moz-border-radius: 8px;
      border-radius: 8px;    
      -webkit-box-shadow: rgba(0,0,0,1) 0 1px 0;
      -moz-box-shadow: rgba(0,0,0,1) 0 1px 0;
      box-shadow: rgba(0,0,0,1) 0 1px 0;
      text-shadow: rgba(0,0,0,.4) 0 1px 0;
      color: white;
      font-size: 14px;
      text-decoration: none;
      vertical-align: middle;
      } 
    
     .button:hover {
      border-top-color: #28597a;    
      background: #28597a;
      color: #ccc;
      cursor: pointer;     
     }
    
     .button:active {
      border-top-color: #1b435e;    
      background: #1b435e;
     }
     
  26. 「変更の適用」をクリックします。

D.6 ギャラリ・アプリケーションの試用

ギャラリ・アプリケーションを試用するには、次のステップを実行します。

  1. Application Builderに移動します。
  2. イメージ・ギャラリ・アプリケーションの横にある「実行」をクリックします。
  3. resteasy_adminユーザーとしてログインします。
  4. 「イメージのアップロード」をクリックします。
  5. イメージ・ファイル(JPEGまたはPNGファイル)を選択して「アップロード」をクリックします。

    アプリケーションにより、アップロードされたイメージが表示されます。

D.7 ギャラリRESTfulサービスの保護

イメージ・アップロード・サービスへのパブリック・アクセスを許可するのは賢明ではなく、ギャラリ内のイメージへのパブリック・アクセスを許可するのもまた理想的なことではありません。そのため、RESTfulサービスへのアクセスを保護する必要があります。

RESTfulサービスは、2種類の認証をサポートしています。

  • ファースト・パーティ認証。Application Expressアプリケーションが簡単に保護されたRESTfulサービスを消費できるように、RESTfulサービスを作成したパーティによって使用されることを意図した認証です。アプリケーションはRESTfulサービスとともに配置する、つまり、同じOracle Application Expressワークスペースに配置する必要があります。アプリケーションは、標準のOracle Application Express認証を使用する必要があります。

  • サード・パーティ認証。これは、RESTfulサービスを作成したパーティとは関連しない、サード・パーティ・アプリケーションによって使用されることを意図した認証です。サード・パーティ認証は、OAuth 2.0プロトコルを利用しています。

トピック:

D.7.1 RESTfulサービスの保護

RESTfulサービスを保護するには、次のステップを実行します。

  1. 「SQLワークショップ」に移動し、「RESTfulサービス」に移動します。

  2. 「タスク」というラベルのセクションで、RESTfulサービスの権限をクリックします。

  3. 「作成」をクリックし、次のように入力します。

    • 名前: example.gallery

    • ラベル: Gallery Access

    • 割当済グループ: RESTful Services

    • 説明: ギャラリ内イメージの表示およびポスト

    • 保護されたモジュール: gallery.example

  4. 「作成」をクリックします。

今制限したRESTfulサービスへのアクセスを確認するには、次のステップを実行します。

  1. 「SQLワークショップ」に移動し、「RESTfulサービス」に移動します。

  2. gallery.exampleという名前のモジュールをクリックします。

  3. images/の下のGETハンドラをクリックします。

  4. 「テスト」をクリックします。

    次の形式のURIがブラウザに表示されるはずです。

    https://<host>:<port>/ords/resteasy/gallery/images
    

    エラー・メッセージとともにエラー・ページが表示されるはずです。

    401 Unauthorized.
    

関連項目:

適切な資格証明が提供されないかぎり保護されたRESTfulサービスにはアクセスできないので、これは予想どおりの結果です。リクエストに必要な資格証明を追加するには、「ファースト・パーティ認証を使用するためのアプリケーションの変更」を参照してください。

D.7.2 ファースト・パーティ認証を使用するためのアプリケーションの変更

ファースト・パーティ認証はCookieおよびApplication Expressアプリケーションによって確立されたユーザー・セッションを利用しますが、Oracle REST Data Servicesでは、Cookieの検証を可能にするために追加の情報を必要とします。アプリケーションIDと現在のセッションIDを知る必要があります。この情報は、Application Expressアプリケーションが常に把握しているもので、RESTfulサービスに送信される各リクエストにカスタムのHTTPヘッダーApex-Sessionを追加することで、RESTfulサービスに対して行われるリクエストに含める必要があります。アプリケーションIDおよびセッションIDは、カンマによって各々が区切られ、ヘッダーの値として送信されます。次に例を示します。

GET /ords/resteasy/gallery/images/
Host: server.example.com
Apex-Session: 102,6028968452563

場合によって、HTTPリクエストにカスタム・ヘッダーを含められないことがあります。たとえば、<img>タグを使用したHTMLページ内でイメージを表示する場合などで、このような場合には別の方法を使用します。アプリケーションIDおよびセッションIDをカンマで区切り、_apex_sessionという名前の問合せパラメータとしてリクエストURIに追加します。次に例を示します。

<img src="graphics/101?_apex_session=102,6028968452563">

この方法は、カスタム・ヘッダーが使用できない場合にのみ使用することに注意してください。それ以外の場合は、セッションIDをURIに含めることで、誤って保存されたり公開されたりするリスクが増加するため、この方法はお薦めしません。

各リクエストにファースト・パーティの認証情報を追加するようにアプリケーションを変更するには、次のステップを実行します。

  1. Application Builderに移動します。

  2. イメージ・ギャラリ・アプリケーションの横にある「編集」ボタンをクリックします。

  3. 「ホーム」という名前の最初のページをクリックします。

  4. 「ページ」の下で、編集アイコンをクリックし、「JavaScript」タブをクリックします。

  5. 関数およびグローバル変数の宣言フィールドの先頭に次を追加します。

    function setApexSession(pathOrXhr) {  
     var appId = $v('pFlowId');
     var sessionId = $v('pInstance');
     var apexSession = appId + ',' + sessionId;   
     if ( typeof pathOrXhr === 'string' ) {
      var path = pathOrXhr;   
      if ( path.indexOf('?') == -1 ) {
       path = path + '?_apex_session=' + apexSession;
      } else {
       path = path + '&_apex_session=' + apexSession;
      }
      return path;
     } else {
      var xhr = pathOrXhr;
      xhr.setRequestHeader('Apex-Session',apexSession);
      return xhr;
     }
    }
    
  6. これは、setApexSession()という名前のJavaScript関数を定義し、この関数はXMLHttpRequestオブジェクトまたはパスを含む文字列にファースト・パーティの認証情報を追加します。

    次に、適切なときにこの関数を呼び出すように既存のJavaScriptコードを変更する必要があります。

  7. xhr.open('POST',url,true);という行の後に、次の行を追加します。

    setApexSession(xhr);
    
  8. xhr.open('GET', gallery_url)という行の後に、次の行を追加します。

    setApexSession(xhr);
    
  9. var uri = item.uri['$ref'];という行を次のように変更します。

    var uri = setApexSession(item.uri['$ref']);
    
  10. 「変更の適用」をクリックします。

  11. 前と同じようにアプリケーションを実行します。今度は、必要な認証情報をRESTfulサービスに提供しているので、動作するはずです。

D.8 サード・パーティ・アプリケーションからのRESTfulサービスへのアクセス

サード・パーティがギャラリRESTfulサービスを消費および使用する場合、サード・パーティ・アプリケーションを登録してOAuth 2.0の資格証明を取得する必要があり、その後、それをインタラクティブなプロセスを開始するために使用でき、ユーザーにかわってRESTfulサービスにアクセスするサード・パーティ・アプリケーションに権限を付与できます。

アプリケーションが登録されると、アクセス・トークンを取得できます。アクセス・トークンは、保護されたRESTfulサービスへの各リクエストに指定する必要があります。Oracle REST Data Servicesは、RESTfulサービスへのアクセスを許可する前に、そのアクセス・トークンを検証します。

OAuth 2.0では、アプリケーションがアクセス・トークンを取得するために使用できる、多数の異なるプロトコル・フローが定義されています。Oracle REST Data Servicesでは、これらのプロトコル・フローのうち次の2つがサポートされています。

  • 認可コード。サード・パーティ・アプリケーションがそのクライアントの資格証明をセキュアに保持できる場合、たとえば、サード・パーティのWebサイトが適切に保護されている場合などにこのフローが使用されます。

  • 暗黙の権限付与サード・パーティ・アプリケーションがその資格証明を機密にしておくことを保証できない場合、たとえば、JavaScriptベースのブラウザ・アプリケーションまたはネイティブのスマートフォン・アプリケーションなどでこのフローが使用されます。

最初のステップは、サード・パーティ・アプリケーションの登録です。このデモを行うため、サード・パーティの開発者を表すユーザーを作成し、このユーザーを使用してアプリケーションを登録します。

この後のステップでは、RESTEASYワークスペースのユーザー・リポジトリにユーザーを作成し、関連する処理を実行します。

ノート:

Oracle REST Data Servicesでは、ワークスペースのユーザー・リポジトリに定義されたユーザーの認証に加え、WebLogic Serverからアクセス可能な任意のユーザー・リポジトリに対する認証も可能です。詳細は、WebLogicサーバーに対する認証を参照してください。

トピック:

D.8.1 サード・パーティの開発者ユーザーの作成

サード・パーティの開発者ユーザー(RESTfulサービスにアクセスするアプリケーションを登録するサード・パーティの開発者のユーザー・アカウント)を作成するには、次のステップを実行します。

  1. 「管理」に移動します。
  2. 「ユーザーとグループの管理」をクリックします。
  3. 「ユーザーの作成」をクリックし、次の情報を入力します。
    • ユーザー名: 3rdparty_dev

    • 電子メール・アドレス: この開発者ユーザーの電子メール・アドレス

    • パスワード: このユーザーのパスワード。

    • ユーザー・グループ: OAuth 2.0 Client Developer

  4. 「ユーザーの作成」をクリックします。

D.8.2 サード・パーティ・アプリケーションの登録

暗黙の権限付与のOAuth 2.0プロトコル・フローを使用するサード・パーティ・アプリケーションを登録するには、次のステップを実行します。

  1. ブラウザで次のURIに移動します。

    https://server:port/ords/resteasy/ui/oauth2/clients/

  2. 前に作成した3rdparty_devユーザーの資格証明を入力し、「サインイン」をクリックします。
  3. クライアントの登録をクリックし、次の情報を入力します。
    • 名前: 3rd Party Gallery

    • 説明: ギャラリRESTfulサービスを消費するデモ

    • レスポンス・タイプ: Token

    • リダイレクトURI: https://example.org/

    • サポート電子メール: 希望する電子メール・アドレス

    • 必須のスコープ: Gallery Access

  4. 「登録」をクリックします。
  5. 次のページに表示されるリストの「3rd Party Gallery」をクリックします。
  6. 「クライアント識別子」および認可URIフィールドの値に注意してください。

D.8.3 アクセス・トークンの取得

アクセス・トークンを取得するには、アクセス承認のプロンプトをユーザーに表示する必要があります。承認プロセスを開始するには、次のURIを使用してユーザーを承認ページに誘導します。

https://server:port/ords/resteasy/oauth2/auth?response_type=token&\
                                              client_id=CLIENT_IDENTIFIER&\
                                              state=STATE

説明:

  • CLIENT_IDENTIFIERは、登録時にアプリケーションに割り当てられたクライアント識別子です。

  • STATEは、クロス・サイト・リクエスト・フォージェリ(CSRF)攻撃を防止するために使用される、アプリケーションによって生成された固有の値です。

Oracle REST Data ServicesのOAuth 2.0実装では、次の点に注意してください。

  • OAuth 2.0仕様では、前述のリクエストでは次の2つのオプション・パラメータが指定できます。

    • redirect_uri: ユーザーがアクセスを承認/拒否した後で、認可サーバーをリダイレクトする場所を指定します。

    • scope: クライアントがアクセスしようとするRESTfulサービスの権限を指定します。

    Oracle REST Data Servicesでは、これらのパラメータのどちらもサポートしていません。どちらの値もクライアントの登録時に指定されるので、ここでそれを繰り返すのは冗長になるためです。これらのパラメータに指定された値は無視されます。

  • OAuth 2.0仕様ではstateパラメータの使用を推奨していますが、Oracle REST Data Servicesでは、CSRF攻撃の防止に役立つという重要性から、このパラメータの使用を必須としています。

  • レスポンス・タイプもアプリケーションの登録時に指定されるため、response_typeパラメータも冗長ですが、OAuth 2.0仕様でこのパラメータは常に必須と明言されているため、含める必要があります。response_typeの値が登録時のレスポンス・タイプと異なる場合はエラーになります。

ブラウザで前述のURIにアクセスすると、ユーザーはサインオンするように促され、その後、アプリケーションのアクセス・リクエストを確認し、アクセスを承認するか拒否するか選択するように求められます。

ユーザーがリクエストを承認した場合、ブラウザは登録されたリダイレクトURIにリダイレクトされ、アクセス・トークンはURIの一部分としてエンコードされます。

https://example.org/#token_type=bearer&\
                                         access_token=ACCESS_TOKEN&\
                                         expires_in=TOKEN_LIFETIME&\
                                         state=STATE

説明:

  • example.orgは、説明目的でのみ使用されます。実際のアプリケーションでは、example.orgはアクセスをリクエストするサード・パーティ・アプリケーションのURLに置き換えられます。

  • ACCESS_TOKENは、現在のユーザー・セッションに割り当てられた一意の推測できないアクセス・トークンで、RESTfulサービスに対する後続のリクエストでこれを指定する必要があります。

  • TOKEN_LIFETIMEは、アクセス・トークンが有効である秒数です。

  • STATEは、認可フローの開始時にアプリケーションによって提供される一意の値です。戻されたstateの値が初期のstateの値と一致しない場合、それはエラー状態であり、攻撃者がCSRF攻撃により認可プロセスを破壊しようとしている可能性があるため、アクセス・トークンは使用しないでください。

ノート:

生成されたすべてのアクセス・トークンのデフォルトのOAuthアクセス・トークン期間(存続期間)を変更できます。これを実現するには、次のようにして、defaults.xml構成ファイルに、OAuthアクセス・トークン期間を秒単位で指定するsecurity.oauth.tokenLifetimeエントリを追加します。

<entry key="security.oauth.tokenLifetime">600</entry>

ユーザーがリクエストを拒否するか、またはユーザーがRESTfulサービスへのアクセスを許可されていない場合、ブラウザは登録されたリダイレクトURIにリダイレクトされ、エラー・メッセージがURIの一部分にエンコードされます。

https://example.org/#error=access_denied&state=STATE

説明:

  • error=access_deniedは、ユーザーがRESTfulサービスへのアクセスを許可されていない、またはアプリケーションのアクセスを承認しないことを選択したことをクライアントに通知します。

  • STATEは、認可フローの開始時にアプリケーションによって提供される一意の値です。戻されたstateの値が初期のstateの値と一致しない場合、それはエラー状態であり、クライアントはそのレスポンスを無視してください。これは、攻撃者がCSRF攻撃により認可プロセスを破壊しようとしている可能性があります。

D.8.4 アクセス・トークンの使用

アプリケーションがアクセス・トークンを取得した後は、保護されたRESTfulサービスに対する各リクエストにそのアクセス・トークンを含める必要があります。これを行うには、Authorizationヘッダーを次の構文でHTTPリクエストに追加します。

Authorization: Bearer ACCESS_TOKEN

説明:

  • ACCESS_TOKENは、アクセス・トークンの値です。

たとえば、JavaScriptベースのブラウザ・アプリケーションが次のようにギャラリ・サービスを呼び出すことがあります。

var accessToken = ... /* initialize with the value of the access token */
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://server:port/ords/resteasy/gallery/images/',true);
/* Add the Access Token to the request */ 
xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);  
xhr.onload = function(e) {   
 /* logic to process the returned JSON document */
 ...   
};
xhr.send();

この例は、XMLHttpRequest.setRequestHeader(name,value)関数を使用し、AuthorizationヘッダーをHTTPリクエストに追加しています。アクセス・トークンが有効ならば、サーバーはギャラリ内のイメージのリストを表示するJSONドキュメントを戻します。

D.8.5 ブラウザのオリジンについて

Webブラウザの主要なセキュリティ概念の1つに同一生成元ポリシーがあり、これは、同じWebサイト(オリジン)から生成されるページ上で実行中のスクリプトには制限なしで互いのデータにアクセスすることを許可し、他のWebサイトから生成されるデータにはアクセスさせないというものです。

オリジンは、Webサイトのプロトコル、ホスト名およびポートによって定義されます。たとえば、https://example.comは1つのオリジンで、https://another.example.comはホスト名が異なるため異なるオリジンです。同様に、http://example.comはプロトコルが異なるためhttps://example.comとは異なるオリジンです。最後に、http://example.comはポートが異なるためhttp://example.com:8080とは異なるオリジンです。

たとえば、ギャラリRESTfulサービスの場合、サード・パーティのクライアントは次の場所にあります。

https://thirdparty.com/gallery.html

そして、ギャラリRESTfulサービスは次の場所にあります。

https://example.com/ords/resteasy/gallery/images/

そこで、同一生成元ポリシーにより、https://thirdparty.comオリジンのスクリプトは同じオリジンのデータにのみアクセス可能であり、https://example.comは明らかに異なるオリジンであることから、gallery.htmlの行うhttps://example.com/ords/resteasy/gallery/images/へのXMLHttpRequestは阻止されます。

https://example.comの作成者がhttps://thirdparty.comの作成者を信頼できない場合には、これは妥当なことです。しかし、作成者が互いに信頼するだけの理由がある場合には、同一生成元ポリシーは制限が大きすぎます。クロス・オリジン・リソース共有(CORS)と呼ばれるプロトコルにより、https://example.comからブラウザにhttps://thirdparty.comを信頼していることを通知する手段が提供されており、gallery.htmlの行うhttps://example.com/ords/resteasy/gallery/images/へのXMLHttpRequestを許可するようにブラウザに指示できます。

D.8.6 クロス・オリジン・リソース共有のためのRESTfulサービスの構成

クロス・オリジン・リソース共有のRESTfulサービスを構成するには、次のステップを実行します。

  1. 「SQLワークショップ」に移動し、「RESTfulサービス」に移動します。
  2. gallery.exampleという名前のモジュールをクリックします。
  3. 許可済オリジンに、RESTfulサービスへのアクセスが許可されるオリジンを入力します(複数オリジンはカンマで区切ります)。
  4. 「変更の適用」を押します。

D.8.7 認可コード・プロトコル・フローを使用したトークンの取得

他の項で、OAuth 2.0の暗黙のプロトコル・フローを使用したアクセス・トークンの取得方法を説明しました。この項では、認可コードのプロトコル・フローを使用して同じことをする方法を説明します。その手順は、認可コードとアクセス・トークンの交換を必要とするため、暗黙のプロトコル・フローよりも少しばかり複雑です。

この項では、cURLを使用してこの交換手順を模倣します。

トピック:

D.8.7.1 クライアント・アプリケーションの登録

クライアントを登録するには、次のステップに従います。

  1. ブラウザで次のURIに移動します。

    https://server:port/ords/resteasy/ui/oauth2/clients/

  2. 3rdparty_devユーザーの資格証明を入力し、「サインイン」をクリックします。
  3. クライアントの登録をクリックし、次の情報を入力します。
    • 名前: Another Gallery

    • 説明: 認可コードのOAuth 2.0プロトコル・フローを使用したデモ

    • レスポンス・タイプ: Code

    • リダイレクトURI : https://gallery.example.demo

    • サポート電子メール: 希望する電子メール・アドレス

    • 必須のスコープ: Gallery Access

  4. 「登録」をクリックします。
  5. 次のページに表示されるリストの「3rd Party Gallery」をクリックします。
  6. 「クライアント識別子」、「クライアント・シークレット」および認可URIフィールドの値に注意してください。
D.8.7.2 認可コードの取得

認可コード・プロトコル・フローの最初のステップは、認可コードを取得することです。認可コードは、アプリケーションのクライアント識別子およびクライアント・シークレットと一緒に提示された場合、アクセス・トークンと交換できる有効期限の短いトークンです。

アクセス・トークンを取得するには、アクセス承認のプロンプトをユーザーに表示する必要があります。承認プロセスを開始するには、次の形式のURIを使用してユーザーを承認ページに誘導します。

https://server:port/ords/resteasy/oauth2/auth?response_type=code&\
                                                client_id=CLIENT_IDENTIFIER&\
                                                state=STATE

説明:

  • CLIENT_IDENTIFIERは、登録時にアプリケーションに割り当てられたクライアント識別子です。

  • STATEは、クロス・サイト・リクエスト・フォージェリ(CSRF)攻撃を防止するために使用される、アプリケーションによって生成された固有の値です。

ユーザーがリクエストを承認した場合、ブラウザは登録されたリダイレクトURIにリダイレクトされ、アクセス・トークンはURIの問合せ文字列の一部分としてエンコードされます。

https://gallery.example.demo?code=AUTHORIZATION_CODE&state=STATE

説明:

  • AUTHORIZATION_CODEは、認可コードの値です。

  • STATEは、認可フローの開始時にアプリケーションによって提供される一意の値です。戻されたstateの値が初期のstateの値と一致しない場合、それはエラー状態であり、認可コードは使用しないでください。これは、攻撃者がCSRF攻撃により認可プロセスを破壊しようとしている可能性があります。

    登録されたリダイレクトURIhttps://gallery.example.demoが存在しないので、ブラウザはサーバーに「見つかりません」のエラーを報告しますが、URIにエンコードされた認可コードの値を見ることはできるので、この例の目的のためには問題ありません。「認可コードとアクセス・トークンの交換」で使用するため、codeパラメータの値をノートにとっておいてください。

D.8.7.3 認可コードとアクセス・トークンの交換

この項では、cURLを使用して認可コードとアクセス・トークンを交換します。認可コードを交換するため、アプリケーションでは、認可コードおよびそのクライアント識別子とクライアント・シークレットを提供し、Oracle REST Data ServicesのOAuth 2.0トークン・エンドへのHTTPリクエストを行う必要があります。資格証明が正しい場合は、Oracle REST Data Servicesによりアクセス・トークンを含むJSONドキュメントが戻されます。アプリケーションはサーバー側(クライアント識別子とクライアント・シークレットが安全に格納されている)から直接Oracle REST Data ServicesへのHTTPリクエストを行うことに注意してください。プロトコル・フローのこのステップではWebブラウザはまったく関与していません。

認可コードとアクセス・トークンを交換するには、次の形式でcURLコマンドを使用します。

curl -i -d "grant_type=authorization_code&code=AUTHORIZATION_CODE" \
     --user CLIENT_IDENTIFER:CLIENT_SECRET \
     https://server:port/ords/resteasy/oauth2/token

説明:

  • AUTHORIZATION_CODEは、認可コードの値です(前の項のリダイレクトURIの問合せ文字列のcodeパラメータにエンコードされたものです)。

  • CLIENT_IDENTIFERは、クライアント識別子の値です。

  • CLIENT_SECRETは、クライアント・シークレットの値です。

cURLにより、前述のコマンドが次のようなHTTPリクエストに変換されます。

POST /ords/resteasy/oauth2/token HTTP/1.1
Authorization: Basic Q0xJRU5UX0lERU5USUZJRVI6Q0xJRU5UX1NFQ1JFVA== 
Host: server:port 
Accept: */* 
Content-Length: 59 
Content-Type: application/x-www-form-urlencoded  

grant_type=authorization_code&code=AUTHORIZATION_CODE

説明:

  • このリクエストは、OAuth 2.0のトークン・エンドoauth2/tokenへのHTTPのPOSTです。

  • Authorizationヘッダーは、HTTP BASIC認証プロトコルを使用し、アプリケーションのアイデンティティを示すためのクライアント識別子とクライアント・シークレットをエンコードします。

  • リクエストのContent-Typeは、フォーム・データ(application/x-www-form-urlencoded)であり、リクエストのコンテンツはOAuth 2.0トークン権限タイプおよびOAuth 2.0認可コード値を示すフォーム・データです。

前述のHTTPリクエストに対し、次のようなレスポンスが生成されます。

HTTP/1.1 200 OK
ETag: "..."
Content-Type: application/json

{
 "access_token":"04tss-gM35uOeQzR_2ve4Q..",
 "token_type":"bearer",
 "expires_in":3600,
 "refresh_token":"UX4FVHhPFJl6GokvTXYw0A.."
}

レスポンスは、リフレッシュ・トークンとともにアクセス・トークンを含むJSONドキュメントです。アプリケーションがアクセス・トークンを取得した後は、保護されたRESTfulサービスに対する各リクエストにそのアクセス・トークンを含める必要があります。これを行うには、Authorizationヘッダーを次の構文でHTTPリクエストに追加します。

Authorization: Bearer ACCESS_TOKEN
D.8.7.4 OAuth 2.0セッションの継続時間の延長

OAuth 2.0セッションの存続期間を延長するため、リフレッシュ・トークンを新規の有効期限を持つ新規のアクセス・トークンと交換できます。リフレッシュ・トークンは認可コードのプロトコル・フローでのみ発行されることに注意してください。

アプリケーションは、認可コードとアクセス・トークンの交換に使用したものと同様のリクエストを行います。アクセス・トークンのためのリフレッシュ・トークンを交換するには、次の形式でcURLコマンドを使用します。

curl -i -d "grant_type=refresh_token&refresh_token=REFRESH_TOKEN" \      
     --user CLIENT_IDENTIFER:CLIENT_SECRET \
     https://server:port/ords/resteasy/oauth2/token

説明:

  • REFRESH_TOKENは、アクセス・トークンが最初に発行されたときに戻されたリフレッシュ・トークンの値です。

  • CLIENT_IDENTIFERは、クライアント識別子の値です。

  • CLIENT_SECRETは、クライアント・シークレットの値です。

cURLにより、前述のコマンドが次のようなHTTPリクエストに変換されます。

POST /ords/resteasy/oauth2/token HTTP/1.1 
Authorization: Basic Q0xJRU5UX0lERU5USUZJRVI6Q0xJRU5UX1NFQ1JFVA==
Host: server:port
Accept: */*
Content-Length: 53 
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=REFRESH_TOKEN

説明:

  • このリクエストは、OAuth 2.0のトークン・エンドoauth2/tokenへのHTTPのPOSTです。

  • Authorizationヘッダーは、HTTP BASIC認証プロトコルを使用し、アプリケーションのアイデンティティを示すためのクライアント識別子とクライアント・シークレットをエンコードします。

  • リクエストのContent-Typeは、フォーム・データ(application/x-www-form-urlencoded)であり、リクエストのコンテンツはOAuth 2.0トークン権限タイプおよびリフレッシュ・トークン値を示すフォーム・データです。

前述のHTTPリクエストに対し、次のようなレスポンスが生成されます。

HTTP/1.1 200 OK
ETag: "..."
Content-Type: application/json  

{
 "access_token":"hECH_Fc7os2KtXT4pDfkzw..",
 "token_type":"bearer",
 "expires_in":3600,
 "refresh_token":"-7OBQKc_gUQG93ZHCi08Hg.."
}

レスポンスは、新規のリフレッシュ・トークンとともに新規のアクセス・トークンを含むJSONドキュメントです。既存のアクセス・トークンおよびリフレッシュ・トークンは無効になり、古いアクセス・トークンを使用してサービスにアクセスしようとすると失敗します。

D.8.8 アクセス・トークンの保護について

OAuth 2.0では、アクセス・トークンは保護されたサービスにアクセスするために提供する必要のある唯一の資格証明です。このため、アクセス・トークンをセキュアに保持することは重要です。トークンのセキュアな保持を促進するため、次のガイドラインに従ってください。

  • すべての保護されたRESTfulサービスにHTTPSを使用することを強くお薦めします。これにより、安全でないチャネル上の盗聴により攻撃者がアクセス・トークンを盗むことができるスヌーピング攻撃を防ぎます。また、攻撃者によるリクエストのペイロードに存在する機密データの表示を防ぐことができます。

  • クライアント・アプリケーションが、信頼できない他のアプリケーションまたはスクリプトとともにブラウザのオリジンに配置されていないことを確認してください。たとえば、アリスというユーザーが次の場所でホストされているクライアント・アプリケーションを持っているとします。

    https://sharedhosting.com/alice/application
    

    別のユーザー(たとえばフレッド)もまた、次のように同じオリジンに彼のアプリケーションをホストできる場合、

    https://sharedhosting.com/fred/trouble
    

    同じオリジンhttps://sharedhost.comを共有しているため、ブラウザではどちらのアプリケーションについても相手のデータにアクセスすることを防ぐことはできず、/alice/applicationが取得したアクセス・トークンを盗むことは、/fred/troubleには容易になります。

    このようなシナリオを防ぐために、アリスのアプリケーションは、たとえば、自身のオリジンにデプロイする必要があります。

    https://alice.sharedhosting.com/application
    

    または

    https://application.alice.sharedhosting.com
    

    または

    https://aliceapp.com