Protect a service application by using OpenID Connect (OIDC) Bearer token authentication

使用 Quarkus OpenID Connect (OIDC) 扩展通过 Bearer 令牌验证保护 Jakarta REST 应用程序。载体令牌由 OIDC 和 OAuth 2.0 兼容授权服务器(如 Keycloak)颁发。

Use the Quarkus OpenID Connect (OIDC) extension to secure a Jakarta REST application with Bearer token authentication. The bearer tokens are issued by OIDC and OAuth 2.0 compliant authorization servers, such as Keycloak.

有关 OIDC Bearer 令牌验证的详情,请参阅 Quarkus OpenID Connect (OIDC) Bearer token authentication指南。

For more information about OIDC Bearer token authentication, see the Quarkus OpenID Connect (OIDC) Bearer token authentication guide.

如果您想通过使用 OIDC 授权代码流程验证来保护 Web 应用程序,请参阅 OpenID Connect authorization code flow mechanism for protecting web applications指南。

If you want to protect web applications by using OIDC Authorization Code Flow authentication, see the OpenID Connect authorization code flow mechanism for protecting web applications guide.

Prerequisites

Unresolved directive in security-oidc-bearer-token-authentication-tutorial.adoc - include::{includes}/prerequisites.adoc[]* jq command-line processor tool

Unresolved directive in security-oidc-bearer-token-authentication-tutorial.adoc - include::{includes}/prerequisites.adoc[] * The jq command-line processor tool

Architecture

此示例展示了如何构建一个提供两个端点的简单微服务:

This example shows how you can build a simple microservice that offers two endpoints:

  • /api/users/me

  • /api/admin

这些端点受保护并且仅在客户端发送携带请求的 bearer 令牌时才能访问,该令牌必须有效(例如,签名、到期时间和受众)并且受微服务信任。

These endpoints are protected and can only be accessed if a client sends a bearer token along with the request, which must be valid (for example, signature, expiration, and audience) and trusted by the microservice.

Keycloak 服务器颁发携带令牌,并代表颁发令牌的主体。因为它是一个 OAuth 2.0 授权服务器,所以令牌还引用代表用户执行操作的客户端。

A Keycloak server issues the bearer token and represents the subject for which the token was issued. Because it is an OAuth 2.0 authorization server, the token also references the client acting on the user’s behalf.

任何带有有效令牌的用户都可以访问 /api/users/me 端点。作为响应,它使用从令牌中的信息获取的用户详细信息返回一个 JSON 文档。

Any user with a valid token can access the /api/users/me endpoint. As a response, it returns a JSON document with user details obtained from the information in the token.

/api/admin 端点受 RBAC(基于角色的访问控制)保护,只有具有 admin 角色的用户才能访问。在此端点,使用 @RolesAllowed 注解以声明方式强制执行访问约束。

The /api/admin endpoint is protected with RBAC (Role-Based Access Control), which only users with the admin role can access. At this endpoint, the @RolesAllowed annotation is used to enforce the access constraint declaratively.

Solution

按照下一部分中的说明逐步创建应用程序。你也可以直接转到完整示例。

Follow the instructions in the next sections and create the application step by step. You can also go straight to the completed example.

你可以通过运行命令 git clone {quickstarts-clone-url} 克隆 Git 存储库,或者你可以下载一个 {quickstarts-archive-url}[archive]。

You can clone the Git repository by running the command git clone {quickstarts-clone-url}, or you can download an {quickstarts-archive-url}[archive].

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

The solution is located in the security-openid-connect-quickstart directory.

Create the Maven project

你可以使用 oidc 扩展名创建一个新的 Maven 项目,或者可以将此扩展名添加到现有的 Maven 项目。完成以下命令之一:

You can either create a new Maven project with the oidc extension or you can add the extension to an existing Maven project. Complete one of the following commands:

要创建一个新的 Maven 项目,请使用以下命令:

To create a new Maven project, use the following command:

Unresolved directive in security-oidc-bearer-token-authentication-tutorial.adoc - include::{includes}/devtools/create-app.adoc[]

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

If you already have your Quarkus project configured, you can add the oidc extension to your project by running the following command in your project base directory:

Unresolved directive in security-oidc-bearer-token-authentication-tutorial.adoc - include::{includes}/devtools/extension-add.adoc[]

