编辑此页面

使用 JWT RBAC

本指南介绍了如何将 SmallRye JWT 集成到您的 Quarkus 应用程序中,以实现符合 MicroProfile JWT 规范的 JSON Web Token (JWT) 安全。您将学习如何验证 JWT、将其表示为 MicroProfile JWT org.eclipse.microprofile.jwt.JsonWebToken,以及如何使用 Bearer 令牌授权和 基于角色的访问控制 来保护 Quarkus HTTP 端点。

Quarkus OpenID Connect (quarkus-oidc) 扩展也支持 Bearer 令牌授权,并使用 smallrye-jwt 将 Bearer 令牌表示为 JsonWebToken。有关详细信息,请参阅 OIDC Bearer 令牌身份验证 指南。

如果您的 Quarkus 应用程序需要使用 OIDC 授权码流来认证用户,则必须使用 OpenID Connect 扩展。有关更多信息,请参阅 保护 Web 应用程序的 OIDC 授权码流机制

先决条件

要完成本指南,您需要

  • 大约 15 分钟

  • 一个 IDE

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

  • Apache Maven 3.9.9

  • 如果您想使用它,可以选择 Quarkus CLI

  • 如果您想构建本机可执行文件(或者如果您使用本机容器构建,则为 Docker),可以选择安装 Mandrel 或 GraalVM 并进行适当的配置

快速入门

解决方案

我们建议按照后续部分的说明逐步创建应用程序。如果您愿意,可以跳到已完成的示例。

要访问示例,请克隆 Git 存储库或下载存档

已完成的解决方案位于 security-jwt-quickstart 目录中。

创建 Maven 项目

首先,使用以下命令创建一个新项目

CLI
quarkus create app org.acme:security-jwt-quickstart \
    --extension='rest-jackson,smallrye-jwt,smallrye-jwt-build' \
    --no-code
cd security-jwt-quickstart

要创建 Gradle 项目,请添加 --gradle--gradle-kotlin-dsl 选项。

有关如何安装和使用 Quarkus CLI 的更多信息,请参阅 Quarkus CLI 指南。

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.24.4:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=security-jwt-quickstart \
    -Dextensions='rest-jackson,smallrye-jwt,smallrye-jwt-build' \
    -DnoCode
cd security-jwt-quickstart

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

对于 Windows 用户

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

  • 如果使用 Powershell,请将 -D 参数括在双引号中,例如 "-DprojectArtifactId=security-jwt-quickstart"

此命令会生成 Maven 项目并导入 smallrye-jwt 扩展,其中包含 MicroProfile JWT RBAC 支持。

如果您已经配置了 Quarkus 项目,可以通过在项目根目录下运行以下命令将 smallrye-jwt 扩展添加到项目中

CLI
quarkus extension add smallrye-jwt,smallrye-jwt-build
Maven
./mvnw quarkus:add-extension -Dextensions='smallrye-jwt,smallrye-jwt-build'
Gradle
./gradlew addExtension --extensions='smallrye-jwt,smallrye-jwt-build'

此命令将以下依赖项添加到您的构建文件中

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

检查 Jakarta REST 资源

src/main/java/org/acme/security/jwt/TokenSecuredResource.java 中创建一个 REST 端点,内容如下

REST 端点 V1
package org.acme.security.jwt;

import jakarta.annotation.security.PermitAll;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.InternalServerErrorException;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.SecurityContext;

import org.eclipse.microprofile.jwt.JsonWebToken;

@Path("/secured")
public class TokenSecuredResource {

    @Inject
    JsonWebToken jwt; (1)

    @GET
    @Path("permit-all")
    @PermitAll (2)
    @Produces(MediaType.TEXT_PLAIN)
    public String hello(@Context SecurityContext ctx) {
        return getResponseString(ctx); (3)
    }

    private String getResponseString(SecurityContext ctx) {
        String name;
        if (ctx.getUserPrincipal() == null) { (4)
            name = "anonymous";
        } else if (!ctx.getUserPrincipal().getName().equals(jwt.getName())) { (5)
            throw new InternalServerErrorException("Principal and JsonWebToken names do not match");
        } else {
            name = ctx.getUserPrincipal().getName(); (6)
        }
        return String.format("hello %s,"
            + " isHttps: %s,"
            + " authScheme: %s,"
            + " hasJWT: %s",
            name, ctx.isSecure(), ctx.getAuthenticationScheme(), hasJwt()); (7)
    }

    private boolean hasJwt() {
        return jwt.getClaimNames() != null;
    }
}
1 注入 JsonWebToken 接口,提供对当前已验证令牌相关联的声明的访问。此接口扩展了 java.security.Principal
2 @PermitAll 是一个标准的 Jakarta 安全注解。它表示给定端点可供所有调用者访问,无论是否已验证。
3 注入 Jakarta REST SecurityContext 以检查请求的安全状态。getResponseString() 函数生成响应。
4 通过检查请求用户/调用者 Principal 是否为 null 来检查调用是否不安全。
5 确保 PrincipalJsonWebToken 中的名称匹配,因为 JsonWebToken 代表当前的 Principal
6 检索 Principal 的名称。
7 构建一个包含调用者姓名、请求 SecurityContextisSecure()getAuthenticationScheme() 状态,以及是否注入了非 null JsonWebToken 的响应。

以开发模式运行应用程序

现在,您可以使用以下任一命令以开发模式运行应用程序

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

然后,您应该看到类似以下示例的输出

