Authentication mechanisms in Quarkus

Overview of supported authentication mechanisms

一些支持的身份验证机制内置于 Quarkus 中,而另一些则需要您添加扩展。所有这些机制都在以下部分中进行了详细说明:

下表将特定身份验证要求映射到您可以在 Quarkus 中使用的支持机制:

Table 1. Authentication requirements and mechanisms
Authentication requirement Authentication mechanism

Username and password

Basic, Form-based authentication

Bearer access token

OIDC Bearer token authenticationJWTOAuth2

Single sign-on (SSO)

OIDC Code Flow, Form-based authentication

Client certificate

Mutual TLS authentication

WebAuthn

WebAuthn

Kerberos ticket

Kerberos

有关更多信息,请参阅以下 Token authentication mechanism comparison 表。

Built-in authentication mechanisms

Quarkus Security 提供以下内置的身份验证支持:

Basic authentication

您可以使用内置的 HTTP 基本身份验证机制保护您的 Quarkus 应用程序端点。有关更多信息,请参阅以下文档:

Form-based authentication

Quarkus 提供表单认证,其工作方式与传统的 Servlet 表单认证类似。与传统的表单认证不同,经过身份验证的用户不会存储在 HTTP 会话中,因为 Quarkus 不支持集群 HTTP 会话。相反,身份验证信息存储在加密 cookie 中,所有共享相同加密密钥的集群成员都可以读取该 cookie。

要应用加密,请添加 quarkus.http.auth.session.encryption-key 属性,并确保您设置的值的长度至少为 16 个字符。加密密钥使用 SHA-256 进行哈希。产生的提取物用作 cookie 值的 AES-256 加密的密钥。cookie 包含到期时间作为加密值的一部分,因此集群中的所有节点的时钟都必须同步。在一分钟的间隔内,如果会话正在使用,则会使用更新的到期时间生成新的 cookie。

要开始进行表单认证,你应该拥有类似于 Enable Basic authentication 中所述的设置,并且属性 quarkus.http.auth.form.enabled 必须设为 true

使用基于表单的认证,简单的 application.properties 可能会看起来类似于此:

quarkus.http.auth.form.enabled=true

quarkus.http.auth.form.login-page=login.html
quarkus.http.auth.form.landing-page=hello
quarkus.http.auth.form.error-page=

# Define testing user
quarkus.security.users.embedded.enabled=true
quarkus.security.users.embedded.plain-text=true
quarkus.security.users.embedded.users.alice=alice
quarkus.security.users.embedded.roles.alice=user

在该 application.properties 文件中配置用户名、密钥和角色仅适用于测试场景。为了保护生产应用程序,使用数据库或 LDAP 来存储此信息至关重要。有关详细信息,你可以查看 Quarkus Security with Jakarta Persistence 或在 Enable Basic authentication 中提到的其他内容。

并且应用程序登录页面将包含类似于此的 HTML 表单:

<form action="/j_security_check" method="post">
    <label>Username</label>
    <input type="text" placeholder="Username" name="j_username" required>
    <label>Password</label>
    <input type="password" placeholder="Password" name="j_password" required>
    <button type="submit">Login</button>
</form>

对于单页应用 (SPA),你通常可以通过移除默认页面路径来避免重定向,如以下示例所示:

# do not redirect, respond with HTTP 200 OK
quarkus.http.auth.form.landing-page=

# do not redirect, respond with HTTP 401 Unauthorized
quarkus.http.auth.form.login-page=
quarkus.http.auth.form.error-page=

# HttpOnly must be false if you want to log out on the client; it can be true if logging out from the server
quarkus.http.auth.form.http-only-cookie=false

现在你已经为 SPA 禁用重定向,你必须从你的客户端以编程方式登录和注销。以下是用于登录到 j_security_check 端点和通过销毁 Cookie 来注销应用程序的 JavaScript 方法示例。