这会将以下内容添加到构建文件中:

This will add the following to your build file:

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

Write the application

  1. Implement the /api/users/me endpoint as shown in the following example, which is a regular Jakarta REST resource:======

package org.acme.security.openid.connect;

import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

import org.jboss.resteasy.reactive.NoCache;
import io.quarkus.security.identity.SecurityIdentity;

@Path("/api/users")
public class UsersResource {

    @Inject
    SecurityIdentity securityIdentity;

    @GET
    @Path("/me")
    @RolesAllowed("user")
    @NoCache
    public User me() {
        return new User(securityIdentity);
    }

    public static class User {

        private final String userName;

        User(SecurityIdentity securityIdentity) {
            this.userName = securityIdentity.getPrincipal().getName();
        }

        public String getUserName() {
            return userName;
        }
    }
}
  1. Implement the /api/admin endpoint as shown in the following example:======

package org.acme.security.openid.connect;

import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/api/admin")
public class AdminResource {

    @GET
    @RolesAllowed("admin")
    @Produces(MediaType.TEXT_PLAIN)
    public String admin() {
        return "granted";
    }
}

此示例的主要区别在于使用了 @RolesAllowed 注解来验证只有被授予了 admin 角色的用户才能访问此端点。

The main difference in this example is that the @RolesAllowed annotation is used to verify that only users granted the admin role can access the endpoint.

@RequestScoped@ApplicationScoped 上下文中都支持注入 SecurityIdentity

Injection of the SecurityIdentity is supported in both @RequestScoped and @ApplicationScoped contexts.

Configure the application

  • Configure the Quarkus OpenID Connect (OIDC) extension by setting the following configuration properties in the src/main/resources/application.properties file.======

%prod.quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus
quarkus.oidc.client-id=backend-service
quarkus.oidc.credentials.secret=secret

# Tell Dev Services for Keycloak to import the realm file
# This property is not effective when running the application in JVM or native modes

quarkus.keycloak.devservices.realm-path=quarkus-realm.json

其中:

Where:

  • %prod.quarkus.oidc.auth-server-url sets the base URL of the OpenID Connect (OIDC) server. The %prod. profile prefix ensures that Dev Services for Keycloak launches a container when you run the application in development (dev) mode. For more information, see the Run the application in dev mode section.

  • quarkus.oidc.client-id sets a client id that identifies the application.

  • quarkus.oidc.credentials.secret sets the client secret, which is used by the client_secret_basic authentication method.

有关更多信息,请参阅 Quarkus OpenID Connect (OIDC) configuration properties 指南。

For more information, see the Quarkus OpenID Connect (OIDC) configuration properties guide.

== Start and configure the Keycloak server

  1. Put the realm configuration file on the classpath (target/classes directory) so that it gets imported automatically when running in dev mode. You do not need to do this if you have already built a complete solution, in which case, this realm file is added to the classpath during the build.

在开发模式中运行应用程序时不要启动 Keycloak 服务器;Dev Services for Keycloak 将启动容器。要了解详细信息,请参阅 Run the application in dev mode 部分。

Do not start the Keycloak server when you run the application in dev mode; Dev Services for Keycloak will start a container. For more information, see the Run the application in dev mode section.

  1. To start a Keycloak server, you can use Docker to run the following command:======

docker run --name keycloak -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -p 8180:8080 quay.io/keycloak/keycloak:{keycloak.version} start-dev
  • Where the keycloak.version is set to version 25.0.2 or later.

    1. You can access your Keycloak server at localhost:8180.

    2. To access the Keycloak Administration console, log in as the admin user by using the following login credentials:

  • Username: admin

  • Password: admin

    1. Import the realm configuration file from the upstream community repository to create a new realm.

要了解详细信息,请参阅关于 creating and configuring a new realm 的 Keycloak 文档。

For more information, see the Keycloak documentation about creating and configuring a new realm.