quarkus:dev 输出
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------------< org.acme:security-jwt-quickstart >-----------------------
[INFO] Building security-jwt-quickstart 1.0.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
...
Listening for transport dt_socket at address: 5005
2020-07-15 16:09:50,883 INFO  [io.quarkus] (Quarkus Main Thread) security-jwt-quickstart 1.0.0-SNAPSHOT on JVM (powered by Quarkus 999-SNAPSHOT) started in 1.073s. Listening on: http://0.0.0.0:8080
2020-07-15 16:09:50,885 INFO  [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2020-07-15 16:09:50,885 INFO  [io.quarkus] (Quarkus Main Thread) Installed features: [cdi, mutiny, rest, rest-jackson, security, smallrye-context-propagation, smallrye-jwt, vertx, vertx-web]

现在 REST 端点正在运行,您可以使用 curl 等命令行工具访问它

curl 命令用于 /secured/permit-all
$ curl http://127.0.0.1:8080/secured/permit-all; echo

此命令返回以下响应

hello anonymous, isHttps: false, authScheme: null, hasJWT: false

您在请求中没有提供任何 JWT,因此您不应期望端点看到任何安全状态,并且响应与此一致

  • username 为匿名。

  • isHttpsfalse,因为未使用 https

  • authSchemenull

  • hasJWTfalse

使用 Ctrl-C 停止 Quarkus 服务器。

现在让我们来保护一些东西。看一下新的端点方法 helloRolesAllowed,如下所示

REST 端点 V2
package org.acme.security.jwt;

import jakarta.annotation.security.PermitAll;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.InternalServerErrorException;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.SecurityContext;

import org.eclipse.microprofile.jwt.JsonWebToken;

@Path("/secured")
public class TokenSecuredResource {

    @Inject
    JsonWebToken jwt; (1)

    @GET
    @Path("permit-all")
    @PermitAll
    @Produces(MediaType.TEXT_PLAIN)
    public String hello(@Context SecurityContext ctx) {
        return getResponseString(ctx);
    }

    @GET
    @Path("roles-allowed") (2)
    @RolesAllowed({ "User", "Admin" }) (3)
    @Produces(MediaType.TEXT_PLAIN)
    public String helloRolesAllowed(@Context SecurityContext ctx) {
        return getResponseString(ctx) + ", birthdate: " + jwt.getClaim("birthdate").toString(); (4)
    }

    private String getResponseString(SecurityContext ctx) {
        String name;
        if (ctx.getUserPrincipal() == null) {
            name = "anonymous";
        } else if (!ctx.getUserPrincipal().getName().equals(jwt.getName())) {
            throw new InternalServerErrorException("Principal and JsonWebToken names do not match");
        } else {
            name = ctx.getUserPrincipal().getName();
        }
        return String.format("hello %s,"
            + " isHttps: %s,"
            + " authScheme: %s,"
            + " hasJWT: %s",
            name, ctx.isSecure(), ctx.getAuthenticationScheme(), hasJwt());
    }

    private boolean hasJwt() {
        return jwt.getClaimNames() != null;
    }
}
1 注入 JsonWebToken 以访问 JWT 中的声明。
2 此端点暴露在 /secured/roles-allowed
3 @RolesAllowed 注解将访问限制为具有 "User" 或 "Admin" 角色的用户。
4 响应的构造与 hello 方法类似,此外还添加了直接从注入的 JsonWebToken 中检索的 birthdate 声明。

在将此添加到 TokenSecuredResource 后,请重新运行 ./mvnw quarkus:dev 命令,然后尝试 curl -v http://127.0.0.1:8080/secured/roles-allowed; echo 来尝试访问新端点。

您的输出应如下所示

curl 命令用于 /secured/roles-allowed
$ curl -v http://127.0.0.1:8080/secured/roles-allowed; echo

此命令返回以下响应

*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET /secured/roles-allowed HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< Connection: keep-alive
< Content-Type: text/html;charset=UTF-8
< Content-Length: 14
< Date: Sun, 03 Mar 2019 16:32:34 GMT
<
* Connection #0 to host 127.0.0.1 left intact

太好了。您没有在请求中提供 JWT,因此正确拒绝了对该端点的访问。相反,您收到了 HTTP 401 Unauthorized 错误。

要访问该端点,您必须获取一个有效的 JWT 并将其包含在您的请求中。这涉及两个步骤

  1. 使用必要的 JWT 验证信息配置 SmallRye JWT 扩展。

  2. 生成一个具有适当声明的 JWT 以匹配配置。

配置 SmallRye JWT 扩展安全信息

创建 security-jwt-quickstart/src/main/resources/application.properties,内容如下

TokenSecuredResource 的应用程序属性
mp.jwt.verify.publickey.location=publicKey.pem (1)
mp.jwt.verify.issuer=https://example.com/issuer (2)

quarkus.native.resources.includes=publicKey.pem (3)
1 指定类路径上公钥文件 publicKey.pem 的位置。有关添加此密钥,请参阅 添加公钥
2 将预期的发布者定义为 https://example.com/issuer
3 确保 publicKey.pem 文件作为资源包含在原生可执行文件中。

添加公钥

JWT 规范定义了 JWT 的各种安全级别。MicroProfile JWT RBAC 规范要求使用 RSA-256 签名算法签名的 JWT。这反过来又需要 RSA 公钥对。在 REST 端点服务器端,您需要配置用于验证请求中发送的 JWT 的 RSA 公钥的位置。先前配置的 mp.jwt.verify.publickey.location=publicKey.pem 设置期望公钥作为 publicKey.pem 在类路径上可用。为此,请将以下内容复制到 security-jwt-quickstart/src/main/resources/publicKey.pem 文件中。

RSA 公钥 PEM 内容
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlivFI8qB4D0y2jy0CfEq
Fyy46R0o7S8TKpsx5xbHKoU1VWg6QkQm+ntyIv1p4kE1sPEQO73+HY8+Bzs75XwR
TYL1BmR1w8J5hmjVWjc6R2BTBGAYRPFRhor3kpM6ni2SPmNNhurEAHw7TaqszP5e
UF/F9+KEBWkwVta+PZ37bwqSE4sCb1soZFrVz/UT/LF4tYpuVYt3YbqToZ3pZOZ9
AX2o1GCG3xwOjkc4x0W7ezbQZdC9iftPxVHR8irOijJRRjcPDtA6vPKpzLl6CyYn
sIYPd99ltwxTHjr3npfv/3Lw50bAkbT4HeLFxTx4flEoZLKO/g0bAoV2uqBhkA9x
nQIDAQAB
-----END PUBLIC KEY-----

生成 JWT

通常,JWT 从身份管理器(如 Keycloak)获取。但对于这个快速入门,我们将使用 smallrye-jwt 提供的 JWT 生成 API 来生成我们自己的 JWT。有关更多信息,请参阅 使用 SmallRye JWT 生成 JWT 令牌

将以下列表中的代码放在 security-jwt-quickstart/src/test/java/org/acme/security/jwt/GenerateToken.java

GenerateToken 主驱动类
package org.acme.security.jwt;

import java.util.Arrays;
import java.util.HashSet;

import org.eclipse.microprofile.jwt.Claims;
import io.smallrye.jwt.build.Jwt;

/**
 * A utility class to generate and print a JWT token string to stdout.
 */
public class GenerateToken {

    /**
     * Generates and prints a JWT token.
     */
    public static void main(String[] args) {
        String token = Jwt.issuer("https://example.com/issuer") (1)
                .upn("jdoe@quarkus.io") (2)
                .groups(new HashSet<>(Arrays.asList("User", "Admin"))) (3)
                .claim(Claims.birthdate.name(), "2001-07-13") (4)
                .sign();

        System.out.println(token);
        System.exit(0);
    }
}
1 在 JWT 中设置 iss (issuer) 声明。此值必须与服务器端的 mp.jwt.verify.issuer 配置匹配,令牌才被视为有效。
2 指定 upn (User Principal Name) 声明,MicroProfile JWT RBAC 规范将其定义为在容器安全 API 中标识 Principal 的首选声明。
3 定义 groups 声明,它提供分配给 JWT Bearer 的组成员资格和顶级角色。
4 添加一个 birthdate 声明。由于这可能被视为敏感信息,请考虑按照 使用 SmallRye JWT 生成 JWT 令牌 中的描述加密声明。

请注意,要使此代码正常工作,您需要与 TokenSecuredResource 应用程序中的公钥对应的 RSA 私钥的内容。将以下 PEM 内容复制到 security-jwt-quickstart/src/test/resources/privateKey.pem

RSA 私钥 PEM 内容
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCWK8UjyoHgPTLa
PLQJ8SoXLLjpHSjtLxMqmzHnFscqhTVVaDpCRCb6e3Ii/WniQTWw8RA7vf4djz4H
OzvlfBFNgvUGZHXDwnmGaNVaNzpHYFMEYBhE8VGGiveSkzqeLZI+Y02G6sQAfDtN
qqzM/l5QX8X34oQFaTBW1r49nftvCpITiwJvWyhkWtXP9RP8sXi1im5Vi3dhupOh
nelk5n0BfajUYIbfHA6ORzjHRbt7NtBl0L2J+0/FUdHyKs6KMlFGNw8O0Dq88qnM
uXoLJiewhg9332W3DFMeOveel+//cvDnRsCRtPgd4sXFPHh+UShkso7+DRsChXa6
oGGQD3GdAgMBAAECggEAAjfTSZwMHwvIXIDZB+yP+pemg4ryt84iMlbofclQV8hv
6TsI4UGwcbKxFOM5VSYxbNOisb80qasb929gixsyBjsQ8284bhPJR7r0q8h1C+jY
URA6S4pk8d/LmFakXwG9Tz6YPo3pJziuh48lzkFTk0xW2Dp4SLwtAptZY/+ZXyJ6
96QXDrZKSSM99Jh9s7a0ST66WoxSS0UC51ak+Keb0KJ1jz4bIJ2C3r4rYlSu4hHB
Y73GfkWORtQuyUDa9yDOem0/z0nr6pp+pBSXPLHADsqvZiIhxD/O0Xk5I6/zVHB3
zuoQqLERk0WvA8FXz2o8AYwcQRY2g30eX9kU4uDQAQKBgQDmf7KGImUGitsEPepF
KH5yLWYWqghHx6wfV+fdbBxoqn9WlwcQ7JbynIiVx8MX8/1lLCCe8v41ypu/eLtP
iY1ev2IKdrUStvYRSsFigRkuPHUo1ajsGHQd+ucTDf58mn7kRLW1JGMeGxo/t32B
m96Af6AiPWPEJuVfgGV0iwg+HQKBgQCmyPzL9M2rhYZn1AozRUguvlpmJHU2DpqS
34Q+7x2Ghf7MgBUhqE0t3FAOxEC7IYBwHmeYOvFR8ZkVRKNF4gbnF9RtLdz0DMEG
5qsMnvJUSQbNB1yVjUCnDAtElqiFRlQ/k0LgYkjKDY7LfciZl9uJRl0OSYeX/qG2
tRW09tOpgQKBgBSGkpM3RN/MRayfBtmZvYjVWh3yjkI2GbHA1jj1g6IebLB9SnfL
WbXJErCj1U+wvoPf5hfBc7m+jRgD3Eo86YXibQyZfY5pFIh9q7Ll5CQl5hj4zc4Y
b16sFR+xQ1Q9Pcd+BuBWmSz5JOE/qcF869dthgkGhnfVLt/OQzqZluZRAoGAXQ09
nT0TkmKIvlza5Af/YbTqEpq8mlBDhTYXPlWCD4+qvMWpBII1rSSBtftgcgca9XLB
MXmRMbqtQeRtg4u7dishZVh1MeP7vbHsNLppUQT9Ol6lFPsd2xUpJDc6BkFat62d
Xjr3iWNPC9E9nhPPdCNBv7reX7q81obpeXFMXgECgYEAmk2Qlus3OV0tfoNRqNpe
Mb0teduf2+h3xaI1XDIzPVtZF35ELY/RkAHlmWRT4PCdR0zXDidE67L6XdJyecSt
FdOUH8z5qUraVVebRFvJqf/oGsXc4+ex1ZKUTbY0wqY1y9E39yvB3MaTmZFuuqk8
f3cg+fr8aou7pr9SHhJlZCU=
-----END PRIVATE KEY-----

稍后,您将配置 smallrye.jwt.sign.key.location 属性来指定私钥签名位置。

使用 OpenSSL 生成密钥

也可以使用 OpenSSL 命令行工具生成公钥和私钥对。

openssl 命令生成密钥
openssl genrsa -out rsaPrivateKey.pem 2048
openssl rsa -pubout -in rsaPrivateKey.pem -out publicKey.pem

需要一个额外的步骤来生成并将私钥转换为 PKCS#8 格式,这通常用于安全密钥存储和传输。

openssl 命令执行转换
openssl pkcs8 -topk8 -nocrypt -inform pem -in rsaPrivateKey.pem -outform pem -out privateKey.pem

您可以使用生成的密钥对而不是此快速入门中使用的密钥对。

在生成 TokenSecuredResource 端点的 JSON Web Token (JWT) 之前,请确保 应用程序正在运行

接下来,使用以下命令生成 JWT

示例 JWT 生成输出
$ mvn exec:java -Dexec.mainClass=org.acme.security.jwt.GenerateToken -Dexec.classpathScope=test -Dsmallrye.jwt.sign.key.location=privateKey.pem

JWT 字符串是一个 Base64 URL 编码的字符串,包含三个部分,由 . 字符分隔

  1. 头部,包含有关令牌的元数据,例如签名算法。

  2. 载荷,也称为“声明”,包括令牌的声明或数据。

  3. 签名,用于验证令牌的完整性。

最后,安全访问 /secured/roles-allowed

现在,让我们使用它来向 /secured/roles-allowed 端点发出安全请求。确保 Quarkus 服务器仍在开发模式下运行,然后运行以下命令,确保使用上一步生成的 JWT 的版本

带 JWT 的 /secured/roles-allowedcurl 命令
$ curl -H "Authorization: Bearer eyJraWQ..." http://127.0.0.1:8080/secured/roles-allowed; echo

请务必使用生成的令牌作为 HTTP Authorization Bearer 方案的值。

此命令返回以下响应

hello jdoe@quarkus.io, isHttps: false, authScheme: Bearer, hasJWT: true, birthdate: 2001-07-13

成功!您现在拥有以下内容

  • 一个非匿名的调用者名称:jdoe@quarkus.io

  • 一个身份验证方案:Bearer

  • 一个非 null 的 JsonWebToken

  • birthdate 声明值

使用 JsonWebToken 和声明注入

既然您可以生成 JWT 来访问我们安全的 REST 端点,那么让我们看看您还可以使用 JsonWebToken 接口和 JWT 声明做些什么。org.eclipse.microprofile.jwt.JsonWebToken 接口扩展了 java.security.Principal 接口,并且是您之前使用的 jakarta.ws.rs.core.SecurityContext#getUserPrincipal() 调用返回的对象类型。这意味着不使用 CDI 但可以访问 REST 容器 SecurityContext 的代码可以通过强制转换 SecurityContext#getUserPrincipal() 来获取调用者的 JsonWebToken 接口。

JsonWebToken 接口定义了用于访问底层 JWT 中声明的方法。它提供了 MicroProfile JWT RBAC 规范所需通用声明以及 JWT 中可能存在的任意声明的访问器。

所有 JWT 声明也可以被注入。让我们通过另一个端点 /secured/roles-allowed-admin 来扩展我们的 TokenSecuredResource,该端点使用注入的 birthdate 声明(而不是从 JsonWebToken 获取)

package org.acme.security.jwt;

import jakarta.annotation.security.PermitAll;
import jakarta.annotation.security.RolesAllowed;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.InternalServerErrorException;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.SecurityContext;

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

@Path("/secured")
@RequestScoped (1)
public class TokenSecuredResource {

    @Inject
    JsonWebToken jwt; (2)
    @Inject
    @Claim(standard = Claims.birthdate)
    String birthdate; (3)

    @GET
    @Path("permit-all")
    @PermitAll
    @Produces(MediaType.TEXT_PLAIN)
    public String hello(@Context SecurityContext ctx) {
        return getResponseString(ctx);
    }

    @GET
    @Path("roles-allowed")
    @RolesAllowed({ "User", "Admin" })
    @Produces(MediaType.TEXT_PLAIN)
    public String helloRolesAllowed(@Context SecurityContext ctx) {
        return getResponseString(ctx) + ", birthdate: " + jwt.getClaim("birthdate").toString();
    }

    @GET
    @Path("roles-allowed-admin")
    @RolesAllowed("Admin")
    @Produces(MediaType.TEXT_PLAIN)
    public String helloRolesAllowedAdmin(@Context SecurityContext ctx) {
        return getResponseString(ctx) + ", birthdate: " + birthdate; (4)
    }

    private String getResponseString(SecurityContext ctx) {
        String name;
        if (ctx.getUserPrincipal() == null) {
            name = "anonymous";
        } else if (!ctx.getUserPrincipal().getName().equals(jwt.getName())) {
            throw new InternalServerErrorException("Principal and JsonWebToken names do not match");
        } else {
            name = ctx.getUserPrincipal().getName();
        }
        return String.format("hello %s,"
            + " isHttps: %s,"
            + " authScheme: %s,"
            + " hasJWT: %s",
            name, ctx.isSecure(), ctx.getAuthenticationScheme(), hasJwt());
    }

    private boolean hasJwt() {
        return jwt.getClaimNames() != null;
    }
}
1 @RequestScoped 范围是必需的,以便将 birthdate 声明作为 String 注入。
2 在此处注入 JsonWebToken,提供对所有声明和 JWT 相关信息的访问。
3 birthdate 声明作为 String 注入。这说明了为什么 @RequestScoped 范围是强制性的。
4 注入的 birthdate 声明直接用于构建响应。

现在再次生成令牌并运行

$ curl -H "Authorization: Bearer eyJraWQ..." http://127.0.0.1:8080/secured/roles-allowed-admin; echo

请务必使用生成的令牌作为 HTTP Authorization Bearer 方案的值。

此命令返回以下响应

hello jdoe@quarkus.io, isHttps: false, authScheme: Bearer, hasJWT: true, birthdate: 2001-07-13

以 JVM 模式运行应用程序

您可以将应用程序作为标准 Java 应用程序运行。

  1. 编译应用程序

    CLI
    quarkus build
    Maven
    ./mvnw install
    Gradle
    ./gradlew build
  2. 运行应用程序

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

在原生模式下运行应用程序

您可以在不进行任何修改的情况下将相同的演示编译为原生模式。这意味着您不再需要在生产环境中安装 JVM。运行时技术包含在生成的二进制文件中,并经过优化,以最少的资源运行。

编译需要一段时间,因此默认情况下禁用此步骤。

  1. 通过启用 native 配置文件再次构建您的应用程序

    CLI
    quarkus build --native
    Maven
    ./mvnw install -Dnative
    Gradle
    ./gradlew build -Dquarkus.native.enabled=true
  2. 直接运行以下二进制文件

    ./target/security-jwt-quickstart-1.0.0-SNAPSHOT-runner

探索解决方案

security-jwt-quickstart 目录存储库包含本快速入门指南中涵盖的所有版本,以及演示使用注入的 JsonWebToken 令牌及其通过 CDI API 的声明的子端点。

我们鼓励您探索 security-jwt-quickstart 目录并查看快速入门解决方案,以了解更多关于 SmallRye JWT 扩展的功能。

参考指南

支持的注入范围

当注入 org.eclipse.microprofile.jwt.JsonWebToken 时,支持 @ApplicationScoped@Singleton@RequestScoped 外部 bean 注入范围,并强制执行 @RequestScoped 范围来确保代表当前令牌。

但是,当单个令牌声明作为简单类型(如 String)注入时,必须使用 @RequestScoped,例如

package org.acme.security.jwt;

import jakarta.inject.Inject;
import org.eclipse.microprofile.jwt.Claim;
import org.eclipse.microprofile.jwt.Claims;

@Path("/secured")
@RequestScoped
public class TokenSecuredResource {

    @Inject
    @Claim(standard = Claims.birthdate)
    String birthdate;
}

请注意,您也可以使用注入的 JsonWebToken 来访问单个声明,但在这种情况下,设置 @RequestScoped 是不必要的。

有关更多详细信息,请参阅 MP JWT CDI 注入要求

支持的公钥格式

公钥可以采用以下任何格式,按优先级顺序排列

  • 公钥加密标准 #8 (PKCS#8) PEM

  • JSON Web Key (JWK)

  • JSON Web Key Set (JWKS)

  • JSON Web Key (JWK) Base64 URL 编码

  • JSON Web Key Set (JWKS) Base64 URL 编码

处理验证密钥

如果您需要使用非对称 RSA 或椭圆曲线 (EC) 密钥来验证令牌签名,请使用 mp.jwt.verify.publickey.location 属性来引用本地或远程验证密钥。

使用 mp.jwt.verify.publickey.algorithm 来自定义验证算法(默认为 RS256);例如,在使用 EC 密钥时将其设置为 ES256

如果您需要使用对称密钥来验证令牌签名,那么必须使用 JSON Web Key (JWK) 或 JSON Web Key Set (JWK Set) 格式来表示此密钥,例如

{
 "keys": [
   {
     "kty":"oct",
     "kid":"secretKey",
     "k":"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow"
   }
 ]
}

此密钥 JWK 也必须使用 smallrye.jwt.verify.key.location 来引用。smallrye.jwt.verify.algorithm 应设置为 HS256/HS384/HS512

使用 JWTParser 解析和验证 JsonWebToken

如果无法注入 JWT 令牌,例如,如果它嵌入在服务请求负载中,或者服务终结点通过带外方式获取它,那么可以使用 JWTParser

import org.eclipse.microprofile.jwt.JsonWebToken;
import io.smallrye.jwt.auth.principal.JWTParser;
...
@Inject JWTParser parser;

String token = getTokenFromOidcServer();

// Parse and verify the token
JsonWebToken jwt = parser.parse(token);

您还可以使用它来定制令牌的验证或解密方式。例如,可以提供一个本地 SecretKey

package org.acme.security.jwt;

import io.smallrye.jwt.auth.principal.ParseException;
import jakarta.inject.Inject;
import jakarta.ws.rs.CookieParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.NewCookie;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.jwt.JsonWebToken;
import io.smallrye.jwt.auth.principal.JWTParser;
import io.smallrye.jwt.build.Jwt;

@Path("/secured")
public class SecuredResource {
    private static final String SECRET = "AyM1SysPpbyDfgZld3umj1qzKObwVMko";

    @Inject
    JWTParser parser;

    @GET
    @Produces("text/plain")
    public Response getUserName(@CookieParam("jwt") String jwtCookie) throws ParseException {
        if (jwtCookie == null) {
            // Create a JWT token signed by using the 'HS256' algorithm
            String newJwtCookie = Jwt.upn("Alice").signWithSecret(SECRET);
            // or create a JWT token encrypted by using the 'A256KW' algorithm
            // Jwt.upn("alice").encryptWithSecret(secret);
            return Response.ok("Alice").cookie(new NewCookie("jwt", newJwtCookie)).build();
        } else {
            // All mp.jwt and smallrye.jwt properties are still effective; only the verification key is customized.
            JsonWebToken jwt = parser.verify(jwtCookie, SECRET);
            // or jwt = parser.decrypt(jwtCookie, secret);
            return Response.ok(jwt.getName()).build();
        }
    }
}

另请参阅 如何直接添加 SmallRye JWT 部分,了解如何在不使用 quarkus-smallrye-jwt 提供的 HTTP 支持的情况下使用 JWTParser

令牌解密

如果您的应用程序需要接受带有加密声明或加密内部签名声明的令牌,只需将 smallrye.jwt.decrypt.key.location 属性设置为指向解密密钥。

如果这是唯一设置的密钥属性,则传入的令牌应仅包含加密声明。如果还设置了 mp.jwt.verify.publickeymp.jwt.verify.publickey.location 验证属性,则传入的令牌应包含加密的内部签名令牌。

请参阅 使用 SmallRye JWT 生成 JWT 令牌,了解如何快速生成加密的或内部签名然后加密的令牌。

自定义工厂

io.smallrye.jwt.auth.principal.DefaultJWTCallerPrincipalFactory 是用于解析和验证 JWT 令牌,将其转换为 JsonWebToken principal 的默认实现。此工厂依赖于 MP JWTsmallrye-jwt 属性,如“配置”部分所述,用于验证和自定义 JWT 令牌。

如果您需要实现自定义工厂(例如,跳过已由防火墙验证的令牌的重新验证),可以通过以下方式之一进行操作

  • 使用 ServiceLoader 机制,通过创建 META-INF/services/io.smallrye.jwt.auth.principal.JWTCallerPrincipalFactory 资源。

  • 提供一个 Alternative CDI bean 实现,例如下面的示例

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import jakarta.annotation.Priority;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Alternative;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.consumer.InvalidJwtException;
import io.smallrye.jwt.auth.principal.DefaultJWTCallerPrincipal;
import io.smallrye.jwt.auth.principal.JWTAuthContextInfo;
import io.smallrye.jwt.auth.principal.JWTCallerPrincipal;
import io.smallrye.jwt.auth.principal.JWTCallerPrincipalFactory;
import io.smallrye.jwt.auth.principal.ParseException;

@ApplicationScoped
@Alternative
@Priority(1)
public class TestJWTCallerPrincipalFactory extends JWTCallerPrincipalFactory {

    @Override
    public JWTCallerPrincipal parse(String token, JWTAuthContextInfo authContextInfo) throws ParseException {
        try {
            // Token has already been verified; parse the token claims only
            String json = new String(Base64.getUrlDecoder().decode(token.split("\\.")[1]), StandardCharsets.UTF_8);
            return new DefaultJWTCallerPrincipal(JwtClaims.parse(json));
        } catch (InvalidJwtException ex) {
            throw new ParseException(ex.getMessage());
        }
    }
}

阻塞调用

quarkus-smallrye-jwt 扩展使用 SmallRye JWT 库,该库目前不是响应式的。

quarkus-smallrye-jwt 的角度来看,它作为反应式 Quarkus 安全架构的一部分运行,这意味着进入 SmallRye JWT 验证或解密代码的 IO 线程可能在以下任一情况下被阻塞

  • 默认密钥解析器刷新包含密钥的 JsonWebKey 集合,这涉及对 OIDC 端点的远程调用。

  • 自定义密钥解析器,例如 AWS Application Load Balancer (ALB) 密钥解析器,通过使用当前令牌的密钥标识符标头值,针对 AWS ALB 密钥端点解析密钥。

在这些情况下,如果连接速度慢(例如,响应密钥端点需要超过 3 秒),则当前的事件循环线程很可能会被阻塞。

为防止阻塞,请设置 quarkus.smallrye-jwt.blocking-authentication=true

令牌传播

有关 Bearer 访问令牌传播到下游服务的信息,请参阅 令牌传播 部分。

测试

自动密钥生成

如果未配置验证密钥,此扩展将在开发和测试模式下生成一个非对称 RSA 2024 位签名密钥对。生成密钥对后,将使用 RSA 公钥配置 mp.jwt.verify.publickey 属性,并且 RSA 私钥可用于测试,例如使用 smallrye-jwt-build 对令牌进行签名

import io.smallrye.jwt.build.Jwt;
import jakarta.ws.rs.GET;

@GET
public String token() {
   return Jwt.upn("Alice").sign();
}

有关详细模式,请参阅 签名声明指南

您可以通过设置以下至少一个属性来禁用自动密钥生成

  • mp.jwt.verify.publickey.location

  • mp.jwt.verify.publickey

  • mp.jwt.decrypt.key.location

  • smallrye.jwt.encrypt.key.location

  • smallrye.jwt.sign.key.location

  • smallrye.jwt.sign.key

开发 模式下,如果您不使用 mp.jwt.verify.issuer 属性显式配置发布者,SmallRye JWT 扩展将默认发布者设置为 https://quarkus.net.cn/issuer

如果您希望以编程方式配置发布者,请将 mp.jwt.verify.issuer 设置为 NONE,以防止应用默认值。

Wiremock

如果您将 mp.jwt.verify.publickey.location 配置为指向基于 HTTPS 或 HTTP 的 JsonWebKey (JWK) 集合,那么您可以使用与 OpenID Connect Bearer 令牌集成测试 Wiremock 部分中描述的相同方法,但只需将 application.properties 更改为使用 MP JWT 配置属性即可。

# keycloak.url is set by OidcWiremockTestResource
mp.jwt.verify.publickey.location=${keycloak.url}/realms/quarkus/protocol/openid-connect/certs
mp.jwt.verify.issuer=${keycloak.url}/realms/quarkus

Keycloak

如果您使用 Keycloak 并将 mp.jwt.verify.publickey.location 配置为指向基于 HTTPS 或 HTTP 的 JsonWebKey (JWK) 集合,那么您可以使用与 OpenID Connect Bearer 令牌集成测试 Keycloak 部分中描述的相同方法,但只需将 application.properties 更改为使用 MP JWT 配置属性即可。

# keycloak.url is set by DevServices for Keycloak
mp.jwt.verify.publickey.location=${keycloak.url}/realms/quarkus/protocol/openid-connect/certs
mp.jwt.verify.issuer=${keycloak.url}/realms/quarkus

请注意,Keycloak 发行的令牌具有设置为领域端点地址的 iss (issuer) 声明。

如果您的 Quarkus 应用程序在 Docker 容器中运行,它可能与为 Keycloak 启动的 DevServices 启动的 Keycloak 容器共享网络接口。在这种情况下,Quarkus 应用程序和 Keycloak 通过内部共享的 Docker 网络进行通信。

在这种情况下,请使用以下配置代替

# keycloak.url is set by DevServices for Keycloak,
# Quarkus accesses it through an internal shared docker network interface.
mp.jwt.verify.publickey.location=${keycloak.url}/realms/quarkus/protocol/openid-connect/certs

# Issuer is set to the docker bridge localhost endpoint address represented by the `client.quarkus.oidc.auth-server-url` property
mp.jwt.verify.issuer=${client.quarkus.oidc.auth-server-url}

本地公钥

您可以采用与 OpenID Connect Bearer 令牌集成测试 本地公钥 部分中描述的相同方法,但只需将 application.properties 更改为使用 MP JWT 配置属性即可。

mp.jwt.verify.publickey=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlivFI8qB4D0y2jy0CfEqFyy46R0o7S8TKpsx5xbHKoU1VWg6QkQm+ntyIv1p4kE1sPEQO73+HY8+Bzs75XwRTYL1BmR1w8J5hmjVWjc6R2BTBGAYRPFRhor3kpM6ni2SPmNNhurEAHw7TaqszP5eUF/F9+KEBWkwVta+PZ37bwqSE4sCb1soZFrVz/UT/LF4tYpuVYt3YbqToZ3pZOZ9AX2o1GCG3xwOjkc4x0W7ezbQZdC9iftPxVHR8irOijJRRjcPDtA6vPKpzLl6CyYnsIYPd99ltwxTHjr3npfv/3Lw50bAkbT4HeLFxTx4flEoZLKO/g0bAoV2uqBhkA9xnQIDAQAB
# set it to the issuer value which is used to generate the tokens
mp.jwt.verify.issuer=${keycloak.url}/realms/quarkus

# required to sign the tokens
smallrye.jwt.sign.key.location=privateKey.pem

TestSecurity 注解

添加以下依赖项

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-test-security-jwt</artifactId>
    <scope>test</scope>
</dependency>
build.gradle
testImplementation("io.quarkus:quarkus-test-security-jwt")

然后,编写如下测试代码

import static org.hamcrest.Matchers.is;
import org.junit.jupiter.api.Test;
import io.quarkus.test.common.http.TestHTTPEndpoint;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import io.quarkus.test.security.jwt.Claim;
import io.quarkus.test.security.jwt.JwtSecurity;
import io.restassured.RestAssured;

@QuarkusTest
@TestHTTPEndpoint(ProtectedResource.class)
public class TestSecurityAuthTest {

    @Test
    @TestSecurity(user = "userJwt", roles = "viewer")
    public void testJwt() {
        RestAssured.when().get("test-security-jwt").then()
                .body(is("userJwt:viewer"));
    }

    @Test
    @TestSecurity(user = "userJwt", roles = "viewer")
    @JwtSecurity(claims = {
            @Claim(key = "email", value = "user@gmail.com")
    })
    public void testJwtWithClaims() {
        RestAssured.when().get("test-security-jwt-claims").then()
                .body(is("userJwt:viewer:user@gmail.com"));
    }

}

其中 ProtectedResource 类可能如下所示

@Path("/web-app")
@Authenticated
public class ProtectedResource {

    @Inject
    JsonWebToken accessToken;

    @GET
    @Path("test-security-jwt")
    public String testSecurityOidc() {
        return accessToken.getName() + ":" + accessToken.getGroups().iterator().next();
    }

    @GET
    @Path("test-security-jwt-claims")
    public String testSecurityOidcUserInfoMetadata() {
        return accessToken.getName() + ":" + accessToken.getGroups().iterator().next()
                + ":" + accessToken.getClaim("email");
    }
}

请注意,@TestSecurity 注解必须始终使用,并且其 user 属性将作为 JsonWebToken.getName() 返回,roles 属性则作为 JsonWebToken.getGroups() 返回。@JwtSecurity 注解是可选的,可用于设置其他令牌声明。

@TestSecurity@JwtSecurity 可以组合成一个元注解,如下所示

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.METHOD })
    @TestSecurity(user = "userOidc", roles = "viewer")
    @OidcSecurity(introspectionRequired = true,
        introspection = {
            @TokenIntrospection(key = "email", value = "user@gmail.com")
        }
    )
    public @interface TestSecurityMetaAnnotation {

    }

