使用组和组声明保护 Java WebSphere 应用

本文介绍如何创建使用适用于 Java 的 Microsoft 身份验证库(MSAL)登录用户的 Java WebSphere 应用。 该应用还基于 Microsoft Entra ID 安全组成员身份限制对页面的访问。

下图显示了应用的拓扑:

显示应用的拓扑的关系图。

客户端应用使用 MSAL for Java(MSAL4J)将用户登录到 Microsoft Entra ID 租户,并从 Microsoft Entra ID 获取 ID 令牌 。 ID 令牌证明用户已通过此租户进行身份验证。 应用根据用户的身份验证状态和组成员身份保护其路由。

有关涵盖此方案的视频,请参阅 使用应用角色、安全组、范围和目录角色在应用程序中实现授权。

先决条件

建议

设置示例

以下部分演示如何设置示例应用程序。

克隆或下载示例存储库

若要克隆示例,请打开 Bash 窗口并使用以下命令:

git clone https://github.com/Azure-Samples/ms-identity-msal-java-samples.git
cd 3-java-servlet-web-app/3-Authorization-II/groups

或者,导航到 ms-identity-msal-java-samples 存储库,然后将其下载为 .zip 文件并将其提取到硬盘驱动器。

重要

若要避免 Windows 上的文件路径长度限制,请将存储库克隆或提取到硬盘驱动器根附近的目录中。

将示例应用程序注册到 Microsoft Entra ID 租户

此示例中有一个项目。 以下部分演示如何使用Azure 门户注册应用。

选择要在其中创建应用程序的Microsoft Entra ID 租户

若要选择租户,请使用以下步骤:

  1. 登录到 Azure 门户

  2. 如果帐户存在于多个Microsoft Entra ID 租户中,请选择Azure 门户角的配置文件,然后选择“切换目录”,将会话更改为所需的Microsoft Entra ID 租户。

注册应用(java-servlet-webapp-groups)

首先,按照快速入门中的说明在Azure 门户中注册新应用:将应用程序注册到Microsoft 标识平台。

然后,使用以下步骤完成注册:

  1. 导航到面向开发人员的 Microsoft 标识平台应用注册页

  2. 选择“新注册”。

  3. 显示的“注册应用程序”页 中,输入以下应用注册信息:

    • 在“ 名称 ”部分中,输入一个有意义的应用程序名称,以便向应用的用户显示,例如 java-servlet-webapp-groups
    • 在“支持的帐户类型”下,选择“仅此组织目录中的帐户” 。
    • “重定向 URI”部分中,在组合框中选择“Web”,并输入以下重定向 URI: http://localhost:8080/msal4j-servlet-groups/auth/redirect
  4. 选择“注册”以创建应用程序。

  5. 在应用的注册页上,查找并复制 应用程序(客户端)ID 值以供以后使用。 在应用的配置文件或文件中使用此值。

  6. 选择“保存”以保存更改。

  7. 在应用的注册页上,选择 导航窗格中的“证书和机密 ”,打开可以生成机密并上传证书的页面。

  8. 在“客户端密码”部分中,选择“新建客户端密码” 。

  9. 键入说明 - 例如应用 机密

  10. 选择可用持续时间之一: 1 年2 年内永不过期

  11. 选择 添加 。 将显示生成的值。

  12. 复制并保存生成的值,以便在后续步骤中使用。 代码的配置文件需要此值。 此值不会再次显示,不能通过任何其他方式检索该值。 因此,在导航到任何其他屏幕或窗格之前,请务必将其从Azure 门户保存。

  13. 在应用的注册页上,从导航窗格中选择 API 权限 以打开该页面,以添加对应用程序所需的 API 的访问权限。

  14. 选择添加权限

  15. 确保已选择“Microsoft API”选项卡。

  16. 在“常用 Microsoft API”部分,选择“Microsoft Graph”。

  17. “委派权限部分中,从列表中选择 User.ReadGroupMember.Read.All 。 如有必要,请使用搜索框。

  18. 选择“添加权限”。

  19. GroupMember.Read.All 需要管理员同意,因此请选择 {tenant} 的“授予/撤销管理员许可”,当系统询问是否要为租户中的所有帐户授予请求的权限同意时,请选择“ ”。 需要Microsoft Entra ID 租户管理员才能执行此操作。


配置应用(java-servlet-webapp-groups)以使用应用注册

使用以下步骤配置应用:

注意

