练习 - 使用 MSAL 让用户登录

已完成

在本练习中,你将使用适用于 Java 的 Microsoft 身份验证库 (MSAL4J) 在示例 Java Web 应用程序中添加身份验证,并支持用户使用其 Microsoft Entra 帐户进行登录。

本练习中使用的示例应用程序是一个 Java servlet 应用程序,允许用户登录并显示用户名和基本配置文件信息。 同时还支持调用 Microsoft Graph API 以显示一些用户信息。

创建 Java Web 应用程序

从 shell 或命令行:

  1. 为应用程序创建文件夹。

    mkdir ~/javawebapp
    
  2. 将 GitHub 存储库中的示例应用程序克隆到新文件夹中。

    git clone https://github.com/Azure-Samples/ms-identity-java-servlet-webapp-authentication.git ~/javawebapp
    
  3. 切换到本练习的示例应用程序所在的文件夹。

    cd ~/javawebapp/ms-identity-java-servlet-webapp-authentication/2-Authorization-I/call-graph
    

配置应用程序

若要配置代码,请打开首选 IDE 中的应用程序项目,例如 IntelliJ 或 VS Code。

  1. 打开 ./src/main/resources/authentication.properties 文件。

  2. 找到字符串 {enter-your-tenant-id-here}。 将现有值替换为“目录(租户)ID”(如下图所示),因为该应用是使用“仅该组织目录中的帐户”选项注册的。

  3. 找到字符串 {enter-your-client-id-here},并将现有值替换为从 Azure 门户复制的已注册应用程序的应用程序(客户端)ID (clientId)。

    Screenshot highlighting the App ID of an app registered with Microsoft Entra ID on Azure portal.

  4. 找到字符串 {enter-your-client-secret-here},并将现有值替换为在 Azure 门户中创建应用程序期间保存的密钥。

运行应用程序

  1. 请确保 Tomcat 服务器正在运行,并且你具有向其部署 Web 应用的权限。 请确保服务器主机地址为 http://localhost:8080

  2. 使用 Maven 编译和打包项目:

    cd ~/javawebapp/2-Authorization-I/call-graph
    mvn clean package
    
  3. ./target/msal4j-servlet-graph.war 中找到生成的 .war 文件。 若要部署到 Tomcat,请将此 .war 文件复制到 Tomcat 安装目录中的 /webapps/ 目录,然后启动 Tomcat 服务器。

  4. 打开浏览器并导航到 http://localhost:8080/msal4j-servlet-graph/。 系统会你将重定向以使用 Microsoft Entra ID 登录。 成功登录后,应会看到如下页面:

    Screenshot showing user name displayed on the page after successfully signing in to sample application.

  5. 选择“ID 令牌详细信息”按钮以查看一些 ID 令牌的解码声明

验证码概述

可在项目目录 java/com/microsoft/azuresamples/msal4j/ 下的示例应用程序中找到大部分身份验证代码。 它包含多个 servlet,这些 servlet 会在应用程序中提供身份验证终结点,以进行登录、注销和处理来自 Microsoft Entra ID 的重定向回叫。 这些 servlet 使用 java/com/microsoft/azuresamples/msal4j/helpers/ 目录中的帮助程序类来调用 MSAL 提供身份验证方法。 在 AuthenticationFilter.java 中定义的 servlet 筛选器将未经身份验证的请求重定向到受保护的路由,再转到 401 未经授权的 HTTP 错误页面。

若要向应用程序添加身份验证,需要在 java/com/microsoft/azuresamples/msal4j/authservletsjava/com/microsoft/azuresamples/msal4j/authwebapp 目录下包括 servlet 类、java/com/microsoft/azuresamples/msal4j/helpers/ 目录中的帮助程序类和项目中的身份验证 servlet 筛选器 AuthenticationFilter.java。 下面更详细地介绍了 MSAL 验证码。

  1. MSAL4J 在 Maven 上可用。 你需要在项目的 pom.xml 文件中将 MSAL4J 添加为依赖项:

    <dependency>
        <groupId>com.microsoft.azure</groupId>
        <artifactId>msal4j</artifactId>
        <version>1.9.1</version>
    </dependency>
    
  2. 登录过程的第一步是向 Microsoft Entra 租户的 /authorize 终结点发送请求。 利用 MSAL4J ConfidentialClientApplication 实例构造授权请求 URL。 应用将浏览器重定向到此 URL,即用户登录的位置。

    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 是标识提供者将安全令牌发送回的 URI。 收集用户凭据后,Microsoft Entra ID 会将浏览器(以及授权代码)重定向到此 URI。 它必须与 Microsoft Entra 应用注册中的重定向 URI 匹配。
    • SCOPES:范围是应用程序请求的权限。 通常,这三个范围 openid profile offline_access 足以接收用户登录的 ID 令牌响应,并且默认由 MSAL 设置。
  3. Microsoft Entra ID 将会向用户显示登录提示。 如果登录尝试成功,用户的浏览器将被重定向到应用的重定向终结点,终结点中具有有效的“授权代码”。 然后,ConfidentialClientApplication 实例将此授权代码交换为来自 Microsoft Entra ID 的 ID 令牌和访问令牌。

    // 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:必须再次传递上一步中使用的范围。
  4. 如果 acquireToken 成功,则提取令牌声明。 如果 nonce 检查通过,则结果将放入 contextIdentityContextData 实例)并保存到会话中。 这样应用程序便可在需要访问该结果时从会话(通过 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());