这对于在多个测试方法中使用相同的安全设置特别有用。

如何检查日志中的错误

请启用 io.quarkus.smallrye.jwt.runtime.auth.MpJwtValidatorTRACE 级别日志记录,以查看有关令牌验证或解密错误的更多详细信息。

quarkus.log.category."io.quarkus.smallrye.jwt.runtime.auth.MpJwtValidator".level=TRACE
quarkus.log.category."io.quarkus.smallrye.jwt.runtime.auth.MpJwtValidator".min-level=TRACE

主动身份验证

如果您想在调用公共端点方法时跳过令牌验证,请禁用 主动身份验证

请注意,如果未执行令牌验证,则无法通过公共方法访问注入的 JsonWebToken

如何直接添加 SmallRye JWT

使用 JWTParser 解析和验证 JsonWebToken,请在以下情况下直接使用 smallrye-jwt 而不是 quarkus-smallrye-jwt

  • 您使用不支持 HTTP 的 Quarkus 扩展,例如 Quarkus GRPC

  • 您提供特定于扩展的 HTTP 支持,这与 quarkus-smallrye-jwtVert.x HTTP 提供的支持冲突,例如 Quarkus AWS Lambda

从添加 smallrye-jwt 依赖项开始

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

然后,更新 application.properties 以包含 smallrye-jwt 提供的所有 CDI producer,如下所示

