JavaアプリケーションとOracle Identity Cloud Service間の認証について学習

Java WebアプリケーションとOracle Identity Cloud Service間の認証について学習する準備ができました。これには、次の内容の理解が含まれます。

  • Oracle Identity Cloud ServiceがJava SDKに対してサポートする3-legged認証フロー

  • JavaアプリケーションでSDKを使用してOracle Identity Cloud Serviceで認証する場合のユース・ケース

  • Java SDKのメソッドおよび関数

3-Legged認証フローについて学習

Oracle Identity Cloud Serviceでは、Java SDKの3-legged認証フローがサポートされています。このフローでは、ユーザーはOracle Identity Cloud Serviceと直接対話します。ユーザーがサインインすると、Oracle Identity Cloud Serviceは、SDKがユーザー・アクセス・トークンと交換する認可コードを発行します。Java Webアプリケーションでは、このアクセス・トークンを使用して、アプリケーション内の保護されたリソースへのアクセス権をユーザーに付与します。3-leggedフローでは、認可コード付与タイプが使用されます。

セキュリティを強化するために、Oracleでは、3-leggedフローを使用して、認証のためにJava WebアプリケーションをOracle Identity Cloud Serviceと統合することをお薦めします。認可コード付与タイプを使用すると、再認証しなくても、Oracle Identity Cloud Serviceによって保護されている他のアプリケーションにアクセスすることもできます。

JavaアプリケーションでSDKを使用するための主なユースケースについて学習

Java Webアプリケーションは、2つのユースケースを実装します。1つはユーザーの認証用、もう1つはログイン・ユーザーに関する詳細情報へのアクセス用です。

次のデータ・フロー図は、各ユース・ケースのWebブラウザ、WebアプリケーションおよびOracle Identity Cloud Service間のイベント、コールおよびレスポンスのフローを示しています。

ユースケース1: ユーザーの認証

データ・フローは次のようになります。

  1. ユーザーが保護されたリソースをリクエストします。

  2. 認証モジュールは、SDKを使用してOracle Identity Cloud Serviceのrequest-authorization-code URLを生成し、このURLをWebブラウザへのリダイレクト・レスポンスとして送信します。

  3. WebブラウザがURLをコールします。

  4. Oracle Identity Cloud Service「サインイン」ページが表示されます。

  5. ユーザーがOracle Identity Cloud Serviceのサインイン資格証明を送信します。

  6. ユーザーがサインインすると、Oracle Identity Cloud Serviceによってユーザーのセッションが作成され、認可コードが発行されます。

  7. Webアプリケーションは、認可コードをアクセス・トークンと交換するためのバックエンド(またはサーバー間)呼出しを行います。

  8. Oracle Identity Cloud Serviceは、アクセス・トークンとIDトークンを発行します。

  9. セッションが確立され、ユーザーはホーム・ページにリダイレクトされます。

  10. Webアプリケーションの「ホーム」ページが表示されます。

ユースケース2: ユーザーの詳細の取得

データ・フローは次のようになります。

  1. ユーザーは/myProfileリソースをリクエストします。

  2. Webアプリケーションは、Oracle Identity Cloud ServiceのSDKを使用してIDトークンを検証します。

  3. IDトークン検証から返されるデータには、JSONオブジェクトの形式でユーザーの詳細が含まれます。

  4. 「マイ・プロファイル」ページでは、JSONオブジェクトがHTMLコンテンツとしてレンダリングされます。

メソッドおよび関数について学習

Java SDKは、Webアプリケーション・ライブラリとしてロードするJARファイルです。このJARファイルには、次のサード・パーティ・ライブラリが必要です。このライブラリもライブラリにロードする必要があります。

Java SDK zipファイルには、SDKに必要な次のサードパーティ・ライブラリが含まれています。アプリケーションにロードできます。

  • ASMヘルパーMinidev 1.0.2

  • Apache Commons Collections 4.1

  • Apache Commons Lang 3.7

  • JSON小規模および高速パーサー2.3

  • ニンバス LangTag 1.4.3

  • ニンバスJOSE+JWT 5.14

  • OAuth 2.0 SDK with OpenID Connect Extensions 5.30

Oracleには、Java SDKの使用方法を示すサンプルJava Webアプリケーションが用意されています。このアプリケーションは、Servletテクノロジを使用して構築され、Java SDKの動作を理解するための学習エクスペリエンスを簡素化しました。アプリケーションは、Mavenを使用してすべてのライブラリを取得し、Webアプリケーション・リソース(WAR)ファイルを生成します。

