演習 - MSAL を使用してユーザーをサインインさせる
この演習では、Java 用 Microsoft 認証ライブラリ (MSAL4J) を使用して、サンプルの Java Web アプリケーションに認証を追加し、ユーザーが自分の Microsoft Entra アカウントでサインインできるようにします。
この演習のサンプル アプリケーションである Java サーブレット アプリケーションを使うと、ユーザーはサインインして、ユーザー名と基本的なプロファイル情報を表示できます。 また、Microsoft Graph API を呼び出して、ユーザー情報を表示することもできます。
Java Web アプリケーションの作成
シェルまたはコマンド ラインから:
アプリケーション用のフォルダーを作成します。
mkdir ~/javawebapp
GitHub リポジトリから新しいフォルダーにサンプル アプリケーションをクローンします。
git clone https://github.com/Azure-Samples/ms-identity-java-servlet-webapp-authentication.git ~/javawebapp
この演習のサンプル アプリケーションがあるフォルダーに移動します。
cd ~/javawebapp/ms-identity-java-servlet-webapp-authentication/2-Authorization-I/call-graph
アプリケーションを構成する
コードを構成するには、IntelliJ や VS Code などの適切な IDE でアプリケーション プロジェクトを開きます。
./src/main/resources/authentication.properties ファイルを開きます。
aad.authority
プロパティで、文字列{enter-your-tenant-id-here}
を見つけます。 [この組織のディレクトリ内のアカウントのみ] オプションを使ってアプリを登録したので、次の図に示すように、既存の値を [ディレクトリ (テナント) ID] 値に置き換えます。aad.clientId
プロパティで、文字列{enter-your-client-id-here}
を見つけて、既存の値を、Azure portal からコピーした登録済みアプリケーションの [アプリケーション (クライアント) ID] 値 (clientId
値) に置き換えます。aad.secret
プロパティで、文字列{enter-your-client-secret-here}
を見つけて、既存の値を、Azure portal でアプリを作成するときに保存したキー値に置き換えます。
アプリケーションの実行
Tomcat サーバーが実行されていること、および Web アプリをそこにデプロイする権限を持っていることを確認します。 サーバーのホスト アドレスが
http://localhost:8080
であることを確認します。Maven を使用してプロジェクトをコンパイルし、パッケージ化します。
cd ~/javawebapp/2-Authorization-I/call-graph mvn clean package
./target/msal4j-servlet-graph.war で、結果の .war ファイルを見つけます。 Tomcat にデプロイするには、この .war ファイルを Tomcat のインストール ディレクトリ内の /webapps/ ディレクトリにコピーして、Tomcat サーバーを起動します。
ブラウザーを開き、
http://localhost:8080/msal4j-servlet-graph/
に移動します。 Microsoft Entra ID でのサインインにリダイレクトされます。 サインインに成功すると、次のようなページが表示されます。[ID Token Details] (ID トークンの詳細) ボタンを選ぶと、ID トークンのデコードされたクレームの一部が表示されます。
認証コードの概要
サンプル アプリケーションの認証コードの大部分は、プロジェクトの java/com/microsoft/azuresamples/msal4j/
ディレクトリにあります。 そこに含まれる複数のサーブレットにより、サインイン、サインアウト、および Microsoft Entra ID からのリダイレクト コールバックの処理のための認証エンドポイントが、アプリケーションに提供されます。 これらのサーブレットでは、ディレクトリ java/com/microsoft/azuresamples/msal4j/helpers/ 内のヘルパー クラスを使用して、MSAL によって提供される認証メソッドが呼び出されます。 AuthenticationFilter.java
で定義されているサーブレット フィルターにより、保護されたルートへの認証されていない要求は、401 未承認 HTTP エラー ページにリダイレクトされます。
アプリケーションに認証を追加するには、java/com/microsoft/azuresamples/msal4j/authservlets
と java/com/microsoft/azuresamples/msal4j/authwebapp
ディレクトリにサーブレット クラスを、java/com/microsoft/azuresamples/msal4j/helpers/ ディレクトリにヘルパー クラスを、プロジェクトに認証サーブレット フィルター AuthenticationFilter.java
を、それぞれ含める必要があります。 詳細な MSAL 認証コードを次に示します。
MSAL4J は Maven で使用できます。 MSAL4J を依存関係としてプロジェクトの pom.xml ファイルに追加する必要があります。
<dependency> <groupId>com.microsoft.azure</groupId> <artifactId>msal4j</artifactId> <version>1.17.2</version> </dependency>
サインイン プロセスの最初のステップでは、Microsoft Entra テナントの
/authorize
エンドポイントに要求を送信します。 承認要求の URL を作成するには、MSAL4J のConfidentialClientApplication
インスタンスを利用します。 アプリでブラウザーをこの URL にリダイレクトし、ユーザーはそこでサインインします。 次のコードは、AuthHelper
クラスのredirectToAuthorizationEndpoint
メソッドの実装からの抜粋です。final ConfidentialClientApplication client = getConfidentialClientInstance(); AuthorizationRequestUrlParameters parameters = AuthorizationRequestUrlParameters .builder(Config.REDIRECT_URI, Collections.singleton(Config.SCOPES)) .responseMode(ResponseMode.QUERY).prompt(Prompt.SELECT_ACCOUNT).state(state).nonce(nonce).build(); final String authorizeUrl = client.getAuthorizationRequestUrl(parameters).toString(); contextAdapter.redirectUser(authorizeUrl);
AuthorizationRequestUrlParameters
:AuthorizationRequestUrl
を作成するために設定する必要があるパラメーター。REDIRECT_URI
:リダイレクト URI は、ID プロバイダーがセキュリティ トークンを送り返す先の URI です。 ユーザー資格情報が収集された後、Microsoft Entra ID によってこの URI にブラウザーが認証コードと共にリダイレクトされます。 Microsoft Entra でのアプリ登録のリダイレクト URI と一致している必要があります。SCOPES
:スコープは、アプリケーションによって要求されるアクセス許可です。 通常、ユーザー サインイン用の ID トークン応答を受け取るには 3 つのスコープopenid profile offline_access
で十分であり、これらは MSAL によって既定で設定されます。
Microsoft Entra ID により、ユーザーにサインイン プロンプトが表示されます。 サインインの試行が成功した場合、ユーザーのブラウザーは、エンドポイントでの有効な承認コードと共にアプリのリダイレクト エンドポイントにリダイレクトされます。 その後、
ConfidentialClientApplication
のインスタンスによって、この認証コードが Microsoft Entra ID の ID トークンとアクセス トークンに交換されます。 次のコードは、AuthHelper
クラスのprocessAADCallback
メソッドの実装からの抜粋です。// First, validate the state, then parse any error codes in response, then extract the authCode. Then: // build the auth code params: final AuthorizationCodeParameters authParams = AuthorizationCodeParameters .builder(authCode, new URI(Config.REDIRECT_URI)).scopes(Collections.singleton(Config.SCOPES)).build(); // Get a client instance and leverage it to acquire the token: final ConfidentialClientApplication client = AuthHelper.getConfidentialClientInstance(); final IAuthenticationResult result = client.acquireToken(authParams).get();
AuthorizationCodeParameters
: ID トークンやアクセス トークンと承認コードを交換するために設定する必要があるパラメーター。authCode
: リダイレクト エンドポイントで受信された承認コード。REDIRECT_URI
: 前のステップで使用したリダイレクト URI を、再度渡す必要があります。SCOPES
: 前のステップで使用したスコープを、再度渡す必要があります。
acquireToken
が成功すると、トークン クレームが抽出されます。 nonce チェックに合格すると、結果はcontext
(IdentityContextData
のインスタンス) に配置されて、セッションに保存されます。 その後、アプリケーションでそれにアクセスする必要があるときは常に、セッションからIdentityContextAdapterServlet
のインスタンスを使用してこれをインスタンス化できます。// parse IdToken claims from the IAuthenticationResult: // (the next step - validateNonce - requires parsed claims) context.setIdTokenClaims(result.idToken()); // if nonce is invalid, stop immediately! this could be a token replay! // if validation fails, throws exception and cancels auth: validateNonce(context); // set user to authenticated: context.setAuthResult(result, client.tokenCache().serialize());