quarkus.index-dependency.smallrye-jwt.group-id=io.smallrye
quarkus.index-dependency.smallrye-jwt.artifact-id=smallrye-jwt

配置参考

Quarkus 配置

构建时固定的配置属性 - 所有其他配置属性都可以在运行时覆盖

配置属性

类型

默认

MP-JWT 配置对象

环境变量:QUARKUS_SMALLRYE_JWT_ENABLED

显示更多

布尔值

true

支持 SHA256withRSA 签名的 java.security.Provider 的名称

环境变量:QUARKUS_SMALLRYE_JWT_RSA_SIG_PROVIDER

显示更多

字符串

SunRsaSign

如果获取远程密钥可能是一项耗时的操作,则启用此属性。如果使用本地密钥,请勿启用。

环境变量:QUARKUS_SMALLRYE_JWT_BLOCKING_AUTHENTICATION

显示更多

布尔值

false

始终创建 HTTP 401 挑战,即使对于不包含身份验证凭据的请求也是如此。JWT 身份验证机制在需要身份验证挑战时将返回 HTTP 401。但是,如果它与交互式身份验证机制之一一起使用,那么向从浏览器访问应用程序的用户返回 HTTP 401 可能是不理想的。如果您愿意,可以通过将此属性设置为“true”来请求 JWT 身份验证机制在这种情况下不创建挑战。