Java SDKには、Oracle Identity Cloud Service接続情報とともにロードされたHashMapオブジェクト・インスタンスが必要です。Java Webアプリケーションは、このHashMapインスタンスをConnectionOptions.javaクラスのクラス属性として実装します。

//Instance of a HashMap.
    private Map<String,Object> options = new HashMap<>();
  
public ConnectionOptions(){
        this.options = new HashMap<>();
}
  
public Map<String,Object> getOptions(){
        //Adding Oracle Identity Cloud Service connection parameters to the HashMap instance.
        this.options.put(IDCSTokenAssertionConfiguration.IDCS_HOST, "identity.oraclecloud.com");
        this.options.put(IDCSTokenAssertionConfiguration.IDCS_PORT, "443");
        this.options.put(IDCSTokenAssertionConfiguration.IDCS_CLIENT_ID, "123456789abcdefghij");
        this.options.put(IDCSTokenAssertionConfiguration.IDCS_CLIENT_SECRET, "abcde-12345-zyxvu-98765-qwerty");
        this.options.put(IDCSTokenAssertionConfiguration.IDCS_CLIENT_TENANT, "idcs-abcd1234");
        this.options.put(Constants.AUDIENCE_SERVICE_URL, "https://idcs-abcd1234.identity.oraclecloud.com");
        this.options.put(Constants.TOKEN_ISSUER, "https://identity.oraclecloud.com/");
        this.options.put(Constants.TOKEN_CLAIM_SCOPE, "urn:opc:idm:t.user.me openid");
        this.options.put("SSLEnabled", "true");
        this.options.put("redirectURL", "http://localhost:8080/callback");
        this.options.put("logoutSufix", "/oauth2/v1/userlogout");
        this.options.put(Constants.CONSOLE_LOG, "True");
        this.options.put(Constants.LOG_LEVEL, "DEBUG");
        return this.options;
    }

次に、このSDKの各必須属性の簡単な説明を示します:

名前 説明
IDCSTokenAssertionConfiguration.IDCS_HOST Oracle Identity Cloud Serviceインスタンスのドメイン接尾辞。
IDCSTokenAssertionConfiguration.IDCS_PORT Oracle Identity Cloud Serviceインスタンス用に予約されているHTTPSポート番号(通常は443)。
IDCSTokenAssertionConfiguration.IDCS_CLIENT_ID Identity Cloud ServiceコンソールにJava Webアプリケーションを登録した後に生成されるクライアントIDの値。
IDCSTokenAssertionConfiguration.IDCS_CLIENT_SECRET Identity Cloud ServiceコンソールにJava Webアプリケーションを登録した後に生成されるクライアント・シークレットの値。
IDCSTokenAssertionConfiguration.IDCS_CLIENT_TENANT Oracle Identity Cloud Serviceインスタンスのドメイン接頭辞。通常、この接頭辞はidcs-abcd1234のような値です。
Constants.AUDIENCE_SERVICE_URL Oracle Identity Cloud Serviceインスタンスの完全修飾ドメイン名URL。
Constants.TOKEN_ISSUER この属性については、Oracleでは値https://identity.oraclecloud.comを保持することをお薦めします。
Constants.TOKEN_CLAIM_SCOPE

スコープは、アプリケーションがOracle Identity Cloud Serviceユーザーのかわりにアクセスまたは処理できるデータを制御します。

アプリケーションがSDKを使用してユーザーを認証する場合、スコープはopenidです。アプリケーションがSDKを使用してユーザーの詳細を取得する場合、スコープはurn:opc:idm:t.user.me openidです。

SSLEnabled Oracle Identity Cloud ServiceがHTTPSリクエストまたはHTTPリクエストのどちらに応答するかを示します。この属性については、Oracleでは値trueを保持することをお薦めします。
Constants.CONSOLE_LOG SDKログを有効にします。
Constants.LOG_LEVEL SDKのログ・レベルを示します。

アプリケーションは、logoutSufix属性とredirectURL属性の両方を使用して、これらの値をハード・コードしないようにします。SDKには必要ありません。

Java Webアプリケーションは、/auth URLをマップするAuthServletクラスを実装します。ユーザーがOracle Identity Cloud Serviceでの認証を決定すると、WebブラウザはこのURLに対するリクエストを行います。AuthServletクラスは、Authentication Managerオブジェクトを初期化し、Java SDKを使用してOracle Identity Cloud Serviceの認可コードURLを生成してから、WebブラウザをこのURLにリダイレクトします。