在以下步骤中, ClientIDApplication IDAppId相同。

  1. 在 IDE 中打开项目。

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

  3. 找到字符串 {enter-your-tenant-id-here}。 如果将应用注册到此组织目录中的 “帐户 ”选项,请将现有值替换为Microsoft Entra 租户 ID。

  4. 找到字符串{enter-your-client-id-here},并将现有值替换为从Azure 门户复制的应用程序 ID 或clientIdjava-servlet-webapp-groups应用程序。

  5. 找到字符串{enter-your-client-secret-here},并将现有值替换为在创建java-servlet-webapp-groups应用期间保存的值(在Azure 门户中)。

配置安全组

可以使用以下选项来进一步配置应用程序以接收组声明:

注意

若要获取本地组的samAccountName或组 ID,请参阅有关使用从 Active Directory 同步的组属性的先决条件部分,该节使用 Microsoft Entra ID 为应用程序配置组声明中的组属性On Premises Group Security Identifier

将应用程序配置为接收已登录用户分配到的所有组,包括嵌套组

若要配置应用程序,请使用以下步骤:

  1. 在应用的注册页上,选择 导航窗格中的“令牌配置 ”以打开页面,可在其中配置颁发给应用程序的声明提供的令牌。

  2. 选择“ 添加组声明 ”以打开 “编辑组声明 ”屏幕。

  3. 选择 “安全组 ”或“ 所有组”(包括通讯组列表,但不包括分配给应用程序的组) 选项。 选择这两个选项会否定安全组选项的效果

  4. “ID ”部分下,选择“ 组 ID”。 此选择会导致Microsoft Entra ID 发送用户在登录用户后收到的 ID 令牌的组声明中向用户分配的对象 ID ID。

将应用程序配置为从用户可能分配到的已筛选组组接收组声明值

如果出现以下情况,此选项非常有用:

  • 应用程序对一组选定的组感兴趣,这些组可能分配有登录用户。
  • 应用程序对此用户分配到的租户中的每个安全组不感兴趣。

此选项可帮助应用程序避免 超额 问题。

注意

此功能在 Microsoft Entra ID 免费版不可用。

使用此选项时,嵌套组分配不可用。