环境变量:QUARKUS_SMALLRYE_JWT_SILENT

显示更多

布尔值

false

MicroProfile JWT 配置

属性名称 默认 描述

mp.jwt.verify.publickey

none

mp.jwt.verify.publickey 配置属性允许将公钥文本作为字符串提供。公钥根据 支持的公钥格式 部分中定义的顺序从提供的字符串中解析。

mp.jwt.verify.publickey.location

none

配置属性允许指定公钥的外部或内部位置。该值可以是相对路径或 URL。如果该值指向基于 HTTPS 的 JWK 集合,那么为了在原生模式下工作,必须还将 quarkus.ssl.native 属性设置为 true。有关更多详细信息,请参阅 使用 SSL 和原生可执行文件

mp.jwt.verify.publickey.algorithm

RS256

签名算法列表。将其设置为 ES256 以支持椭圆曲线签名算法。

mp.jwt.decrypt.key.location

none

配置属性允许指定私钥解密的位置,可以是外部或内部。

mp.jwt.decrypt.key.algorithm

RSA-OAEP,RSA-OAEP-256

解密算法列表。将其设置为 RSA-OAEP-256 以仅支持带 SHA-256 的 RSA-OAEP。

mp.jwt.verify.issuer

none

配置属性指定服务器接受为有效的 JWT 的 iss (issuer) 声明的值。