如果要使用 Keycloak 管理客户端通过应用程序配置服务器,则需要包含 quarkus-keycloak-admin-rest-client 或(如果应用程序使用 quarkus-rest-clientquarkus-keycloak-admin-resteasy-client 扩展。要了解详细信息,请参阅 Quarkus Keycloak Admin Client 指南。

If you want to use the Keycloak Admin Client to configure your server from your application, you need to include either the quarkus-keycloak-admin-rest-client or the quarkus-keycloak-admin-resteasy-client (if the application uses quarkus-rest-client) extension. For more information, see the Quarkus Keycloak Admin Client guide.

Run the application in dev mode

  1. To run the application in dev mode, run the following commands:====== Unresolved directive in security-oidc-bearer-token-authentication-tutorial.adoc - include::{includes}/devtools/dev.adoc[]

  • Dev Services for Keycloak will start a Keycloak container and import a quarkus-realm.json.

    1. Open a Dev UI, which you can find at /q/dev-ui. Then, in an OpenID Connect card, click the Keycloak provider link .

    2. When prompted to log in to a Single Page Application provided by OpenID Connect Dev UI, do the following steps:

  • Log in as alice (password: alice), who has a user role.

    • Accessing /api/admin returns a 403 status code.

    • Accessing /api/users/me returns a 200 status code.

  • Log out and log in again as admin (password: admin), who has both admin and user roles.

    • Accessing /api/admin returns a 200 status code.

    • Accessing /api/users/me returns a 200 status code.

== Run the Application in JVM mode

在完成 dev 模式后,可以将应用程序作为标准 Java 应用程序运行。

When you are done with dev mode, you can run the application as a standard Java application.

  1. Compile the application:====== Unresolved directive in security-oidc-bearer-token-authentication-tutorial.adoc - include::{includes}/devtools/build.adoc[]

  1. Run the application:======

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

== Run the application in native mode

可以将同一 demo 原样编译为本地模式,而无需任何修改。这意味着不再需要在生产环境上安装 JVM。运行时技术包含在生成的二进制文件中,并经过优化,可以以所需的最小资源来运行。

You can compile this same demo as-is into native mode without any modifications. This implies that you no longer need to install a JVM on your production environment. The runtime technology is included in the produced binary and optimized to run with minimal resources required.

编译花费的时间稍微长一点,所以默认情况下会禁用此步骤。

Compilation takes a bit longer, so this step is disabled by default.

  1. Build your application again by enabling the native profile:====== Unresolved directive in security-oidc-bearer-token-authentication-tutorial.adoc - include::{includes}/devtools/build-native.adoc[]

  1. After waiting a little while, you run the following binary directly:======

./target/security-openid-connect-quickstart-1.0.0-SNAPSHOT-runner

== Test the application

有关在开发模式下测试应用程序的信息,请参阅前面的 Run the application in dev mode 部分。

For information about testing your application in dev mode, see the preceding Run the application in dev mode section.

您可以使用`curl`测试在 JVM 或本地模式中启动的应用程序。

You can test the application launched in JVM or native modes with curl.

  • Because the application uses Bearer token authentication, you must first obtain an access token from the Keycloak server to access the application resources:

export access_token=$(\
    curl --insecure -X POST http://localhost:8180/realms/quarkus/protocol/openid-connect/token \
    --user backend-service:secret \
    -H 'content-type: application/x-www-form-urlencoded' \
    -d 'username=alice&password=alice&grant_type=password' | jq --raw-output '.access_token' \
 )

前面的示例为用户 alice 获取了一个访问令牌。

The preceding example obtains an access token for the user alice.

curl -v -X GET \
  http://localhost:8080/api/users/me \
  -H "Authorization: Bearer "$access_token
  • Only users with the admin role can access the http://localhost:8080/api/admin endpoint. If you try to access this endpoint with the previously-issued access token, you get a 403 response from the server.

curl -v -X GET \
   http://localhost:8080/api/admin \
   -H "Authorization: Bearer "$access_token
  • To access the admin endpoint, obtain a token for the admin user:

export access_token=$(\
    curl --insecure -X POST http://localhost:8180/realms/quarkus/protocol/openid-connect/token \
    --user backend-service:secret \
    -H 'content-type: application/x-www-form-urlencoded' \
    -d 'username=admin&password=admin&grant_type=password' | jq --raw-output '.access_token' \
 )

有关编写依赖于 Dev Services for Keycloak 的集成测试的信息,请参阅“OpenID Connect (OIDC) 持有者令牌身份验证”指南的 Dev Services for Keycloak 部分。

For information about writing integration tests that depend on Dev Services for Keycloak, see the Dev Services for Keycloak section of the "OpenID Connect (OIDC) Bearer token authentication" guide.

== References