public class AuthServlet extends HttpServlet {
    protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //Loading the configurations
        Map<String, Object> options = new ConnectionOptions().getOptions();
        //Configuration object instance with the parameters loaded.
        IDCSTokenAssertionConfiguration config = new IDCSTokenAssertionConfiguration(options);
	String redirectUrl = (String)options.get("redirectURL");
        String scope = (String)options.get(Constants.TOKEN_CLAIM_SCOPE);
        //Authentication Manager loaded with the configurations.
        AuthenticationManager am = AuthenticationManagerFactory.getInstance(config);
        //Using Authentication Manager to generate the Authorization Code URL, passing the
        //application's callback URL as parameter, along with code value and code parameter.
        String authzURL = am.getAuthorizationCodeUrl(redirectUrl, scope, "1234", "code");
        //Redirecting the browser to the Oracle Identity Cloud Service Authorization URL.
        response.sendRedirect(authzURL);
    }
}

認可コードURLの生成には、次のパラメータが使用されます。

名前 説明
redirectUrl ユーザーがサインインすると、Oracle Identity Cloud ServiceはユーザーのWebブラウザをこのURLにリダイレクトします。このURLは、Identity Cloud Serviceコンソールで信頼できるアプリケーション用に構成するURLと一致する必要があります。Java WebアプリケーションのリダイレクトURLの指定の詳細は、Javaアプリケーションの登録を参照してください。
scope 認証のOAuthまたはOpenID Connectスコープ。このアプリケーションでは、openid認証のみが必要です。
state OAuthプロトコルは、このパラメータを定義します。サンプルJava Webアプリケーションでは、このコードを使用して、Oracle Identity Cloud Serviceへの通信を確立できるかどうかを確認します。この例では、このパラメータの値は1234です。
response_type 認可コード付与タイプに必要なパラメータ。この例では、このパラメータの値はcodeです。

ユーザーがサインインすると、Oracle Identity Cloud Serviceは、開発者が実装する必要があるコールバックURLにユーザーのWebブラウザをリダイレクトします。Java Webアプリケーションは、CallbackServletを使用してこのリクエストを処理します。

public class CallbackServlet extends HttpServlet {
    protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //Loading the configurations
        Map<String, Object> options = new ConnectionOptions().getOptions();
        //After Oracle Identity Cloud Service authenticates the user, the browser is redirected to the
        //callback URL, implemented as a Servlet.
        IDCSTokenAssertionConfiguration config = new IDCSTokenAssertionConfiguration(options);
        //Authentication Manager loaded with the configurations.
        AuthenticationManager am = AuthenticationManagerFactory.getInstance(config);
        //Getting the authorization code from the "code" parameter
        String authzCode = request.getParameter("code");
        //Using the Authentication Manager to exchange the Authorization Code to an Access Token.
        AuthenticationResult ar = am.authorizationCode(authzCode);
        //Getting the Access Token object and its String value.
        AccessToken access_token = ar.getToken(OAuthToken.TokenType.ACCESS_TOKEN);
        String access_token_string = access_token.getToken();
        //Getting the ID Token object and its String value.
        IdToken id_token = ar.getToken(OAuthToken.TokenType.ID_TOKEN);
        String id_token_string = id_token.getToken();
        //Validating both Tokens to acquire information for User such as UserID, 
        //DisplayName, list of groups and AppRoles assigned to the user.
        IdToken id_token_validated = am.validateIdToken(id_token_string);
        //Storing information into the HTTP Session.
        HttpSession session=request.getSession();
        session.setAttribute("access_token", access_token_string);
        session.setAttribute("id_token", id_token_string);
        session.setAttribute("userId", id_token_validated.getUserId());
        session.setAttribute("displayName", id_token_validated.getDisplayName());
	//Forwarding the request to the Home page.
        request.getRequestDispatcher("private/home.jsp").forward(request, response);
    }
}	

アプリケーションは認可コード・パラメータをリクエストし、それを使用してJava SDKのAuthenticationManager.authorizationCode()メソッドをコールし、アクセス・トークンとIDトークンの両方をリクエストします。その後、サーブレットはAuthenticationManagerインスタンスを使用してIDトークンを検証します。AuthenticationManager.validateIdToken()メソッドは、表示名、ユーザーID、およびユーザーに割り当てられたグループとアプリケーション・ロールのリストなどのユーザー情報を含むIdTokenオブジェクトのインスタンスを返します。アクセス・トークンとIDトークンの値、および一部のユーザー情報はHTTPセッション・オブジェクトに格納されるため、リクエストは/private/home.jspというアプリケーション内の保護されたURLに転送できます。/private/home.jspに加えて、Java Webアプリケーションには/private/myProfile.jspという別の保護されたURLがあります。