mp.jwt.verify.audiences

none

令牌 aud 声明可能包含的受众的逗号分隔列表。

mp.jwt.verify.clock.skew

60

用于令牌过期和年龄验证的时钟偏差(以秒为单位)。如果当前时间在令牌过期时间之后的指定秒数之内,则接受已过期令牌。默认值为 60 秒。

mp.jwt.verify.token.age

none

令牌 iat (issued at) 时间必须经过的秒数。

mp.jwt.token.header

授权

如果使用其他标头(如 Cookie)来传递令牌,请设置此属性。

mp.jwt.token.cookie

none

包含令牌的 Cookie 名称。仅当 mp.jwt.token.header 设置为 Cookie 时,此属性才有效。

其他 SmallRye JWT 配置

SmallRye JWT 提供更多可用于自定义令牌处理的属性

属性名称 默认 描述

smallrye.jwt.verify.secretkey

none

作为字符串提供的密钥。

smallrye.jwt.verify.key.location

NONE

验证密钥的位置,可以指向公钥和私钥。私钥只能是 JWK 格式。请注意,如果设置了此属性,则会忽略 'mp.jwt.verify.publickey.location'。

smallrye.jwt.verify.algorithm

签名算法。此属性仅应用于设置对称算法(如 HS256)。对于设置非对称算法(如 ES256)已弃用 - 请改用 'mp.jwt.verify.publickey.algorithm'。

