使用角色和角色声明保护 Java WebLogic 应用

本文演示了一个 Java WebLogic 应用,它使用 OpenID Connect 登录用户,并使用 Microsoft Entra ID 应用程序角色(应用角色)来进行授权。

此应用程序使用 Microsoft Entra ID 的应用程序角色和角色声明功能来实现基于角色的访问控制 (RBAC)。 另一种方法是使用 Microsoft Entra ID 组和组声明。 Microsoft Entra ID 组和应用程序角色并不相互排斥。 可以同时使用它们来提供精细的访问控制。

还可以将 RBAC 与应用程序角色和角色声明结合使用,以安全地执行授权策略。

有关涵盖此场景和本示例的视频,请观看使用应用角色、安全组、范围和目录角色在应用程序中实现授权

有关协议在这种情况和其他情况下如何工作的详细信息,请参阅身份验证与授权

此应用程序使用适用于 Java 的 MSAL (MSAL4J) 登录用户,并从 Microsoft Entra ID 获取 ID 令牌

此示例首先使用适用于 Java 的 MSAL (MSAL4J) 登录用户。 在主页上会显示一个选项,以供用户查看其 ID 令牌中的声明。 此应用程序还能让用户根据所分配的应用角色来查看特权管理员页面或普通用户页面。 其目的是提供一个示例,说明如何在应用程序中根据用户所属的角色来限制其对某些功能或页面的访问。

这种授权是通过 RBAC 实现的。 通过 RBAC,管理员会将权限授予角色,而不是单个用户或组。 然后,管理员再将角色分配给不同的用户和组,以便控制用户对某些内容和功能的访问。

此示例应用程序定义了以下两个应用程序角色

  • PrivilegedAdmin:授权访问“仅限管理员”和“常规用户”页面。
  • RegularUser:授权访问“常规用户”页面。

这些应用程序角色在 Azure 门户的应用程序注册清单中定义。 当用户登录到应用程序时,Microsoft Entra ID 会以角色成员身份的形式,为单独授予用户的每个角色发出角色声明。

可以通过 Azure 门户为角色分配用户和组。

注意

如果 https://login.microsoftonline.com/common/ 终结点被用作用户登录的授权,则租户中的来宾用户不存在角色声明。 需要将用户登录到租户终结点(如 https://login.microsoftonline.com/tenantid)。

先决条件

建议

设置示例

以下各部分将介绍如何设置示例应用程序。

克隆或下载示例存储库

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

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

或者,导航到 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-roles)

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

然后,按照以下步骤完成注册:

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

  2. 选择新注册

  3. 在出现的“注册应用程序”页面中,输入以下应用的注册信息:

    • 在“名称”部分中,输入一个有意义的应用名称,以便向应用用户显示,例如 java-servlet-webapp-roles

    • 在“支持的帐户类型”下,选择以下选项之一:

      • 如果生成的应用程序仅供租户中的用户使用,即单租户应用程序,请选择“仅限此组织目录中的帐户”。
    • 在“重定向 URI”部分中,在组合框中选择“Web”并输入以下重定向 URI:http://localhost:8080/msal4j-servlet-roles/auth/redirect

  4. 选择“注册”以创建应用程序。

  5. 在应用的注册页面上,找到并复制“应用程序(客户端) ID”值,以供稍后使用。 可以在应用程序的一个或多个配置文件中使用此值。

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

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

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

  9. 键入描述,例如“应用机密”。

  10. 从可用期限中选择一个:“1 年内”、“2 年内”或“永不过期”。

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

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

定义应用程序角色

要定义应用角色,请执行以下步骤:

  1. 还是在同一个应用注册中,在导航窗格中选择“应用角色”。

  2. 选择“创建应用角色”,然后输入或选择以下值:

    • 在“显示名称”中,请输入合适的名称 - 例如 PrivilegedAdmin
    • 在“允许的成员类型”中,请选择“用户”。
    • 在“值”中,请输入 PrivilegedAdmin
    • 在“说明”中,请输入可查看管理员页面的 PrivilegedAdmins
  3. 选择“创建应用角色”,然后输入或选择以下值:

    • 在“显示名称”中,请输入合适的名称 - 例如 RegularUser
    • 在“允许的成员类型”中,请选择“用户”。
    • 在“值”中,请输入 RegularUser
    • 在“说明”中,请输入可以查看用户页的 RegularUsers
  4. 选择“应用”以保存所做的更改。

将用户分配给应用程序角色

要将用户添加到前面定义的应用角色中,请遵循此处的准则:将用户和组分配到角色。


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

按照以下步骤来配置应用:

注意

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

  1. 在 IDE 中打开项目。

  2. 打开 authentication.properties 文件。

  3. 找到字符串 {enter-your-tenant-id-here}。 使用 Microsoft Entra ID 租户 ID 替换现有值。

  4. 找到字符串 {enter-your-client-id-here},然后用从 Azure 门户复制的应用程序 ID 或 java-servlet-webapp-call-graph 应用程序的 clientId 来替换现有值。

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

  6. 找到 app.roles 属性并确保值设置为 app.roles=admin PrivilegedAdmin, user RegularUser,或者用特定角色的名称代替。

生成示例

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