const login = () => {
    // Create an object to represent the form data
    const formData = new URLSearchParams();
    formData.append("j_username", username);
    formData.append("j_password", password);

    // Make an HTTP POST request using fetch against j_security_check endpoint
    fetch("j_security_check", {
        method: "POST",
        body: formData,
        headers: {
            "Content-Type": "application/x-www-form-urlencoded",
        },
    })
    .then((response) => {
        if (response.status === 200) {
            // Authentication was successful
            console.log("Authentication successful");
        } else {
            // Authentication failed
            console.error("Invalid credentials");
        }
    })
    .catch((error) => {
        console.error(error);
    });
};

要从客户端注销 SPA,必须将 Cookie 设置为 quarkus.http.auth.form.http-only-cookie=false,以便你可以销毁 Cookie,并且可能重定向回你的主页。

const logout= () => {
    // delete the credential cookie, essentially killing the session
    const removeCookie = `quarkus-credential=; Max-Age=0;path=/`;
    document.cookie = removeCookie;

    // perform post-logout actions here, such as redirecting back to your login page
};

要从服务器注销 SPA,可以将 Cookie 设置为 quarkus.http.auth.form.http-only-cookie=true,并使用此示例代码来销毁 Cookie。

@ConfigProperty(name = "quarkus.http.auth.form.cookie-name")
String cookieName;

@Inject
CurrentIdentityAssociation identity;

@POST
public Response logout() {
    if (identity.getIdentity().isAnonymous()) {
        throw new UnauthorizedException("Not authenticated");
    }
    final NewCookie removeCookie = new NewCookie.Builder(cookieName)
            .maxAge(0)
            .expiry(Date.from(Instant.EPOCH))
            .path("/")
            .build();
    return Response.noContent().cookie(removeCookie).build();
}

以下属性可用于配置基于表单的认证:

Unresolved include directive in modules/ROOT/pages/security-authentication-mechanisms.adoc - include::../../../target/quarkus-generated-doc/config/quarkus-vertx-http_quarkus.http.auth.adoc[]

Mutual TLS authentication

Quarkus 提供了双向 TLS (mTLS) 认证,以便你可以基于用户的 X.509 证书来认证用户。

要使用此认证方法,你必须首先为你的应用程序启用 SSL/TLS。有关详细信息,请参阅 Quarkus“HTTP 参考”指南的 Supporting secure connections with SSL/TLS 部分。

在你的应用程序接受安全连接后,下一步是使用包含你的应用程序信任的所有证书的文件名来配置 quarkus.http.ssl.certificate.trust-store-file 属性。指定的文件还包括有关你的应用程序如何要求证书的信息,当客户端(比如浏览器或其他服务)尝试访问其保护的资源之一时。