smallrye.jwt.verify.key-format

ANY

将此属性设置为特定密钥格式(如 PEM_KEYPEM_CERTIFICATEJWKJWK_BASE64URL)以优化验证密钥的加载方式。

smallrye.jwt.verify.key-provider

DEFAULT

默认情况下,PEM、JWK 或 JWK 密钥集可以从本地文件系统读取,也可以根据 MicroProfile JWT 规范的要求从 URI 获取。将此属性设置为 AWS_ALB 以支持 AWS Application Load Balancer 验证密钥解析。

smallrye.jwt.verify.relax-key-validation

false

放宽验证密钥的验证;将此属性设置为 true 允许长度小于 2048 位的公钥 RSA 密钥。

smallrye.jwt.verify.certificate-thumbprint

false

如果启用了此属性,则签名令牌必须包含 'x5t' 或 'x5t#S256' X509Certificate 指纹标头。验证密钥只能是 JWK 或 PEM 证书密钥格式。JWK 密钥必须设置 'x5c' (Base64 编码的 X509Certificate) 属性。

smallrye.jwt.token.header

授权

如果使用其他标头(如 Cookie)来传递令牌,请设置此属性。此属性已弃用 - 请使用 'mp.jwt.token.header'。

smallrye.jwt.key-cache-size

100

密钥缓存大小。当密钥提供程序(如 AWS_ALB)与 smallrye.jwt.verify.key-provider=AWS_ALB 一起配置以动态解析密钥时,使用此属性和 smallrye.jwt.key-cache-time-to-live 来控制密钥缓存。

smallrye.jwt.key-cache-time-to-live

10

密钥缓存条目的生存时间(分钟)。当密钥提供程序(如 AWS_ALB)与 smallrye.jwt.verify.key-provider=AWS_ALB 一起配置以动态解析密钥时,使用此属性和 smallrye.jwt.key-cache-size 来控制密钥缓存。

smallrye.jwt.token.cookie

none