若要在应用中启用此选项,请使用以下步骤:

  1. 在应用的注册页上,选择 导航窗格中的“令牌配置 ”以打开页面,可在其中配置颁发给应用程序的声明提供的令牌。

  2. 选择“ 添加组声明 ”以打开 “编辑组声明 ”屏幕。

  3. 选择 分配给应用程序的组。

    选择其他选项(例如 安全组所有组(包括分配给应用程序的通讯组,但不包括分配给应用程序的组) 会否定应用从选择使用此选项的好处。

  4. “ID ”部分下,选择“ 组 ID”。 此选择将导致Microsoft Entra ID,发送用户分配到的组的对象 ID 的组 ID。ID 令牌组声明。

  5. 如果使用“公开 API”选项公开 Web API,则还可以选择“访问”部分下的“组 ID”选项。 此选项会导致Microsoft Entra ID,在访问令牌的组声明中向用户分配的组的对象 ID

  6. 在应用的注册页上,选择 导航窗格中的“概述 ”以打开应用程序概述屏幕。

  7. 选择具有本地目录中托管应用程序中应用程序名称的超链接。 此字段标题可能被截断 - 例如 Managed application in ...。 选择此链接时,导航到 与在其中创建应用程序的租户中的应用程序服务主体关联的企业应用程序概述 页。 可以使用浏览器的后退按钮导航回应用注册页。

  8. 在导航窗格中选择“ 用户和组 ”,打开可以向应用程序分配用户和组的页面。

  9. 选择“添加用户”。

  10. 从结果屏幕中选择 “用户和组 ”。

  11. 选择要分配给此应用程序的组。

  12. 选择“ 选择 ”以完成组选择。

  13. 选择“ 分配 ”以完成组分配过程。

    当用户登录到应用时,应用程序现在会在组声明中接收这些选定的组,该组是其中一个或多个已分配组的成员。

  14. 在导航窗格中选择“属性以打开列出应用程序的基本属性的页面。将“需要用户分配?”标志设置为“是”。

重要

将“用户分配”设置为“是时,Microsoft Entra ID 会检查“用户和组”窗格中只有分配给应用程序的用户才能登录到应用。 可以直接分配用户,也可以分配他们所属的安全组。

配置应用(java-servlet-webapp-groups)以识别组 ID

使用以下步骤配置应用:

重要

“令牌配置”页上,如果选择了除 groupID 以外的任何选项(例如 DNSDomain\sAMAccountName),则应在以下步骤中输入组名称,例如, contoso.com\Test Group 而不是对象 ID:

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

  2. 找到字符串{enter-your-admins-group-id-here},并将现有值替换为从Azure 门户复制的组的对象 IDGroupAdmin。 同时从占位符值中删除大括号。

  3. 找到字符串{enter-your-users-group-id-here},并将现有值替换为从Azure 门户复制的组的对象 IDGroupMember。 同时从占位符值中删除大括号。

生成示例

若要使用 Maven 生成示例,请导航到包含 示例pom.xml 文件的目录,然后运行以下命令:

mvn clean package

此命令生成可在 各种应用程序服务器上运行的 .war 文件。

运行示例

这些说明假定你已安装 WebSphere 并设置服务器。 可以在 Azure 虚拟机上部署 WebSphere 应用程序服务器(传统)群集中的指南进行基本服务器设置。

在部署到 WebSphere 之前,请使用以下步骤在示例本身中进行一些配置更改,然后生成或重新生成包:

  1. 导航到应用的 authentication.properties 文件,并将要使用的服务器 URL 和端口号的值 app.homePage 更改为要使用的端口号,如以下示例所示:

    # app.homePage is by default set to dev server address and app context path on the server
    # for apps deployed to azure, use https://your-sub-domain.azurewebsites.net
    app.homePage=https://<server-url>:<port-number>/msal4j-servlet-auth/
    
  2. 保存此文件后,使用以下命令重新生成应用:

    mvn clean package
    
  3. 代码生成完成后,将 .war 文件复制到目标服务器的文件系统。

还需要在 Azure 应用注册中进行更改,在Azure 门户中将其设置为“身份验证”选项卡上的“重定向 URI”值

  1. 导航到面向开发人员的 Microsoft 标识平台应用注册页

  2. 使用搜索框搜索应用注册 ,例如 java-servlet-webapp-authentication

  3. 通过选择应用名称打开应用注册。

  4. 从菜单中选择“身份验证”。

  5. “Web - 重定向 URI”部分中,选择“添加 URI”。

  6. 填写应用的 URI,追加 /auth/redirect - 例如 https://<server-url>:<port-number>/auth/redirect

  7. 选择“保存”。

使用以下步骤使用 WebSphere 的集成解决方案控制台部署示例:

  1. “应用程序”选项卡上,选择“新建应用程序,然后选择“新建企业应用程序”。

  2. 选择生成的 .war 文件,然后选择“下一步,直到到达 Web 模块安装步骤的映射上下文根。 其他默认设置应正常。

  3. 对于上下文根,请将它设置为与在示例配置/Azure 应用注册中设置的“重定向 URI”中的端口号之后的值相同。 也就是说,如果重定向 URI 为 http://<server-url>:9080/msal4j-servlet-auth/,则上下文根应为 msal4j-servlet-auth

  4. 选择“完成”。

  5. 应用程序安装完成后,转到“应用程序”选项卡的“WebSphere 企业应用程序”部分。

  6. 从应用程序列表中选择安装的 .war 文件,然后选择“开始进行部署。

  7. 部署完成后,导航到 http://<server-url>:9080/{whatever you set as the context root} 并应能够看到应用程序。

探索示例

使用以下步骤浏览示例:

  1. 请注意屏幕中心显示的已登录或注销状态。
  2. 选择角落中的上下文敏感按钮。 首次运行应用时,此按钮将 读取登录
  3. 在下一页上,按照说明使用 Microsoft Entra ID 租户中的帐户登录。
  4. 在同意屏幕上,请注意请求的范围。
  5. 请注意,上下文敏感按钮现在显示 “注销 ”并显示用户名。
  6. 选择 ID 令牌详细信息 以查看某些 ID 令牌的解码声明。
  7. 选择“组以查看有关已登录用户的安全组成员身份的任何信息。
  8. 选择“仅限管理员”或“常规用户以访问受声明保护的终结点的组。
    • 如果已登录的用户位于 GroupAdmin 组中,则用户可以输入这两个页面。
    • 如果登录用户位于 GroupMember 组中,则用户只能输入 “常规用户 ”页。
    • 如果登录的用户未处于两个组中,则用户无法访问这两个页面之一。
  9. 使用角落中的按钮注销。
  10. 注销后,选择 “ID 令牌详细信息 ”,观察应用在未授权用户时显示 401: unauthorized 错误而不是 ID 令牌声明。

关于代码

此示例使用 MSAL for Java (MSAL4J) 登录并获取可能包含组声明的 ID 令牌。 如果 ID 令牌中存在太多用于排放的组,该示例使用 Microsoft Graph SDK for Java 从 Microsoft Graph 获取组成员身份数据。 根据用户所属的组,登录用户可以访问任何一个、一个或两个受保护页面, Admins Only 以及 Regular Users

如果要复制此示例的行为,则必须使用 Maven 将 MSAL4J 和 Microsoft Graph SDK 添加到项目中。 可以在 src/main/java/com/microsoft/azuresamples/msal4j 文件夹中复制pom.xml文件和 authservlet 文件夹的内容。 还需要 authentication.properties 文件。 这些类和文件包含可在各种应用程序中使用的通用代码。 也可以复制示例的其余部分,但会专门生成其他类和文件来解决此示例的目标。

目录

下表显示了示例项目文件夹的内容:

文件/文件夹 说明
src/main/java/com/microsoft/azuresamples/msal4j/groupswebapp/ 此目录包含定义应用的后端业务逻辑的类。
src/main/java/com/microsoft/azuresamples/msal4j/authservlets/ 此目录包含用于登录和注销终结点的类。
____Servlet.java 所有可用的终结点在以 ____Servlet.java 结尾.java类中定义。
src/main/java/com/microsoft/azuresamples/msal4j/helpers/ 用于身份验证的帮助程序类。
AuthenticationFilter.java 将未经身份验证的请求重定向到受保护的终结点到 401 页。
src/main/resources/authentication.properties Microsoft Entra ID 和程序配置。
src/main/webapp/ 此目录包含 UI - JSP 模板
CHANGELOG.md 示例更改列表。
CONTRIBUTING.md 参与示例的指南。
许可证 示例的许可证。

在令牌中处理组声明,包括处理超额

以下部分介绍了应用如何处理组声明。

组声明

登录用户所属的安全组的对象 ID 在令牌的组声明中返回,如以下示例所示:

{
  ...
  "groups": [
    "0bbe91cc-b69e-414d-85a6-a043d6752215",
    "48931dac-3736-45e7-83e8-015e6dfd6f7c",]
  ...
}

组超额声明

为了确保令牌大小不超过 HTTP 标头大小限制,Microsoft 标识平台限制它在组声明中包含的对象 ID 数。

SAML 令牌的超额限制为 150,JWT 令牌为 200,对于单页应用程序为 6。 如果用户属于超出超额限制的组的成员,则Microsoft 标识平台不会在令牌中的组声明中发出组 ID。 而是在令牌中包含一个超额声明,该声明指示应用程序查询Microsoft图形 API检索用户的组成员身份,如以下示例所示:

{
  ...
  "_claim_names": {
    "groups": "src1"
    },
    {
   "_claim_sources": {
    "src1": {
        "endpoint":"[Graph Url to get this user's group membership from]"
        }
    }
  ...
}

在此示例中创建超额方案以进行测试

若要创建超额方案,可以使用以下步骤:

  1. 可以使用 AppCreationScripts 文件夹中提供的 BulkCreateGroups.ps1 文件创建大量组并向其分配用户。 此文件有助于在开发过程中测试超额方案。 请记住更改 BulkCreateGroups.ps1 脚本中提供的用户objectId

  2. 运行此示例并发生超额时,将在用户登录后在主页中看到 _claim_names

  3. 我们强烈建议你尽可能使用组筛选功能,以避免遇到组超额。 有关详细信息,请参阅“ 配置应用程序以从用户可能分配到的已筛选组组接收组声明值”部分。

  4. 如果无法避免出现组超额,建议使用以下步骤处理令牌中的组声明:

    1. 检查声明 _claim_names ,其中一个值是 。 此声明指示超额。
    2. 如果找到,请调用指定的 _claim_sources 终结点来提取用户的组。
    3. 如果未找到,请查看 用户的组的组 声明。

注意

处理超额需要调用 Microsoft Graph 来读取已登录用户的组成员身份,因此你的应用需要具有 getMemberObjects 函数的 GroupMember.Read.All 权限才能成功执行。

有关 Microsoft Graph 编程的详细信息,请参阅视频 :面向开发人员的 Microsoft Graph 简介。

ConfidentialClientApplication

ConfidentialClientApplication实例在AuthHelper.java文件中创建,如以下示例所示。 此对象有助于创建Microsoft Entra 授权 URL,并有助于交换访问令牌的身份验证令牌。

// getConfidentialClientInstance method
IClientSecret secret = ClientCredentialFactory.createFromSecret(SECRET);
confClientInstance = ConfidentialClientApplication
                      .builder(CLIENT_ID, secret)
                      .authority(AUTHORITY)
                      .build();

以下参数用于实例化:

  • 应用的客户端 ID。
  • 客户端机密,这是机密客户端应用程序的要求。
  • Microsoft Entra ID 颁发机构,其中包括Microsoft Entra 租户 ID。

在此示例中,使用 Config.java 文件中的属性读取器authentication.properties 文件中读取这些值。

分步演练

以下步骤提供应用的功能的演练:

  1. 登录过程的第一步是向Microsoft Entra ID 租户的终结点发送请求 /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:收集用户凭据后,Microsoft Entra 会重定向浏览器以及身份验证代码。 它必须与Azure 门户中Microsoft Entra ID 应用注册中的重定向 URI 匹配。
    • SCOPES范围 是应用程序请求的权限。
      • 通常,这三个范围 openid profile offline_access 足以接收 ID 令牌响应。
      • 可以在 authentication.properties 文件中找到应用请求的范围的完整列表。 可以添加更多范围,例如 User.Read
  2. Microsoft Entra ID 将会向用户显示登录提示。 如果登录尝试成功,则用户的浏览器将重定向到应用的重定向终结点。 对此终结点的有效请求包含 授权代码

  3. 然后,该 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 检查通过,则结果将置于 context -- 实例 IdentityContextData 中,并保存到会话中。 然后,应用程序可以通过需要访问会话的实例IdentityContextAdapterServlet来实例IdentityContextData化会话,如以下代码所示:

    // 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());
    
    // handle groups overage if it has occurred.
    handleGroupsOverage(contextAdapter);
    
  5. 在上一步之后,可以使用 <a0/> 实例调用context.getGroups()来提取组成员身份。

  6. 如果用户是组数过多(超过 200 个)的成员,则调用context.getGroups()可能为空(如果不是呼叫)。handleGroupsOverage() 同时, context.getGroupsOverage() 返回 true,指示发生超额,获取组的完整列表需要调用 Microsoft Graph。 handleGroupsOverage()请参阅AuthHelper.java中的方法,了解此应用程序在超额时如何使用context.setGroups()

保护路由

请参阅 AuthenticationFilter.java ,了解示例应用如何筛选对路由的访问。 在 authentication.properties 文件中,该 app.protect.authenticated 属性包含仅经过身份验证的用户可以访问的逗号分隔路由,如以下示例所示:

# for example, /token_details requires any user to be signed in and does not require special groups claim
app.protect.authenticated=/token_details

逗号分隔规则集中 app.protect.groups 列出的任何路由也是对未经身份验证的经过身份验证的用户的限制,如以下示例所示。 但是,这些路由还包含以空格分隔的组成员身份列表。 只有属于至少一个相应组的用户才能在进行身份验证后访问这些路由。

# define short names for group IDs here for the app. This is useful in the next property (app.protect.groups).
# EXCLUDE the curly braces, they are in this file only as delimiters.
# example:
# app.groups=groupA abcdef-qrstuvw-xyz groupB abcdef-qrstuv-wxyz
app.groups=admin {enter-your-admins-group-id-here}, user {enter-your-users-group-id-here}

# A route and its corresponding group(s) that can view it, <space-separated>; the start of the next route & its group(s) is delimited by a <comma-and-space-separator>
# this says: /admins_only can be accessed by admin group, /regular_user can be accessed by admin group and user group
app.protect.groups=/admin_only admin, /regular_user admin user

作用域

范围告知Microsoft Entra ID 应用程序请求的访问级别。

根据请求的范围,Microsoft Entra ID 在登录时向用户显示同意对话。 如果用户同意一个或多个范围并获取令牌,则 scopes-consented-to 编码为结果 access_token

有关应用程序请求的范围,请参阅 authentication.properties。 默认情况下,应用程序将范围值设置为 GroupMember.Read.All。 如果应用程序需要调用 Graph 以获取用户的组成员身份,则需要此特定Microsoft图形 API 范围。

详细信息

下一步

将 Java WebSphere 应用部署到 Azure 上的传统 WebSphere 虚拟机