myProfile.jspページでは、次のコード・サンプルを使用して、Oracle Identity Cloud Serviceにサインインしているユーザーの詳細を取得します:


<%if(session.getAttribute("access_token")==null) response.sendRedirect("/login.html");%>
<!DOCTYPE html>
<%
/**
 * The /private/myProfile.jsp page accesses the user's access token previously set in the session, 
 * calls the getAuthenticatedUser method to retrieve the user's information, and then formats it as HTML.
 * @author felippe.oliveira@oracle.com
 * @Copyright Oracle
*/
  java.util.Map<String, Object> options = new sampleapp.util.ConnectionOptions().getOptions();
  //Configuration object instance with the parameters loaded.
  oracle.security.jps.idcsbinding.shared.IDCSTokenAssertionConfiguration configuration = new oracle.security.jps.idcsbinding.shared.IDCSTokenAssertionConfiguration(options);
  oracle.security.jps.idcsbinding.shared.AuthenticationManager am = oracle.security.jps.idcsbinding.shared.AuthenticationManagerFactory.getInstance(configuration);
  
  //Getting the Access Token and the Id Token from the session object
  String access_token_string = (String)session.getAttribute("access_token");
  String id_token_string = (String)session.getAttribute("id_token");

  //Validating the ID Token to get user information, groups and app roles associated with the user.
  oracle.security.jps.idcsbinding.api.AccessToken access_token_validated = am.validateAccessToken(access_token_string);
  oracle.security.jps.idcsbinding.api.IdToken id_token_validated = am.validateIdToken(id_token_string);
%>

/private/myProfile.jspページは、セッションで設定されたIDトークン値にアクセスし、AuthenticationManager.validateIdToken()メソッドをコールしてユーザーの情報を取得し、HTMLコンテンツとして表示します。


<p><b>Information from the Identity Token:</b></p><p><%
out.println("DisplayName = "+ id_token_validated.getDisplayName() +"<br>");
out.println("IdentityDomain = "+ id_token_validated.getIdentityDomain() +"<br>");
out.println("UserName = "+ id_token_validated.getUserName()+"<br>");
out.println("UserId = "+ id_token_validated.getUserId()+"<br>");
out.println("Issuer = "+ id_token_validated.getIssuer()+"<br>");

java.util.List<oracle.security.jps.idcsbinding.api.IDCSAppRole> appRoles = id_token_validated.getAppRoles();
if(!appRoles.isEmpty()){
   out.println("App Roles:<br>");
   for(oracle.security.jps.idcsbinding.api.IDCSAppRole appRole: appRoles){
      out.println("&nbsp;appRole = "+ appRole.getName() +"<br>");
   }//for
}//if

java.util.List<oracle.security.jps.idcsbinding.api.IDCSGroup> groups = id_token_validated.getGroupMembership();
if(!groups.isEmpty()){
   out.println("Groups:<br>");
   for(oracle.security.jps.idcsbinding.api.IDCSGroup group: groups){
      out.println("&nbsp;group = "+ group.getName() +"<br>");
   }//for
}//if
%>
</p>
<p><b>Access Token:</b></p><p><%=access_token_string%></p>

アプリケーションとOracle Identity Cloud Service間のシングル・サインオンからユーザーをサインアウトするために、Java Webアプリケーションは、/logout URLをマップするLogoutServletを実装します。

public class LogoutServlet extends HttpServlet {
    protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session=request.getSession();
        String id_token = (String)session.getAttribute("id_token");
        session.invalidate();
        Map options = new ConnectionOptions().getOptions();
        String logoutURL = (String)options.get(Constants.AUDIENCE_SERVICE_URL) + (String)options.get("logoutSufix") +"?post_logout_redirect_uri=http%3A//localhost%3A8080&id_token_hint="+ id_token;
        response.sendRedirect(logoutURL);
    }
}

このサーブレットにより、アプリケーションのセッションが無効になり、ユーザーのWebブラウザがOracle Identity Cloud ServiceのOAuthログアウトURLにリダイレクトされます。