包含令牌的 Cookie 名称。仅当 smallrye.jwt.token.header 设置为 Cookie 时,此属性才有效。此属性已弃用 - 请使用 mp.jwt.token.cookie

smallrye.jwt.always-check-authorization

false

将此属性设置为 true,即使 smallrye.jwt.token.header 设置为 Cookie 但不存在名称为 smallrye.jwt.token.cookie 的 Cookie,也会检查 Authorization 标头。

smallrye.jwt.token.schemes

Bearer

包含备用单个或多个方案(如 DPoP)的逗号分隔列表。

smallrye.jwt.token.kid

none

密钥标识符。如果设置了验证 JWK 密钥和每个 JWT 令牌,则必须具有匹配的 kid 标头。

smallrye.jwt.time-to-live

none

JWT 可以签发使用的最大秒数。实际上,JWT 的过期日期与签发日期之间的差值不得超过此值。将此属性设置为非正值会放宽令牌必须具有有效的 'iat' (issued at) 声明的要求。

smallrye.jwt.require.named-principal

true

如果应用程序依赖于 java.security.Principal 返回名称,则令牌必须设置 upnpreferred_usernamesub 声明。设置此属性会导致 SmallRye JWT 在无法可靠地处理非 null Principal 名称时抛出异常,如果缺少这些声明中的任何一个。

smallrye.jwt.path.sub

none

指向包含主题名称的声明的路径。它从顶级 JSON 对象开始,可以包含多个段,每个段仅代表一个 JSON 对象名称,例如:realms/subject。如果令牌没有 'sub' 声明但主题设置在不同的声明中,则可以使用此属性。对于命名空间限定的声明,请使用双引号。

smallrye.jwt.claims.sub

none

当当前令牌没有可用的标准或自定义 sub 声明时,此属性可以设置默认的 sub 声明值。实际上,如果未设置 upnpreferred_usernamesub 声明,则可以使用此属性来自定义 java.security.Principal 名称。

smallrye.jwt.path.groups

none

指向包含组的声明的路径。它从顶级 JSON 对象开始,可以包含多个段,每个段仅代表一个 JSON 对象名称,例如:realm/groups。如果令牌没有 'groups' 声明但组设置在不同的声明中,则可以使用此属性。对于命名空间限定的声明,请使用双引号。

smallrye.jwt.groups-separator

space

用于分割可能包含多个组值的字符串的分隔符。仅当 smallrye.jwt.path.groups 属性指向具有字符串值的自定义声明时才使用。默认值是单个空格,因为标准的 OAuth2 scope 声明可能包含空格分隔的序列。

smallrye.jwt.claims.groups

none

当当前令牌没有可用的标准或自定义组声明时,此属性可以设置默认的组声明值。

smallrye.jwt.jwks.refresh-interval

60

JWK 缓存刷新间隔(分钟)。除非 mp.jwt.verify.publickey.location 指向基于 HTTP 或 HTTPS 的 URL 的 JWK 集合,并且 JWK HTTPS 端点没有返回带有正 max-age 参数值的 HTTP Cache-Control 响应标头,否则将被忽略。

smallrye.jwt.jwks.forced-refresh-interval

30

强制 JWK 缓存刷新间隔(分钟),用于限制强制刷新尝试的频率,当由于缓存没有与当前令牌的 kid 标头匹配的 kid 属性的 JWK 密钥而导致令牌验证失败时,可能会发生这些尝试。除非 mp.jwt.verify.publickey.location 指向基于 HTTP 或 HTTPS 的 URL 的 JWK 集合,否则将被忽略。

smallrye.jwt.expiration.grace

0

过期宽限期(秒)。默认情况下,如果当前时间在令牌过期时间后不超过 1 分钟,则仍接受已过期令牌。此属性已弃用。请改用 mp.jwt.verify.clock.skew

smallrye.jwt.verify.aud

none

令牌 aud 声明可能包含的受众的逗号分隔列表。此属性已弃用 - 请使用 mp.jwt.verify.audiences

smallrye.jwt.required.claims

none

令牌必须包含的声明的逗号分隔列表。

smallrye.jwt.decrypt.key.location

none

用于指定私钥解密位置(外部或内部)的配置属性。此属性已弃用 - 请使用 mp.jwt.decrypt.key.location

smallrye.jwt.decrypt.algorithm

RSA_OAEP

解密算法。

smallrye.jwt.decrypt.key

none

作为字符串提供的解密密钥。

smallrye.jwt.token.decryption.kid

none

解密密钥标识符。如果设置了此属性,则解密 JWK 密钥和每个 JWT 令牌都必须具有匹配的 kid 标头。

smallrye.jwt.client.tls.certificate.path

none

TLS 受信任证书的路径,如果必须通过 HTTPS 获取密钥,则可能需要配置此路径。

smallrye.jwt.client.tls.trust-all

false

信任所有主机名。如果必须通过 HTTPS 获取密钥,并且此属性设置为 true,则默认信任所有主机名。

smallrye.jwt.client.tls.hosts

none

受信任的主机名集合。如果必须通过 HTTPS 获取密钥,并且 smallrye.jwt.client.tls.trust-all 设置为 false,则可以使用此属性配置受信任的主机名。

smallrye.jwt.http.proxy.host

none

HTTP 代理主机。

smallrye.jwt.http.proxy.port

80

HTTP 代理端口。

smallrye.jwt.keystore.type

JKS

如果 mp.jwt.verify.publickey.location 指向 KeyStore 文件,则可以使用此属性来自定义密钥库类型。如果未设置,则在默认为 JKS 之前会检查文件名以确定密钥库类型。

smallrye.jwt.keystore.provider

如果 mp.jwt.verify.publickey.locationmp.jwt.decrypt.key.location 指向 KeyStore 文件,则可以使用此属性来自定义 KeyStore 提供程序。

smallrye.jwt.keystore.password

密钥库密码。如果设置了 mp.jwt.verify.publickey.locationmp.jwt.decrypt.key.location,则必须设置此属性。

smallrye.jwt.keystore.verify.key.alias

如果 mp.jwt.verify.publickey.location 指向 KeyStore 文件,则必须设置此属性以标识从匹配证书的 KeyStore 中提取的公钥验证密钥。

smallrye.jwt.keystore.decrypt.key.alias

如果 mp.jwt.decrypt.key.location 指向 KeyStore 文件,则必须设置此属性以标识私钥解密密钥。

smallrye.jwt.keystore.decrypt.key.password

如果 mp.jwt.decrypt.key.location 指向 KeyStore 文件,并且私钥解密密钥在 KeyStore 中的密码与 smallrye.jwt.keystore.password 不同,则可以设置此属性。

smallrye.jwt.resolve-remote-keys-at-startup

false

将此属性设置为 true 以在应用程序启动时解析远程密钥。

相关内容