Protect a web application by using OpenID Connect (OIDC) authorization code flow

使用 Quarkus OIDC 扩展了解如何通过使用 Quarkus OIDC 的授权代码流程机制保护应用程序 HTTP 端点,从而提供强大的身份验证和授权。 有关更多信息,请参见 OIDC code flow mechanism for protecting web applications。 要了解如何将众所周知的社交提供商(例如 Apple、Facebook、GitHub、Google、Mastodon、Microsoft、Spotify、Twitch 和 X(以前的 Twitter)与 Quarkus OIDC 配合使用,请参见 Configuring well-known OpenID Connect providers。另外请参见 Authentication mechanisms in Quarkus。 如果您想通过使用 OIDC Bearer 令牌身份验证来保护您的服务应用程序,请参见 OIDC Bearer token authentication

Prerequisites

如要完成本指南,您需要:

  • Roughly 15 minutes

  • An IDE

  • 安装了 JDK 17+,已正确配置 JAVA_HOME

  • Apache Maven ${proposed-maven-version}

  • 如果你想使用 Quarkus CLI, 则可以选择使用

  • 如果你想构建一个本机可执行文件(或如果你使用本机容器构建,则使用 Docker),则可以选择安装 Mandrel 或 GraalVM 以及 configured appropriately

Architecture

在此示例中,我们构建了一个具有单个页面的简单 web 应用程序:

  • /index.html

此页面受保护,并且只有经过身份验证的用户才能访问。

Solution

按照以下各节中的说明,按步骤创建应用。或者您可以直接转到已完成示例。

通过运行 git clone $${quickstarts-base-url}.git 命令克隆 Git 存储库。或者下载一个 $${quickstarts-base-url}/archive/main.zip[存档]。

该解决方案位于 security-openid-connect-web-authentication-quickstart directory

Create the Maven project

首先,我们需要一个新项目。通过运行以下命令创建一个新项目:

CLI
quarkus create app {create-app-group-id}:{create-app-artifact-id} \
    --no-code
cd {create-app-artifact-id}

要创建一个 Gradle 项目,添加 --gradle--gradle-kotlin-dsl 选项。 有关如何安装和使用 Quarkus CLI 的详细信息,请参见 Quarkus CLI 指南。

Maven
mvn {quarkus-platform-groupid}:quarkus-maven-plugin:{quarkus-version}:create \
    -DprojectGroupId={create-app-group-id} \
    -DprojectArtifactId={create-app-artifact-id} \
    -DnoCode
cd {create-app-artifact-id}

要创建一个 Gradle 项目,添加 -DbuildTool=gradle-DbuildTool=gradle-kotlin-dsl 选项。

适用于 Windows 用户:

  • 如果使用 cmd,(不要使用反斜杠 \ ,并将所有内容放在同一行上)

  • 如果使用 Powershell,将 -D 参数用双引号引起来,例如 "-DprojectArtifactId={create-app-artifact-id}"

如果你的 Quarkus 项目已经配置好,你可以通过在项目基础目录中运行以下命令将 oidc 扩展名添加到项目:

CLI
quarkus extension add {add-extension-extensions}
Maven
./mvnw quarkus:add-extension -Dextensions='{add-extension-extensions}'
Gradle
./gradlew addExtension --extensions='{add-extension-extensions}'

这会将以下依赖关系添加到您的构建文件中:

pom.xml
<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-oidc</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-oidc")

Write the application

让我们编写一个简单的 Jakarta REST 资源,它已注入授权代码授予响应中返回的所有令牌:

package org.acme.security.openid.connect.web.authentication;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;

import org.eclipse.microprofile.jwt.Claims;
import org.eclipse.microprofile.jwt.JsonWebToken;

import io.quarkus.oidc.IdToken;
import io.quarkus.oidc.RefreshToken;

@Path("/tokens")
public class TokenResource {

   /**
    * Injection point for the ID token issued by the OpenID Connect provider
    */
   @Inject
   @IdToken
   JsonWebToken idToken;

   /**
    * Injection point for the access token issued by the OpenID Connect provider
    */
   @Inject
   JsonWebToken accessToken;

   /**
    * Injection point for the refresh token issued by the OpenID Connect provider
    */
   @Inject
   RefreshToken refreshToken;

   /**
    * Returns the tokens available to the application.
    * This endpoint exists only for demonstration purposes.
    * Do not expose these tokens in a real application.
    *
    * @return an HTML page containing the tokens available to the application.
    */
   @GET
   @Produces("text/html")
   public String getTokens() {
       StringBuilder response = new StringBuilder().append("<html>")
               .append("<body>")
               .append("<ul>");


       Object userName = this.idToken.getClaim(Claims.preferred_username);

       if (userName != null) {
           response.append("<li>username: ").append(userName.toString()).append("</li>");
       }

       Object scopes = this.accessToken.getClaim("scope");

       if (scopes != null) {
           response.append("<li>scopes: ").append(scopes.toString()).append("</li>");
       }

       response.append("<li>refresh_token: ").append(refreshToken.getToken() != null).append("</li>");

       return response.append("</ul>").append("</body>").append("</html>").toString();
   }
}

此端点已注入 ID、访问和刷新令牌。它从 ID 令牌返回一个 preferred_username 声明,从访问令牌返回一个 scope 声明,并返回一个刷新令牌可用性状态。

只有当端点需要使用 ID 令牌与当前经过身份验证的用户进行交互或使用访问令牌代表此用户访问下游服务时,才需要注入令牌。

有关更多信息,请参阅参考指南的 Access ID and Access Tokens 部分。

Configure the application

OIDC 扩展允许您使用 src/main/resources 目录中的 application.properties 文件定义配置。

quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus
quarkus.oidc.client-id=frontend
quarkus.oidc.credentials.secret=secret
quarkus.oidc.application-type=web-app
quarkus.http.auth.permission.authenticated.paths=/*
quarkus.http.auth.permission.authenticated.policy=authenticated

这是启用应用程序身份验证时可以执行的最简单的配置。

quarkus.oidc.client-id 属性引用 OIDC 提供者发出的 client_id,而 quarkus.oidc.credentials.secret 属性设置客户端密钥。

quarkus.oidc.application-type 属性设置为 web-app,告诉 Quarkus 您希望启用 OIDC 授权代码流程,以便将您的用户重定向到 OIDC 提供者以进行身份验证。

最后,将 quarkus.http.auth.permission.authenticated 权限设置为告知 Quarkus 您想要保护的路径。在这种情况下,所有路径都受到策略的保护,该策略确保只有 authenticated 用户才能访问它们。有关更多信息,请参阅 Security Authorization Guide

当您不使用 quarkus.oidc.credentials.secret 配置客户端密钥时,建议配置 quarkus.oidc.token-state-manager.encryption-secretquarkus.oidc.token-state-manager.encryption-secret 启用默认令牌状态管理器,以在浏览器 cookie 中加密用户令牌。如果未定义此密钥,并且未配置 quarkus.oidc.credentials.secret 回调,则 Quarkus 将使用随机密钥。随机密钥会导致在应用程序重新启动或在您的应用程序的多实例环境中,现有登录被视为无效。或者,还可以通过将 quarkus.oidc.token-state-manager.encryption-required 设置为 false 来禁用加密。但是,您应该只在开发环境中禁用密钥加密。 建议加密密钥长度为 32 个字符。例如,quarkus.oidc.token-state-manager.encryption-secret=AyM1SysPpbyDfgZld3umj1qzKObwVMk

Start and configure the Keycloak server

要启动 Keycloak 服务器,请使用 Docker 并运行以下命令:

docker run --name keycloak -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -p 8180:8080 quay.io/keycloak/keycloak:{keycloak.version} start-dev

在其中 keycloak.version 设置为 25.0.2 或更高版本。

您可以在 localhost:8180 访问您的 Keycloak 服务器。

要访问 Keycloak 管理控制台,请使用 admin 用户登录。用户名和密码均为 admin

要创建新的领域,导入 realm configuration file。有关详细信息,请参阅 Keycloak 文档,了解如何 create and configure a new realm

Run the application in dev and JVM modes

要以 dev 模式运行应用程序,请使用:

CLI
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

在 dev 模式下探索应用程序后,您可以将其作为标准 Java 应用程序运行。

首先,对其进行编译:

CLI
quarkus build
Maven
./mvnw install
Gradle
./gradlew build

然后,运行它:

java -jar target/quarkus-app/quarkus-run.jar

Run the application in Native mode

可以将此相同演示编译为本机代码。无需任何修改。

这意味着您无需在生产环境中安装 JVM,因为运行时技术包含在生成的可执行文件中,并经过优化,可使用最少资源运行。

编译需要更长时间,因此默认情况下此步骤已关闭。您可以通过启用本机构建来重新构建:

CLI
quarkus build --native
Maven
./mvnw install -Dnative
Gradle
./gradlew build -Dquarkus.native.enabled=true

一段时间后,您可以直接运行此二进制文件:

./target/security-openid-connect-web-authentication-quickstart-runner

Test the application

要测试应用程序,请打开浏览器并访问以下 URL:

如果所有内容按预期工作,您将被重定向到 Keycloak 服务器进行身份验证。

要对该应用程序进行身份验证,请在 Keycloak 登录页面输入以下凭据:

  • Username: alice

  • Password: alice

单击 Login 按钮后,您将被重定向回该应用程序,并且将创建会话 Cookie。

此演示的会话有效期很短,并且在每次刷新页面时,系统都会要求您重新进行身份验证。有关如何增加会话超时时间的详细信息,请参阅 Keycloak session timeout 文档。例如,如果您在开发模式中使用 Dev Services for Keycloak,则可以从开发 UI 中通过单击 Keycloak Admin 链接直接访问 Keycloak Admin 控制台:

dev ui oidc keycloak card

有关编写依赖于 Dev Services for Keycloak 的集成测试的详细信息,请参阅 Dev Services for Keycloak 部分。

Summary

您已经了解如何设置和使用 OIDC 授权代码流机制来保护和测试应用程序 HTTP 端点。完成本教程后,浏览 OIDC Bearer token authenticationother authentication mechanisms