Quarkus 中的身份验证机制
Quarkus 安全框架支持多种身份验证机制,您可以使用它们来保护您的应用程序。您也可以组合使用身份验证机制。
在为 Quarkus 应用程序选择身份验证机制之前,请查看所提供的信息。 |
支持的身份验证机制概述
某些受支持的身份验证机制内置于 Quarkus 中,而其他机制则需要您添加扩展。所有这些机制将在以下各节中详细介绍
下表将特定身份验证要求映射到您可以在 Quarkus 中使用的受支持机制
身份验证要求 | 身份验证机制 |
---|---|
用户名和密码 |
|
Bearer 访问令牌 |
|
单点登录 (SSO) |
|
客户端证书 |
|
WebAuthn |
|
Kerberos 票证 |
有关更多信息,请参阅以下 令牌身份验证机制比较 表。
内置身份验证机制
Quarkus Security 提供以下内置身份验证支持
基于表单的身份验证
Quarkus 提供了基于表单的身份验证,其工作方式与传统的 Servlet 基于表单的身份验证类似。与传统的表单身份验证不同,经过身份验证的用户不会存储在 HTTP 会话中,因为 Quarkus 不支持集群 HTTP 会话。相反,身份验证信息存储在加密 cookie 中,该 cookie 可以被共享相同加密密钥的所有集群成员读取。
要应用加密,请添加 quarkus.http.auth.session.encryption-key
属性,并确保您设置的值至少为 16 个字符长。加密密钥使用 SHA-256 进行哈希处理。生成的摘要用作 cookie 值的 AES-256 加密的密钥。cookie 包含作为加密值一部分的过期时间,因此集群中的所有节点必须同步其时钟。如果会话正在使用中,则每隔一分钟会生成一个带有更新后的过期时间的新 cookie。
要开始使用表单身份验证,您应该具有与 启用 Basic 身份验证 中描述的类似设置,并且必须将属性 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 或其他在 启用 Basic 身份验证 中提到的。 |
并且应用程序登录页面将包含类似于此的 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。
import io.quarkus.security.identity.CurrentIdentityAssociation;
import io.quarkus.vertx.http.runtime.security.FormAuthenticationMechanism;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.POST;
@Inject
CurrentIdentityAssociation identity;
@POST
public Response logout() {
if (identity.getIdentity().isAnonymous()) {
throw new UnauthorizedException("Not authenticated");
}
FormAuthenticationMechanism.logout(identity.getIdentity()); (1)
return Response.noContent().build();
}
1 | 通过删除会话 cookie 执行注销。 |
以下属性可用于配置基于表单的身份验证
构建时固定的配置属性 - 所有其他配置属性都可以在运行时覆盖
配置属性 |
类型 |
默认 |
---|---|---|
确定是否启用整个权限集。 默认情况下,如果定义了权限集,则启用它。 环境变量: 显示更多 |
布尔值 |
|
此权限集链接到的 HTTP 策略。 有三种内置策略:permit、deny 和 authenticated。可以定义基于角色的策略,扩展可以添加自己的策略。 环境变量: 显示更多 |
字符串 |
必需 |
此权限集适用的方法。如果未设置,则它们适用于所有方法。 请注意,如果请求匹配任何权限集中的任何路径,但由于未列出该方法而与约束不匹配,则该请求将被拒绝。 方法特定的权限优先于没有任何方法设置的匹配项。 这意味着例如,如果 Quarkus 配置为允许 GET 和 POST 请求到 /admin,并且没有配置其他权限,则对 /admin 的 PUT 请求将被拒绝。 环境变量: 显示更多 |
字符串列表 |
|
此权限检查适用的路径。如果路径以 /* 结尾,则将其视为路径前缀,否则将其视为完全匹配。 匹配是基于长度完成的,因此最具体的路径匹配优先。 如果多个权限集匹配同一路径,则显式方法匹配优先于没有方法设置的匹配,否则将应用最严格的权限。 环境变量: 显示更多 |
字符串列表 |
|
路径特定的身份验证机制,必须用于验证用户身份。它需要匹配 环境变量: 显示更多 |
字符串 |
|
指示此策略始终应用于匹配的路径,以及具有获胜路径的策略。避免创建多个共享策略以最大限度地减少性能影响。 环境变量: 显示更多 |
布尔值 |
|
权限检查应应用于所有匹配的路径,还是 Jakarta REST 资源特定的路径。 环境变量: 显示更多 |
|
|
允许访问受此策略保护的资源的各个角色。默认情况下,允许任何经过身份验证的用户访问。 环境变量: 显示更多 |
字符串列表 |
|
根据 环境变量: 显示更多 |
Map<String,List<String>> |
|
如果此策略成功应用(该策略允许请求继续)并且经过身份验证的请求具有所需角色,则授予 环境变量: 显示更多 |
Map<String,List<String>> |
|
此策略授予的权限将使用此配置属性指定的 环境变量: 显示更多 |
字符串 |
|
将 例如,如果 环境变量: 显示更多 |
Map<String,List<String>> |
|
客户端证书属性,其值将根据证书属性文件中指定的角色映射映射到“SecurityIdentity”角色。该属性必须是相对专有名称 (RDN) 或主题备用名称 (SAN) 之一。默认情况下,通用名称 (CN) 属性值用于角色映射。支持的值为
环境变量: 显示更多 |
字符串 |
|
包含客户端证书属性值到角色映射的属性文件。仅当使用 属性文件应具有 环境变量: 显示更多 |
path |
|
字符串 |
||
登录页面。可以通过设置 环境变量: 显示更多 |
字符串 |
|
用户名栏位名称。 环境变量: 显示更多 |
字符串 |
|
密码栏位名称。 环境变量: 显示更多 |
字符串 |
|
错误页面。可以通过设置 环境变量: 显示更多 |
字符串 |
|
如果没有要重定向回的已保存页面,则重定向到的着陆页。可以通过设置 环境变量: 显示更多 |
字符串 |
|
用于控制用于将用户重定向回他们想要访问的位置的 cookie 的名称的选项。 环境变量: 显示更多 |
字符串 |
|
非活动(空闲)超时 达到非活动超时后,不会续订 cookie,并且会强制进行新的登录。 环境变量: 显示更多 |
|
|
在 cookie 被替换为具有更新的超时的新的 cookie 之前,cookie 可以存在多长时间,也称为“续订超时”。 请注意,较小的值会导致服务器负载略有增加(因为会更频繁地生成新的加密 cookie);但是,较大的值会影响非活动超时,因为超时是在生成 cookie 时设置的。 例如,如果将其设置为 10 分钟,并且非活动超时为 30 分钟,如果用户上次请求是在 cookie 已存在 9 分钟时,则实际超时将在上次请求后 21 分钟发生,因为超时仅在生成新的 cookie 时刷新。 也就是说,服务器端没有跟踪超时;时间戳已编码并加密在 cookie 本身中,并且每次请求都会对其进行解密和解析。 环境变量: 显示更多 |
|
|
字符串 |
|
|
会话和位置 cookie 的 cookie 路径。 环境变量: 显示更多 |
字符串 |
|
设置 HttpOnly 属性以防止通过 JavaScript 访问 cookie。 环境变量: 显示更多 |
布尔值 |
|
会话和位置 cookie 的 SameSite 属性。 环境变量: 显示更多 |
|
|
会话 cookie 的 Max-Age 属性。这是浏览器保留 cookie 的时间量。 默认值为空,这意味着 cookie 将保留到浏览器关闭为止。 环境变量: 显示更多 |
||
要求所有注册的 HTTP 身份验证机制都必须尝试验证请求凭据。 默认情况下,当 可以使用以下实用程序方法检索所有生成的安全身份
注入的 默认情况下,此属性为 false,这意味着身份验证过程会在创建第一个 如果启用了特定于路径的身份验证,则将忽略此属性。 环境变量: 显示更多 |
布尔值 |
|
|
|
关于 Duration 格式
要编写持续时间值,请使用标准 您还可以使用简化的格式,以数字开头
在其他情况下,简化格式将被转换为
|
相互 TLS 身份验证
Quarkus 提供相互 TLS (mTLS) 身份验证,以便您可以根据用户的 X.509 证书对用户进行身份验证。
要使用此身份验证方法,您必须首先为您的应用程序启用 SSL/TLS。有关更多信息,请参阅 Quarkus "HTTP 参考" 指南的 支持使用 SSL/TLS 的安全连接 部分。
在您的应用程序接受安全连接后,下一步是使用保存您的应用程序信任的所有证书的文件名配置 quarkus.http.ssl.certificate.trust-store-file
属性。此文件还包括有关当客户端(例如浏览器或另一服务)尝试访问其受保护资源之一时,您的应用程序如何请求证书的信息。
由于 JKS 不再是 Quarkus 中的默认密钥库和信任库格式,因此框架会根据文件扩展名进行有根据的猜测
-
.pem
、.crt
和.key
被读取为 PEM 证书和密钥。 -
.jks
、.keystore
和.truststore
被读取为 JKS 密钥库和信任库。 -
.p12
、.pkcs12
和.pfx
被读取为 PKCS12 密钥库和信任库。
如果您的文件未使用这些扩展名之一,则必须使用以下属性设置格式
quarkus.http.ssl.certificate.key-store-file-type=JKS # or P12 or PEM
quarkus.http.ssl.certificate.trust-store-file-type=JKS # or P12 or PEM
JKS 的使用越来越少。自 Java 9 以来,Java 中的默认密钥库格式为 PKCS12。JKS 和 PKCS12 之间最显着的区别是 JKS 是 Java 特有的格式。相比之下,PKCS12 是一种标准化的、与语言无关的存储加密私钥和证书的方式。
以下是启用 mTLS 的配置示例
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 | 将 quarkus.http.ssl.client-auth 设置为 required 会使服务器要求客户端证书。如果服务器应接受没有证书的请求,您可以将其设置为 REQUEST 。当支持 mTLS 以外的多种身份验证方法时,此设置很有用。 |
4 | 定义一个策略,其中只有经过身份验证的用户才能访问您应用程序中的资源。 |
5 | 禁用纯 HTTP 协议,要求所有请求都使用 HTTPS。当您将 quarkus.http.ssl.client-auth 设置为 required 时,会自动禁用 quarkus.http.insecure-requests 。 |
当传入请求与信任库中的有效证书匹配时,您的应用程序可以通过注入 SecurityIdentity
来获取主题,如下所示
@Inject
SecurityIdentity identity;
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return String.format("Hello, %s", identity.getPrincipal().getName());
}
您还可以使用以下示例中概述的代码获取证书
import java.security.cert.X509Certificate;
import io.quarkus.security.credential.CertificateCredential;
CertificateCredential credential = identity.getCredential(CertificateCredential.class);
X509Certificate certificate = credential.getCertificate();
将证书属性映射到角色
来自客户端证书的信息可用于向 Quarkus SecurityIdentity
添加角色。
您可以在检查客户端证书的通用名称 (CN) 属性后向 SecurityIdentity
添加新角色。添加新角色的最简单方法是使用证书属性到角色映射功能。
例如,您可以按如下方式更新介绍 相互 TLS 身份验证 的部分中显示的属性
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 classpath 资源包含证书的 CN 值到角色的映射,形式为 CN=role 或 CN=role1,role2 等。假设它包含三个条目:alice=user,admin 、bob=user 和 jdoe=tester 。 |
2 | 使用 HTTP 安全策略要求 SecurityIdentity 必须具有 user 或 admin 角色才能授权请求。 |
给定前面的配置,如果客户端证书的 CN 属性等于 alice
或 bob
,则授权请求,如果等于 jdoe
,则禁止请求。
使用证书属性来增强 SecurityIdentity
如果自动 将证书属性映射到角色 选项不适合,您可以随时注册 SecurityIdentityAugmentor
。自定义 SecurityIdentityAugmentor
可以检查不同客户端证书属性的值,并相应地增强 SecurityIdentity
。
有关自定义 SecurityIdentity
的更多信息,请参阅 Quarkus "安全提示和技巧" 指南中的 安全身份自定义 部分。
其他支持的身份验证机制
Quarkus Security 还通过扩展支持以下身份验证机制
WebAuthn 身份验证
WebAuthn 是一种身份验证机制,用于替换密码。当您编写用于注册新用户或登录用户的服务时,您可以使用 WebAuthn 来替换密码,而不是要求输入密码。有关更多信息,请参阅 使用 WebAuthn 身份验证机制保护 Quarkus 应用程序 指南。
OpenID Connect 身份验证
OpenID Connect (OIDC) 是在 OAuth 2.0 协议之上工作的一个身份层。OIDC 使客户端应用程序能够根据 OIDC 提供程序执行的身份验证来验证用户的身份,并检索有关该用户的基本信息。
Quarkus quarkus-oidc
扩展提供了一个反应式的、可互操作的、多租户启用的 OIDC 适配器,该适配器支持 Bearer 令牌和授权码流身份验证机制。Bearer 令牌身份验证机制从 HTTP 授权标头中提取令牌。
授权码流机制会将用户重定向到 OIDC 提供程序以验证用户的身份。在用户被重定向回 Quarkus 后,该机制通过交换为 ID、访问和刷新令牌授予的提供的代码来完成身份验证过程。
您可以使用可刷新的 JSON Web Key (JWK) 集验证 ID 和访问 JSON Web Token (JWT) 令牌,或远程内省它们。但是,不透明(也称为二进制令牌)只能远程内省。
使用 Quarkus OIDC 扩展,Bearer 令牌和授权码流身份验证机制都使用 SmallRye JWT 身份验证 将 JWT 令牌表示为 MicroProfile JWT |
有关 OIDC 身份验证的其他 Quarkus 资源
有关您可用于保护 Quarkus 应用程序的 OIDC 身份验证和授权方法的更多信息,请参阅以下资源
OIDC 主题 | Quarkus 信息资源 |
---|---|
Bearer 令牌身份验证机制 |
|
授权码流身份验证机制 |
|
OIDC 和 SAML 身份代理 |
|
可以支持 Bearer 令牌身份验证或授权码流机制的多个租户 |
|
使用常用的 OpenID Connect 提供程序保护 Quarkus |
|
使用 Keycloak 来集中授权 |
|
以编程方式配置 Keycloak |
要在运行时启用 Quarkus OIDC 扩展,请在构建时设置 有关在多租户 OIDC 部署中管理各个租户配置的更多信息,请参阅“使用 OpenID Connect (OIDC) 多租户”指南中的禁用租户配置部分。 |
OpenID Connect 客户端和过滤器
quarkus-oidc-client
扩展提供了 OidcClient
,用于从支持以下令牌授予的 OpenID Connect 和 OAuth2 提供程序获取和刷新访问令牌
-
client-credentials
-
密码
-
refresh_token
quarkus-resteasy-client-oidc-filter
扩展需要 quarkus-oidc-client
扩展。它提供了 JAX-RS RESTful Web Services 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 身份验证
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
令牌。
有关更多信息,请参阅使用 JWT RBAC指南。
OAuth2 身份验证
quarkus-elytron-security-oauth2
提供了 Quarkus quarkus-oidc
Bearer 令牌身份验证机制扩展的替代方案。quarkus-elytron-security-oauth2
基于 Elytron
,主要用于远程内省不透明令牌。
有关更多信息,请参阅 Quarkus 使用 OAuth2 指南。
OpenID Connect、SmallRye JWT 和 OAuth2 身份验证机制之间的选择
使用以下信息选择合适的令牌身份验证机制来保护您的 Quarkus 应用程序。
-
quarkus-oidc
需要 OpenID Connect 提供程序(例如 Keycloak),该提供程序可以验证 Bearer 令牌或使用授权码流验证最终用户。在这两种情况下,quarkus-oidc
都需要连接到指定的 OpenID Connect 提供程序。 -
如果用户身份验证需要授权码流,或者您需要支持多个租户,请使用
quarkus-oidc
。quarkus-oidc
还可以使用授权码流和 Bearer 访问令牌请求用户信息。 -
如果必须验证您的 Bearer 令牌,请使用
quarkus-oidc
、quarkus-elytron-security-oauth2
或quarkus-smallrye-jwt
。 -
如果您的 Bearer 令牌是 JSON Web Token (JWT) 格式,则可以使用前面列表中的任何扩展。当 OpenID Connect 提供程序轮换密钥时,
quarkus-oidc
和quarkus-smallrye-jwt
都支持刷新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 Token (JWT) 令牌格式。不透明令牌往往比 JWT 令牌短得多,但需要在提供程序数据库中维护大多数与令牌相关的状态。不透明令牌实际上是数据库指针。 JWT 令牌比不透明令牌长得多。尽管如此,提供程序通过将大多数与令牌相关的状态存储为令牌声明并对其进行签名或加密来有效地将这些状态委托给客户端。 |
所需功能 | 身份验证机制 | ||
---|---|---|---|
|
|
|
|
Bearer JWT 验证 |
本地验证或自检 |
本地验证 |
自检 |
Bearer 不透明令牌验证 |
自检 |
否 |
自检 |
刷新 |
是 |
是 |
否 |
将令牌表示为 |
是 |
是 |
是 |
将 JWT 作为 MP JWT 注入 |
是 |
是 |
否 |
授权码流 |
是 |
否 |
否 |
多租户 |
是 |
否 |
否 |
用户信息支持 |
是 |
否 |
否 |
PEM 密钥格式支持 |
否 |
是 |
否 |
SecretKey 支持 |
否 |
以 JSON Web Key (JWK) 格式 |
否 |
内部签名和加密或加密的令牌 |
自检 |
本地验证 |
自检 |
自定义令牌验证 |
否 |
使用注入的 JWT 解析器 |
否 |
JWT 作为 cookie 支持 |
否 |
是 |
是 |
组合身份验证机制
如果不同的来源提供用户凭据,您可以组合身份验证机制。例如,您可以组合内置的 Basic 和 Quarkus quarkus-oidc
Bearer 令牌身份验证机制。
身份验证过程会在其中一个身份验证机制生成第一个 SecurityIdentity
时完成。
包含式身份验证
在某些情况下,可能需要所有注册的身份验证机制都创建其 SecurityIdentity
。当必须通过 Mutual TLS 身份验证传递令牌等凭据时,例如,当用户通过 Virtual Private Network
进行身份验证时,或者当当前令牌必须绑定到客户端证书以使令牌验证成功时,这可能是必需的,以保证令牌已完全颁发给当前将此令牌连同其客户端证书传递给 Quarkus 的同一客户端。
在 Quarkus 中,这种身份验证称为 inclusive
,您可以这样启用它
quarkus.http.auth.inclusive=true
如果身份验证是包含式的,则第一个身份验证机制创建的 SecurityIdentity
可以注入到应用程序代码中。例如,如果需要同时进行 Mutual TLS 身份验证和基本身份验证机制身份验证,则 Mutual TLS 身份验证机制将首先创建 SecurityIdentity
。
启用包含式身份验证后,Mutual TLS 身份验证机制具有最高优先级,以确保注入的 SecurityIdentity 始终代表 Mutual TLS 身份验证,并且可以用于访问其他身份验证机制提供的 SecurityIdentity 身份。 |
额外的 SecurityIdentity
实例可以作为第一个 SecurityIdentity
上的 quarkus.security.identities
属性访问,但是,直接访问这些额外的身份可能不是必需的,例如,当 Mutual TLS 身份验证和 OIDC Bearer 身份验证机制已组合并完成其身份验证时,经过身份验证的 Bearer 令牌可以作为令牌凭据与 Mutual TLS 身份验证创建的 SecurityIdentity
一起注入。下面对此进行了举例说明
package org.acme.security;
import io.quarkus.oidc.AccessTokenCredential;
import io.quarkus.oidc.common.runtime.OidcConstants;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.vertx.http.runtime.security.HttpSecurityUtils;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
@ApplicationScoped
public class InclusiveAuthExampleBean {
@Inject
SecurityIdentity mtlsIdentity; (1)
@Inject
AccessTokenCredential accessTokenCredential;
private AccessTokenCredential getAccessTokenCredential() {
if (doItHardWay()) {
var securityIdentities = HttpSecurityUtils.getSecurityIdentities(mtlsIdentity); (2)
if (securityIdentities != null) {
SecurityIdentity bearerIdentity = securityIdentities.get(OidcConstants.BEARER_SCHEME);
if (bearerIdentity != null) {
return bearerIdentity.getCredential(AccessTokenCredential.class);
}
}
}
return accessTokenCredential;
}
}
1 | 这是具有最高优先级的适用身份验证机制创建的 SecurityIdentity 。 |
2 | 其他适用的身份验证机制执行了身份验证,并且可以在 SecurityIdentity 上使用。 |
您不能组合 Quarkus |
基于路径的身份验证
使用 HTTP 安全策略启用基于路径的身份验证
以下配置示例演示了如何为给定的请求路径强制执行单个可选身份验证机制
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
确保 auth-mechanism
属性的值与 HttpAuthenticationMechanism
支持的身份验证方案匹配,例如 basic
、bearer
或 form
。
使用注释为 Jakarta REST 端点启用基于路径的身份验证
可以使用注释来选择特定于每个 Jakarta REST 端点的身份验证机制。仅当由于注释只能用于在 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 端点 /hello/basic 。 |
2 | 此端点需要身份验证,因为当没有标准安全注释伴随 @BasicAuthentication 注释时,默认情况下会添加 @Authenticated 注释。 |
3 | @AuthorizationCodeFlow 注释可以与任何其他标准安全注释组合使用,如 @RolesAllowed 、@PermissionsAllowed 等。 |
4 | 只能使用 OIDC 授权码流机制访问 REST 端点 /hello/code-flow 。 |
身份验证机制^ | 注解 |
---|---|
基本身份验证机制 |
|
基于表单的身份验证机制 |
|
Mutual TLS 身份验证机制 |
|
WebAuthn 身份验证机制 |
|
Bearer 令牌身份验证机制 |
|
OIDC 授权码流机制 |
|
SmallRye JWT 身份验证机制 |
|
Quarkus 会自动保护使用身份验证机制注释注释的端点。当 REST 端点和资源上不存在标准安全注释时,会自动为您添加 io.quarkus.security.Authenticated 注释。 |
还可以使用 io.quarkus.vertx.http.runtime.security.annotation.HttpAuthenticationMechanism
注释基于其方案选择任何身份验证机制。基于注释的类比于 quarkus.http.auth.permission.basic.auth-mechanism=custom
配置属性是 @HttpAuthenticationMechanism("custom")
注释。
为了与各种 Jakarta EE 规范保持一致,建议始终重复注释,而不是依赖于注释继承。 |
使用包含式身份验证启用基于路径的身份验证
默认情况下,Quarkus 仅支持每个路径一种身份验证机制的基于路径的身份验证。如果必须为基于路径的身份验证使用多种身份验证机制,您可以编写自定义 HttpAuthenticationMechanism
,如安全提示和技巧指南的 处理多个 HttpAuthenticationMechanism 部分中所述。另一个选项是在宽松模式下启用包含式身份验证,并编写自定义 HttpSecurityPolicy
或 PermissionChecker
,以验证所有注册的 HTTP 身份验证机制是否创建了其特定于机制的 SecurityIdentity
。
quarkus.http.auth.inclusive-mode=lax (1)
quarkus.http.auth.inclusive=true
1 | 默认情况下,包含式身份验证要求所有注册的 HTTP 身份验证机制都必须创建 SecurityIdentity 。但是,在宽松模式下,如果至少一个注册的 HttpAuthenticationMechanism 创建了 SecurityIdentity ,则身份验证将成功。 |
假设我们有 3 种注册的机制,mTLS、Basic 和 OIDC,并且您只需要 Basic 和 mTLS 身份验证成功即可允许访问 hello
方法。在这种情况下,在宽松模式下启用包含式身份验证允许检查哪些机制生成了身份,如下例所示
import io.quarkus.security.PermissionChecker;
import io.quarkus.security.PermissionsAllowed;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.vertx.http.runtime.security.HttpSecurityUtils;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import java.util.Map;
@Path("/hello")
public class HelloResource {
@PermissionsAllowed("mtls-basic")
@GET
public String hello() {
return "Hello world";
}
@PermissionChecker("mtls-basic")
boolean isMtlsAndBasicAuthentication(SecurityIdentity identity) {
Map<String, SecurityIdentity> identities = HttpSecurityUtils.getSecurityIdentities(identity);
if (identities != null) {
return identities.containsKey("basic") && identities.containsKey("x509"); (1)
}
return false;
}
}
1 | 仅当确认 mTLS 和 Basic 身份验证机制都已对当前请求进行身份验证时,才允许访问端点。 |
如何将其与 HTTP 安全策略结合使用
定义允许访问各个资源的角色的最简单方法是使用 @RolesAllowed
注释。但是,也可以像下面的示例一样使用 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 | 仅当端点使用 @AuthorizationCodeFlow 注释进行注释时,才使 roles1 权限匹配。未注释的端点必须避免 applies-to=JAXRS 选项造成的延迟。 |
主动身份验证
默认情况下,Quarkus 中启用主动身份验证。这意味着,如果传入请求具有凭据,则无论目标页面是否需要身份验证,都将始终对请求进行身份验证。有关更多信息,请参阅 Quarkus 主动身份验证 指南。