mvn clean package

此命令会生成一个 .war 文件,它可以在各种应用服务器上运行。

部署示例

这些说明假定安装了 WebLogic 并设置了某个服务器域。

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

  1. 在示例中,找到在其中配置了客户端 ID、租户、重定向 URL 等的 application.propertiesauthentication.properties 文件。

  2. 在此文件中,将对 localhost:8080localhost:8443 的引用更改为 WebLogic 运行的 URL 和端口,而默认情况下它应该是 localhost:7001

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

使用以下步骤通过 Web 控制台将样本部署到 WebLogic:

  1. 使用 DOMAIN_NAME\bin\startWebLogic.cmd 启动 WebLogic 服务器。

  2. 在浏览器中导航到 WebLogic 网络控制台,网址为 http://localhost:7001/console

  3. 转到域结构>部署,选择“安装”,选择“上传文件”,然后找到通过使用 Maven 生成的 .war 文件。

  4. 选择“作为应用程序安装此部署”,选择“下一步”,选择“完成”,然后选择“保存”。

  5. 大部分的默认设置都可以保留,但应用程序名称除外,应将其与在示例配置或 Azure 应用程序注册中设置的重定向 URI 一致。 也就是说,如果重定向 URI 是 http://localhost:7001/msal4j-servlet-auth,则应将应用程序命名为 msal4j-servlet-auth

  6. 返回到域结构>部署,然后启动应用程序。

  7. 在应用程序启动后,导航至 http://localhost:7001/<application-name>/,这样就能访问应用程序了。

探索示例

按照以下步骤来探索示例:

  1. 注意屏幕中央显示的登录或退出状态。
  2. 选择角落里的上下文相关按钮。 首次运行应用时,此按钮显示“登录”。
  3. 在下一页上,按照说明使用 Microsoft Entra ID 租户中的帐户登录。
  4. 在同意屏幕上,请注意正在请求的范围。
  5. 请注意,上下文相关按钮现在显示“注销”并显示你的用户名。
  6. 选择“ID 令牌详细信息”以查看一些 ID 令牌的解码声明。
  7. 选择“仅限管理员”以查看 /admin_only 页面。 只有具有应用角色 PrivilegedAdmin 的用户才能查看此页面。 否则,系统将显示授权失败消息。
  8. 选择“常规用户”以查看 /regular_user 页面。 只有拥有应用角色 RegularUserPrivilegedAdmin 的用户才能查看此页面。 否则,系统将显示授权失败消息。
  9. 使用角落里的按钮注销。

关于代码

此示例使用 MSAL for Java (MSAL4J) 登录用户并获取可能包含角色声明的 ID 令牌。 根据存在的角色声明,登录的用户可以不访问、访问一个或同时访问受保护页面 Admins OnlyRegular Users

如果要复制此示例的行为,则可以复制 src/main/java/com/microsoft/azuresamples/msal4j 文件夹中的 pom.xml 文件以及 helpersauthservlets 文件夹的内容。 还需要使用 authentication.properties 文件。 这些类和文件包含通用代码,可用于各种应用程序。 也可以复制示例的其他部分,但其他类和文件是专门为实现本示例的目标而生成的。

目录

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

文件/文件夹 说明
src/main/java/com/microsoft/azuresamples/msal4j/roles/ 此目录包含定义应用的后端业务逻辑的类。
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 令牌中的角色声明

如以下示例所示,令牌的角色声明包括为已登录用户分配的角色名称:

{
  ...
  "roles": [
    "Role1",
    "Role2",]
  ...
}

ConfidentialClientApplication

如下例所示,在 AuthHelper.java 文件中创建了一个 ConfidentialClientApplication 实例。 此对象有助于创建 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,即用户登录的 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 ID 在收集用户凭据后会将浏览器以及授权码重定向到哪里。 它必须与 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 的一个实例,并会被保存到会话中。 然后,当应用程序需要访问 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());
    

保护路由

有关示例应用如何筛选路由访问的信息,请参阅 AuthenticationFilter.java。 在 authentication.properties 文件中,app.protect.authenticated 属性包含只有通过身份验证的用户才能访问的以逗号分隔的路由,如下例所示:

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

如以下示例所示,app.protect.roles 下以逗号分隔的规则集中列出的任何路由也禁止非身份验证用户使用。 但是,这些路由还包含一个空格分隔的应用角色成员身份列表:只有至少拥有一个相应角色的用户才能在通过身份验证后访问这些路由。

# local short names for app roles - for example, sets admin to mean PrivilegedAdmin (useful for long rule sets defined in the next key, app.protect.roles)
app.roles=admin PrivilegedAdmin, user RegularUser

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

作用域

范围表明了 Microsoft Entra ID 应用程序请求的访问级别。

根据请求的范围,Microsoft Entra ID 会在登录时向用户显示同意对话。 如果用户同意一个或多个范围并获得了一个令牌,则同意的范围将被编码到生成的 access_token 中。

有关应用程序请求的范围,请参阅 authentication.properties。 这三个范围由 MSAL 请求,默认情况下由 Microsoft Entra ID 提供。

详细信息

下一步

将 Java WebLogic 应用部署到 Azure 虚拟机上的 WebLogic 中