quarkus.http.ssl.certificate.key-store-file=server-keystore.jks            1
quarkus.http.ssl.certificate.key-store-password=the_key_store_secret
quarkus.http.ssl.certificate.trust-store-file=server-truststore.jks        2
quarkus.http.ssl.certificate.trust-store-password=the_trust_store_secret
quarkus.http.ssl.client-auth=required                                      3
quarkus.http.auth.permission.default.paths=/*                              4
quarkus.http.auth.permission.default.policy=authenticated
quarkus.http.insecure-requests=disabled                                    5
1 存储服务器私钥的密钥库。
2 加载受信任证书的信任库。
3 当值设为 required 时,服务器会要求客户端证书。将值设为 REQUEST 以允许服务器接受不带证书的请求。当支持除了 mTLS 之外的认证方法时,此设置很有用。
4 定义一个只有经过认证的用户才应访问你的应用程序中的资源的策略。
5 你可以明确地禁用普通 HTTP 协议,从而要求所有请求都使用 HTTPS。当你将 quarkus.http.ssl.client-auth 设置为 required 时,系统会自动将 quarkus.http.insecure-requests 设置为 disabled

当传入请求与信任库中的有效证书匹配时,你的应用程序可以通过注入 SecurityIdentity 如下所示来获取主题:

Obtaining the subject
@Inject
SecurityIdentity identity;

@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
    return String.format("Hello, %s", identity.getPrincipal().getName());
}

你也可以使用以下示例中概述的代码来获取证书:

Obtaining the certificate
import java.security.cert.X509Certificate;
import io.quarkus.security.credential.CertificateCredential;

CertificateCredential credential = identity.getCredential(CertificateCredential.class);
X509Certificate certificate = credential.getCertificate();

Mapping certificate attributes to roles

客户端证书中的信息可用于向 Quarkus 添加角色 SecurityIdentity.

在检查客户端证书的公用名称 (CN) 属性后,你可以向 SecurityIdentity 添加新角色。添加新角色的最简单方法是使用证书属性到角色映射功能。

例如,你可以按如下方式更新 Mutual TLS authentication 简介部分中所示的属性:

quarkus.http.ssl.certificate.key-store-file=server-keystore.jks
quarkus.http.ssl.certificate.key-store-password=the_key_store_secret
quarkus.http.ssl.certificate.trust-store-file=server-truststore.jks
quarkus.http.ssl.certificate.trust-store-password=the_trust_store_secret
quarkus.http.ssl.client-auth=required
quarkus.http.insecure-requests=disabled

quarkus.http.auth.certificate-role-properties=cert-role-mappings.properties 1

quarkus.http.auth.permission.certauthenticated.paths=/*   2
quarkus.http.auth.permission.certauthenticated.policy=role-policy-cert 2
quarkus.http.auth.policy.role-policy-cert.roles-allowed=user,admin     2
1 cert-role-mappings.properties 类路径资源包含从证书 CN 值到角色(例如 CN=roleCN=role1,role2 等)的映射。假设它包含三个条目:alice=user,adminbob=userjdoe=tester
2 使用 HTTP 安全策略来要求 SecurityIdentity 必须在请求被授权时带有 useradmin 角色。

鉴于前面提到的配置,如果客户端证书的 CN 属性等于 alicebob,则会授权该请求,如果等于 jdoe,则会禁止该请求。

Using certificate attributes to augment SecurityIdentity

如果你认为自动 Mapping certificate attributes to roles 选项不合适,你可以随时注册 SecurityIdentityAugmentor。自定义 SecurityIdentityAugmentor 可以检查不同客户端证书属性的值并相应地扩充 SecurityIdentity

有关自定义 SecurityIdentity 的详细信息,请参阅 Quarkus“安全提示和技巧”指南中的 Security identity customization 部分。

Other supported authentication mechanisms

Quarkus 安全性还通过扩展程序支持以下认证机制:

WebAuthn authentication

WebAuthn 是一种取代密码的认证机制。在编写用于注册新用户或让用户登录的服务时,你可以使用 WebAuthn 替代密码,而无需询问密码。有关详细信息,请参阅 Secure a Quarkus application by using the WebAuthn authentication mechanism 指南。

OpenID Connect authentication

OpenID Connect (OIDC) 是一个在 OAuth 2.0 协议基础上工作的身份层。OIDC 使得客户端应用程序能够基于 OIDC 提供程序执行的认证来验证用户的身份,并检索该用户的基本信息。

Quarkus quarkus-oidc 扩展提供了一个支持承载令牌和鉴权码流程认证机制的响应式、可互操作且支持多租户的 OIDC 适配器。承载令牌认证机制从 HTTP Authorization 标头中提取令牌。

鉴权码流程机制将用户重定向到 OIDC 提供程序以认证用户的身份。在将用户重定向回 Quarkus 后,该机制通过交换授予 ID、访问和刷新令牌的提供的代码来完成认证流程。

你可以使用可刷新的 JSON Web 密钥 (JWK) 集验证 ID 和访问 JSON Web 令牌 (JWT) 令牌,或远程验证它们。但是,不透明的(也称为二进制令牌)只能远程验证。

使用 Quarkus OIDC 扩展时,承载令牌和鉴权码流程认证机制都会使用 SmallRye JWT authentication 来表示 JWT 令牌,如 MicroProfile JWT org.eclipse.microprofile.jwt.JsonWebToken.

Additional Quarkus resources for OIDC authentication

有关可用于保护 Quarkus 应用程序的 OIDC 认证和授权方法的详细信息,请参阅以下资源:

OIDC topic Quarkus information resource

Bearer token authentication mechanism

OIDC Bearer token authentication

鉴权码流程认证机制

OpenID Connect (OIDC) Authorization Code Flow mechanism

OIDC 和 SAML 标识代理

OpenID Connect (OIDC) Authorization Code Flow and SAML Identity broker

可支持 Bearer 令牌认证或授权码流程机制的多租户

Using OpenID Connect (OIDC) multi-tenancy

使用常用的 OpenID Connect 提供程序保护 Quarkus

Configuring well-known OpenID Connect providers

使用 Keycloak 集中授权

Using OpenID Connect (OIDC) and Keycloak to centralize authorization

Configuring Keycloak programmatically

Using the Keycloak admin client

要在运行时启用 Quarkus OIDC 扩展,请在构建时设置 quarkus.oidc.tenant-enabled=false。然后,通过使用系统属性在运行时重新启用它。 有关在多租户 OIDC 部署中管理各个租户配置的更多信息,请参阅“使用 OpenID Connect (OIDC) 多租户”指南中的 Disabling tenant configurations部分。

OpenID Connect client and filters

`quarkus-oidc-client`扩展提供 `OidcClient`用于从支持以下令牌授予的 OpenID Connect 和 OAuth2 提供程序获取和刷新访问令牌:

  • client-credentials

  • password

  • refresh_token

quarkus-resteasy-client-oidc-filter`扩展需要 `quarkus-oidc-client`扩展。它提供 JAX-RS RESTful Web 服务 `OidcClientRequestFilter,它将由 `OidcClient`获取的访问令牌设置为 HTTP `Authorization`标头的 `Bearer`方案值。此过滤器可注册到注入到当前 Quarkus 端点的 MicroProfile REST 客户端实现,但它与该服务端点的身份验证要求无关。例如,它可以是公共端点或受 mTLS 保护。

在此场景中,您无需使用 Quarkus OpenID Connect 适配器保护 Quarkus 端点。

quarkus-resteasy-client-oidc-token-propagation`扩展需要 `quarkus-oidc`扩展。它提供 Jakarta REST `TokenCredentialRequestFilter,它将 OpenID Connect Bearer 令牌或授权码流程访问令牌设置为 HTTP `Authorization`标头的 `Bearer`方案值。此过滤器可注册到注入到当前 Quarkus 端点的 MicroProfile REST 客户端实现中,该实现必须使用 Quarkus OIDC 适配器进行保护。此过滤器可将访问令牌传播到下游服务。

SmallRye JWT authentication

quarkus-smallrye-jwt`扩展提供了一个 MicroProfile JSON Web Token (JWT) 2.1 实现和多个选项来验证已签名和已加密的 `JWT`令牌。它将它们表示为 `org.eclipse.microprofile.jwt.JsonWebToken

quarkus-smallrye-jwt`是 `quarkus-oidc`Bearer 令牌认证机制的替代方法,并且仅通过使用隐私增强电子邮件 (PEM) 密钥或可刷新的 `JWK`密钥集来验证 `JWT`令牌。`quarkus-smallrye-jwt`还提供 JWT 生成 API,您可以使用它轻松创建 `signed、`inner-signed`和 `encrypted``JWT`令牌。

有关更多信息,请参阅 Using JWT RBAC指南。

OAuth2 authentication

quarkus-elytron-security-oauth2`提供了对 Quarkus `quarkus-oidc`提供者令牌认证机制扩展的替代方案。`quarkus-elytron-security-oauth2`基于 `Elytron,并且主要用于远程检查不透明令牌。

有关更多信息,请参阅 Quarkus Using OAuth2指南。

Choosing between OpenID Connect, SmallRye JWT, and OAuth2 authentication mechanisms

使用以下信息选择合适的令牌认证机制以保护您的 Quarkus 应用程序。

List of authentication mechanism use cases
  • `quarkus-oidc`需要一个 OpenID Connect 提供程序(例如 Keycloak),该提供程序可以验证提供者令牌或使用授权代码流对最终用户进行认证。在这两种情况下,`quarkus-oidc`都需要连接到指定的 OpenID Connect 提供程序。

  • 如果用户认证需要授权代码流,或者您需要支持多个租户,请使用 quarkus-oidc。`quarkus-oidc`还可以通过使用授权代码流和提供者访问令牌来请求用户信息。

  • 如果必须验证您的提供者令牌,请使用 quarkus-oidcquarkus-elytron-security-oauth2`或 `quarkus-smallrye-jwt

  • 如果您的提供者令牌采用 JSON Web 令牌 (JWT) 格式,则可以使用前述列表中的任何扩展。quarkus-oidc`和 `quarkus-smallrye-jwt`都支持在 OpenID Connect 提供程序轮换密钥时刷新 `JsonWebKey (JWK) 集。因此,如果必须避免或由提供程序不支持远程令牌自省,请使用 `quarkus-oidc`或 `quarkus-smallrye-jwt`以验证 JWT 令牌。

  • 要远程自省 JWT 令牌,您可以使用 quarkus-oidc`或 `quarkus-elytron-security-oauth2,以便通过使用远程自省来验证不透明或二进制令牌。`quarkus-smallrye-jwt`不支持远程自省不透明或 JWT 令牌,而是依赖于通常从 OpenID Connect 提供程序检索的本地可用密钥。

  • quarkus-oidc`和 `quarkus-smallrye-jwt`支持 JWT 和不透明令牌注入到端点代码中。注入的 JWT 令牌提供有关用户的更多信息。所有扩展都可以将令牌注入为 `Principal

  • `quarkus-smallrye-jwt`支持比 `quarkus-oidc`更多密钥格式。`quarkus-oidc`仅使用 JWK 集中 JWK 格式的密钥,而 `quarkus-smallrye-jwt`支持 PEM 密钥。

  • `quarkus-smallrye-jwt`处理本地签名、内部签名和加密,以及加密令牌。相反,尽管 `quarkus-oidc`和 `quarkus-elytron-security-oauth2`也可以验证此类令牌,但它们将它们视作不透明令牌,并通过远程自省来验证它们。

  • 如果您需要一个用于远程自省不透明或 JWT 令牌的轻量级库,请使用 quarkus-elytron-security-oauth2

架构考虑决定了使用不透明或 JSON Web 令牌 (JWT) 令牌格式。不透明令牌往往比 JWT 令牌短得多,但需要在提供程序数据库中维护大多数令牌关联状态。不透明令牌实际上是数据库指针。 JWT 令牌比不透明令牌长得多。尽管如此,提供程序有效地将大多数令牌关联状态委派给了客户端,方法是将其存储为令牌声明,并对它们进行签名或加密。

Table 2. Token authentication mechanism comparison
Feature required Authentication mechanism

quarkus-oidc

quarkus-smallrye-jwt

quarkus-elytron-security-oauth2

Bearer JWT verification

Local verification or introspection

Local verification

Introspection

Bearer opaque token verification

Introspection

No

Introspection

刷新 `JsonWebKey`集以验证 JWT 令牌

Yes

Yes

No

Represent token as Principal

Yes

Yes

Yes

注入 JWT 作为 MP JWT

Yes

Yes

No

Authorization code flow

Yes

No

No

Multi-tenancy

Yes

No

No

User information support

Yes

No

No

PEM key format support

No

Yes

No

SecretKey support

No

采用 JSON Web 密钥 (JWK) 格式

No

内部签名和加密或加密令牌

Introspection

Local verification

Introspection

Custom token verification

No

With injected JWT parser

No

JWT 作为 cookie 支持

No

Yes

Yes

Combining authentication mechanisms

如果不同的来源提供用户凭证,您可以结合认证机制。例如,您可以结合内置的基本和 Quarkus @“1” 提供方令牌认证机制。

您不能结合 Quarkus @“2” 提供方令牌和 @“3” 认证机制,因为这两个机制都会尝试验证从 HTTP 提供方令牌认证方案中提取的令牌。

Use HTTP Security Policy to enable path-based authentication

下面的配置文件示例展示了如何针对给定的请求路径强制执行一个单一的可选认证机制:

quarkus.http.auth.permission.basic-or-bearer.paths=/service
quarkus.http.auth.permission.basic-or-bearer.policy=authenticated

quarkus.http.auth.permission.basic.paths=/basic-only
quarkus.http.auth.permission.basic.policy=authenticated
quarkus.http.auth.permission.basic.auth-mechanism=basic

quarkus.http.auth.permission.bearer.paths=/bearer-only
quarkus.http.auth.permission.bearer.policy=authenticated
quarkus.http.auth.permission.bearer.auth-mechanism=bearer

确保 @“4” 属性的值与由 @“5” 支持的认证方案匹配,例如 @“6”、@“7” 或 @“8”。

Use annotations to enable path-based authentication for Jakarta REST endpoints

可以使用注释来为每个 Jakarta REST 端点选择一个特定的认证机制。当 @“9” 禁用时,才会启用此功能,这是由于注释只能用于在匹配 REST 端点后选择认证机制。以下是您如何为 REST 端点基础选择认证机制:

quarkus.http.auth.proactive=false
import io.quarkus.oidc.AuthorizationCodeFlow;
import io.quarkus.vertx.http.runtime.security.annotation.BasicAuthentication;
import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@Path("hello")
public class HelloResource {

    @GET
    @BasicAuthentication 1 2
    @Path("basic")
    public String basicAuthMechanism() {
        return "basic";
    }

    @GET
    @RolesAllowed("admin") 3
    @AuthorizationCodeFlow 4
    @Path("code-flow")
    public String codeFlowAuthMechanism() {
        return "code-flow";
    }
}
1 REST 端点 @“10” 只可以通过使用 @“11” 访问。
2 此端点需要认证,因为当没有标准安全注释伴随 @“12” 注释时,@“13” 注释会默认添加。
3 @“14” 注释可以与任何其他标准安全注释相结合,例如 @“15”、@“16” 等。
4 REST 端点 @“17” 只可以通过使用 @“18” 访问。
Table 3. Supported authentication mechanism annotations
Authentication mechanism^ Annotation

Basic authentication mechanism

io.quarkus.vertx.http.runtime.security.annotation.BasicAuthentication

Form-based authentication mechanism

io.quarkus.vertx.http.runtime.security.annotation.FormAuthentication

Mutual TLS authentication mechanism

io.quarkus.vertx.http.runtime.security.annotation.MTLSAuthentication

WebAuthn authentication mechanism

io.quarkus.security.webauthn.WebAuthn

Bearer token authentication mechanism

io.quarkus.oidc.BearerTokenAuthentication

OIDC 授权码流程机制

io.quarkus.oidc.AuthorizationCodeFlow

SmallRye JWT authentication mechanism

io.quarkus.smallrye.jwt.runtime.auth.BearerTokenAuthentication

Quarkus 自动保护用认证机制注释注释的端点。当 REST 端点和资源上不存在标准安全注释时,@“19” 注释会为您添加。

还可以使用 @“20” 注释根据方案选择任何认证机制。基于注释的 @“21” 配置属性的类比是 @“22” 注释。

为了与各种 Jakarta EE 规范保持一致,建议始终重复注释,而不是依赖于注释继承。

How to combine it with HTTP Security Policy

定义允许访问单个资源的角色的最简单方法是 @“23” 注释。不过,也可以像以下示例中那样使用 HTTP 安全策略:

quarkus.http.auth.policy.roles1.roles-allowed=user
quarkus.http.auth.permission.roles1.paths=/hello/code-flow
quarkus.http.auth.permission.roles1.applies-to=JAXRS 1
quarkus.http.auth.permission.roles1.policy=roles1
quarkus.http.auth.permission.roles1.methods=GET 2
1 在选择特定端点的认证机制后,延迟此策略的权限检查。
2 让 @“24” 权限仅匹配用 @“25” 注释注释的端点。未注释的端点必须避免因 @“26” 选项造成的延迟。

Proactive authentication

Quarkus 中默认启用主动认证。这意味着如果传入请求包含凭证,那么该请求将始终经过认证,即使目标页面不需要认证。如需了解更多信息,请参阅 Quarkus @“27” 指南。