Payara Micro 在将 JWT 承载传递给受保护的 JAX-RS 资源时返回 AuthenticationStatus.SEND_FAILURE
Payara Micro returning AuthenticationStatus.SEND_FAILURE when passing a JWT bearer to a protected JAX-RS resource
我对 Java 编程比较陌生,所以请多多包涵。我正在开发要部署在 Payara Micro 上的微服务。为了安全起见,我使用 JWT。因为 Payara Micro 是 MicroProfile 的一个实现,我正在尝试利用 MicroProfile JWT 身份验证规范(Payara Micro 实现规范 1.1)。根据规范,我用 @LoginConfig
注释 JAX-RS 配置 class:
@ApplicationScoped
@LoginConfig(authMethod = "MP-JWT")
@DeclareRoles({"admin", "standard", "premium"})
@ApplicationPath("/*")
public class IdentityService extends Application {}
Payara Micro 实现了一个过滤器,可激活 JAX-RS 资源上的 @RolesAllowed
注释(通常专供 EJB 使用)。我正在利用此注释使用来自 MP JWT 身份验证规范的 JsonWebToken
接口来识别 JWT 的声明,以确定是否允许用户发出 GET 请求以根据声明中的 id 访问用户的信息:
@RolesAllowed({"admin", "standard", "premium"})
@RequestScoped
@Path("/users")
@Produces(MediaType.APPLICATION_JSON)
public class UserEndpoint {
@Inject
private JsonWebToken jwtPrincipal;
@Inject
private UserRepository userRepo;
@Path("/{id}")
@GET
public Response get(@PathParam("id") String id) {
if (!jwtPrincipal.getSubject().equalsIgnoreCase(id))
return Response.status(Response.Status.FORBIDDEN).build();
User user;
user = userRepo.get(UUID.fromString(id));
return Response.status(Response.Status.OK).entity(user).build();
}
此外,根据 Payara's MP JWT auth activation guide,我在我的 META-INF/microprofile-config.properties
文件中提供了以下配置属性供 Payara 验证:
mp.jwt.verify.issuer = com.someDomain
mp.jwt.verify.publickey.location = jwtPublic.pem
应该注意的是,我正在根据用户登录信息通过单独的 @PermitAll
端点出售我自己的 JWT。然而,这些似乎是正确构造的,因为它们可以很容易地调试,并且签名在 jwt.io 的调试器中得到验证(不提供验证密钥,因为问题已经足够长):
ew0KICAidHlwIiA6ICJKV1QiLA0KICAiYWxnIiA6ICJSUzI1NiINCn0=.ew0KICAiaXNzIiA6ICJjb20uc29tZURvbWFpbiIsDQogICJzdWIiIDogIjA5M2EzYzRlLWRiZTItNGI2YS05NzU4LTI5YjM2NjU1MTljOSIsDQogICJleHAiIDogMTU2NTM1Mjc2OCwNCiAgImlhdCIgOiAxNTY1MzA5NTY4LA0KICAidXBuIiA6ICJzb21lRW1haWxAZW1haWwuY29tIiwNCiAgImp0aSIgOiAiYmU0OTNhMTgtOGZiOS00NmM3LTgwMmEtNDY1YjFhYmM2YmZlIiwNCiAgImdyb3VwcyIgOiBbICJzdGFuZGFyZCIgXSwNCiAgInByZWZlcnJlZF91c2VybmFtZSIgOiAiY2FtZXJvbjIiDQp9.F97KMZbkPNuMATlYm0pLKalvadf_aAoxgPiNR-V-Y8toeYNqAm-w1WwnttQCeBuWvXjvPCm70y-HDGGv2tB-KuzHFxKxreyDue11db1fQ6o7QtYWSYvu_1y8bhOnB-MUz3MWnhRSHj8GpCQkrYXNACW9VSv8poqgyhyctEhi98LHGCFLyg1JNrzZw2J7fCdOYeoVZlgo-I9F4XA7FIXSQxgUii1T5RcGkky3tBKVUZ8jIigW68LXtYlq12lfo3PDATxou7c5ybMijLYuwPi7A6rb5bvsqAdsO-mSP6M6t42mUYGJuJ9dx_GmhWOpYzfgFaynMHElpuV58q7pXP5_JRvMx37yHMuA0Z_dj9ruSBqqH-lN0gV3CDheuHICbxAwqFvCEgVG7ZC4S3JNkSTqofifw_iXfTJX-v8cfWI3kfH7PrZmSmrMApGQLt5bQw_HIcIWfbHuA9_r-YksdIzJRsW-2JpSEHxRPeGvq5BlXlMSWu4BoefGcTbj6OKj6Gz_Zb5O8l8mOxQAT3wElN3DRxj3M_jxRM6kg-C-DlEFAxFivNQGbpE4CSKnLaY8XnTeL3j3Pq7tnYm1kG4PWeHCppr9CKgITjfrzzY3UNQX56WwDuRFTfgmY2Tnji4xqmLxxVCGE8ahM8mnxouIiwlPjlixkRXJfkxYb8YaQSMjIGw=
然而,@RolesAllowed
注释的行为似乎出乎意料。当我将 JWT 不记名令牌作为授权 HTTP header:
传递到端点时
curl -i --header "Authorization: Bearer ew0KICAidHlwIiA6ICJKV1QiLA0KICAiYWxnIiA6ICJSUzI1NiINCn0=.ew0KICAiaXNzIiA6ICJjb20uc29tZURvbWFpbiIsDQogICJzdWIiIDogIjA5M2EzYzRlLWRiZTItNGI2YS05NzU4LTI5YjM2NjU1MTljOSIsDQogICJleHAiIDogMTU2NTM1Mjc2OCwNCiAgImlhdCIgOiAxNTY1MzA5NTY4LA0KICAidXBuIiA6ICJzb21lRW1haWxAZW1haWwuY29tIiwNCiAgImp0aSIgOiAiYmU0OTNhMTgtOGZiOS00NmM3LTgwMmEtNDY1YjFhYmM2YmZlIiwNCiAgImdyb3VwcyIgOiBbICJzdGFuZGFyZCIgXSwNCiAgInByZWZlcnJlZF91c2VybmFtZSIgOiAiY2FtZXJvbjIiDQp9.F97KMZbkPNuMATlYm0pLKalvadf_aAoxgPiNR-V-Y8toeYNqAm-w1WwnttQCeBuWvXjvPCm70y-HDGGv2tB-KuzHFxKxreyDue11db1fQ6o7QtYWSYvu_1y8bhOnB-MUz3MWnhRSHj8GpCQkrYXNACW9VSv8poqgyhyctEhi98LHGCFLyg1JNrzZw2J7fCdOYeoVZlgo-I9F4XA7FIXSQxgUii1T5RcGkky3tBKVUZ8jIigW68LXtYlq12lfo3PDATxou7c5ybMijLYuwPi7A6rb5bvsqAdsO-mSP6M6t42mUYGJuJ9dx_GmhWOpYzfgFaynMHElpuV58q7pXP5_JRvMx37yHMuA0Z_dj9ruSBqqH-lN0gV3CDheuHICbxAwqFvCEgVG7ZC4S3JNkSTqofifw_iXfTJX-v8cfWI3kfH7PrZmSmrMApGQLt5bQw_HIcIWfbHuA9_r-YksdIzJRsW-2JpSEHxRPeGvq5BlXlMSWu4BoefGcTbj6OKj6Gz_Zb5O8l8mOxQAT3wElN3DRxj3M_jxRM6kg-C-DlEFAxFivNQGbpE4CSKnLaY8XnTeL3j3Pq7tnYm1kG4PWeHCppr9CKgITjfrzzY3UNQX56WwDuRFTfgmY2Tnji4xqmLxxVCGE8ahM8mnxouIiwlPjlixkRXJfkxYb8YaQSMjIGw=" "localhost:8080/users/093a3c4e-dbe2-4b6a-9758-29b3665519c9"
我收到以下回复:
HTTP/1.1 401 Unauthorized
Server: Payara Micro #badassfish
WWW-Authenticate: Authentication resulted in SEND_FAILURE
在深入研究 Payara 的源代码后,我发现收到了此响应 here。
显然,当从 SecurityContext
收到的 Principal
为空并且 JAX-RS 资源中存在 @RolesAllowed
注释时,就会发生这种情况。然后显然调用了 container-provided 身份验证模块,在 Payara 的情况下是 BASIC 身份验证:https://github.com/payara/Payara/issues/2326#issuecomment-367855133。但是,我不明白为什么在我指定 @LoginConfig(authMethod="MP-JWT")
时使用 BASIC 身份验证并且我提供 JWT 作为授权 header。 github 上有与此相关的问题,但我无法找到有关修复内容的明确答案。
所以,我的问题是,我是否错误地解释了如何让 Payara 为来电者生产可注射的 JsonWebToken
?我该如何修复此 SEND_FAILURE 响应?
对于可能遇到此问题的任何人,在查看 source code for Payara's SignedJWTIdentityStore, I realized that the META-INF/microprofile-config.properties
file isn't scanned by Payara to verify the issuer and public key. While Payara's guide to activating MP JWT Auth 后并没有明确说明这一点,您无法从该文件配置发行者或 public 密钥位置.要配置您接受的颁发者,您必须在 src/main/resources
目录中放置一个名为 payara-mp-jwt.properties
的文件,其中包含密钥 accepted.issuer
和预期值(例如 accepted.issuer = someDomain.com
)。此外,要配置您的 public 密钥,您必须将名为 publicKey.pem
的 public 密钥放在同一目录中。 Payara 将只扫描这些文件,不会扫描 META-INF/microprofile.config
您配置的 public 密钥和接受的发行者值:
public SignedJWTIdentityStore() {
config = ConfigProvider.getConfig();
Optional<Properties> properties = readVendorProperties();
acceptedIssuer = readVendorIssuer(properties)
.orElseGet(() -> config.getOptionalValue(ISSUER, String.class)
.orElseThrow(() -> new IllegalStateException("No issuer found")));
jwtTokenParser = new JwtTokenParser(readEnabledNamespace(properties), readCustomNamespace(properties));
}
private Optional<Properties> readVendorProperties() {
URL mpJwtResource = currentThread().getContextClassLoader().getResource("/payara-mp-jwt.properties");
Properties properties = null;
if (mpJwtResource != null) {
try {
properties = new Properties();
properties.load(mpJwtResource.openStream());
} catch (IOException e) {
throw new IllegalStateException("Failed to load Vendor properties from resource file", e);
}
}
return Optional.ofNullable(properties);
}
包括这些将导致 JsonWebToken
被正确注入。我不确定为什么 Payara 的文档在未扫描的情况下不厌其烦地包含其他文件配置选项。
我对 Java 编程比较陌生,所以请多多包涵。我正在开发要部署在 Payara Micro 上的微服务。为了安全起见,我使用 JWT。因为 Payara Micro 是 MicroProfile 的一个实现,我正在尝试利用 MicroProfile JWT 身份验证规范(Payara Micro 实现规范 1.1)。根据规范,我用 @LoginConfig
注释 JAX-RS 配置 class:
@ApplicationScoped
@LoginConfig(authMethod = "MP-JWT")
@DeclareRoles({"admin", "standard", "premium"})
@ApplicationPath("/*")
public class IdentityService extends Application {}
Payara Micro 实现了一个过滤器,可激活 JAX-RS 资源上的 @RolesAllowed
注释(通常专供 EJB 使用)。我正在利用此注释使用来自 MP JWT 身份验证规范的 JsonWebToken
接口来识别 JWT 的声明,以确定是否允许用户发出 GET 请求以根据声明中的 id 访问用户的信息:
@RolesAllowed({"admin", "standard", "premium"})
@RequestScoped
@Path("/users")
@Produces(MediaType.APPLICATION_JSON)
public class UserEndpoint {
@Inject
private JsonWebToken jwtPrincipal;
@Inject
private UserRepository userRepo;
@Path("/{id}")
@GET
public Response get(@PathParam("id") String id) {
if (!jwtPrincipal.getSubject().equalsIgnoreCase(id))
return Response.status(Response.Status.FORBIDDEN).build();
User user;
user = userRepo.get(UUID.fromString(id));
return Response.status(Response.Status.OK).entity(user).build();
}
此外,根据 Payara's MP JWT auth activation guide,我在我的 META-INF/microprofile-config.properties
文件中提供了以下配置属性供 Payara 验证:
mp.jwt.verify.issuer = com.someDomain
mp.jwt.verify.publickey.location = jwtPublic.pem
应该注意的是,我正在根据用户登录信息通过单独的 @PermitAll
端点出售我自己的 JWT。然而,这些似乎是正确构造的,因为它们可以很容易地调试,并且签名在 jwt.io 的调试器中得到验证(不提供验证密钥,因为问题已经足够长):
ew0KICAidHlwIiA6ICJKV1QiLA0KICAiYWxnIiA6ICJSUzI1NiINCn0=.ew0KICAiaXNzIiA6ICJjb20uc29tZURvbWFpbiIsDQogICJzdWIiIDogIjA5M2EzYzRlLWRiZTItNGI2YS05NzU4LTI5YjM2NjU1MTljOSIsDQogICJleHAiIDogMTU2NTM1Mjc2OCwNCiAgImlhdCIgOiAxNTY1MzA5NTY4LA0KICAidXBuIiA6ICJzb21lRW1haWxAZW1haWwuY29tIiwNCiAgImp0aSIgOiAiYmU0OTNhMTgtOGZiOS00NmM3LTgwMmEtNDY1YjFhYmM2YmZlIiwNCiAgImdyb3VwcyIgOiBbICJzdGFuZGFyZCIgXSwNCiAgInByZWZlcnJlZF91c2VybmFtZSIgOiAiY2FtZXJvbjIiDQp9.F97KMZbkPNuMATlYm0pLKalvadf_aAoxgPiNR-V-Y8toeYNqAm-w1WwnttQCeBuWvXjvPCm70y-HDGGv2tB-KuzHFxKxreyDue11db1fQ6o7QtYWSYvu_1y8bhOnB-MUz3MWnhRSHj8GpCQkrYXNACW9VSv8poqgyhyctEhi98LHGCFLyg1JNrzZw2J7fCdOYeoVZlgo-I9F4XA7FIXSQxgUii1T5RcGkky3tBKVUZ8jIigW68LXtYlq12lfo3PDATxou7c5ybMijLYuwPi7A6rb5bvsqAdsO-mSP6M6t42mUYGJuJ9dx_GmhWOpYzfgFaynMHElpuV58q7pXP5_JRvMx37yHMuA0Z_dj9ruSBqqH-lN0gV3CDheuHICbxAwqFvCEgVG7ZC4S3JNkSTqofifw_iXfTJX-v8cfWI3kfH7PrZmSmrMApGQLt5bQw_HIcIWfbHuA9_r-YksdIzJRsW-2JpSEHxRPeGvq5BlXlMSWu4BoefGcTbj6OKj6Gz_Zb5O8l8mOxQAT3wElN3DRxj3M_jxRM6kg-C-DlEFAxFivNQGbpE4CSKnLaY8XnTeL3j3Pq7tnYm1kG4PWeHCppr9CKgITjfrzzY3UNQX56WwDuRFTfgmY2Tnji4xqmLxxVCGE8ahM8mnxouIiwlPjlixkRXJfkxYb8YaQSMjIGw=
然而,@RolesAllowed
注释的行为似乎出乎意料。当我将 JWT 不记名令牌作为授权 HTTP header:
curl -i --header "Authorization: Bearer ew0KICAidHlwIiA6ICJKV1QiLA0KICAiYWxnIiA6ICJSUzI1NiINCn0=.ew0KICAiaXNzIiA6ICJjb20uc29tZURvbWFpbiIsDQogICJzdWIiIDogIjA5M2EzYzRlLWRiZTItNGI2YS05NzU4LTI5YjM2NjU1MTljOSIsDQogICJleHAiIDogMTU2NTM1Mjc2OCwNCiAgImlhdCIgOiAxNTY1MzA5NTY4LA0KICAidXBuIiA6ICJzb21lRW1haWxAZW1haWwuY29tIiwNCiAgImp0aSIgOiAiYmU0OTNhMTgtOGZiOS00NmM3LTgwMmEtNDY1YjFhYmM2YmZlIiwNCiAgImdyb3VwcyIgOiBbICJzdGFuZGFyZCIgXSwNCiAgInByZWZlcnJlZF91c2VybmFtZSIgOiAiY2FtZXJvbjIiDQp9.F97KMZbkPNuMATlYm0pLKalvadf_aAoxgPiNR-V-Y8toeYNqAm-w1WwnttQCeBuWvXjvPCm70y-HDGGv2tB-KuzHFxKxreyDue11db1fQ6o7QtYWSYvu_1y8bhOnB-MUz3MWnhRSHj8GpCQkrYXNACW9VSv8poqgyhyctEhi98LHGCFLyg1JNrzZw2J7fCdOYeoVZlgo-I9F4XA7FIXSQxgUii1T5RcGkky3tBKVUZ8jIigW68LXtYlq12lfo3PDATxou7c5ybMijLYuwPi7A6rb5bvsqAdsO-mSP6M6t42mUYGJuJ9dx_GmhWOpYzfgFaynMHElpuV58q7pXP5_JRvMx37yHMuA0Z_dj9ruSBqqH-lN0gV3CDheuHICbxAwqFvCEgVG7ZC4S3JNkSTqofifw_iXfTJX-v8cfWI3kfH7PrZmSmrMApGQLt5bQw_HIcIWfbHuA9_r-YksdIzJRsW-2JpSEHxRPeGvq5BlXlMSWu4BoefGcTbj6OKj6Gz_Zb5O8l8mOxQAT3wElN3DRxj3M_jxRM6kg-C-DlEFAxFivNQGbpE4CSKnLaY8XnTeL3j3Pq7tnYm1kG4PWeHCppr9CKgITjfrzzY3UNQX56WwDuRFTfgmY2Tnji4xqmLxxVCGE8ahM8mnxouIiwlPjlixkRXJfkxYb8YaQSMjIGw=" "localhost:8080/users/093a3c4e-dbe2-4b6a-9758-29b3665519c9"
我收到以下回复:
HTTP/1.1 401 Unauthorized
Server: Payara Micro #badassfish
WWW-Authenticate: Authentication resulted in SEND_FAILURE
在深入研究 Payara 的源代码后,我发现收到了此响应 here。
显然,当从 SecurityContext
收到的 Principal
为空并且 JAX-RS 资源中存在 @RolesAllowed
注释时,就会发生这种情况。然后显然调用了 container-provided 身份验证模块,在 Payara 的情况下是 BASIC 身份验证:https://github.com/payara/Payara/issues/2326#issuecomment-367855133。但是,我不明白为什么在我指定 @LoginConfig(authMethod="MP-JWT")
时使用 BASIC 身份验证并且我提供 JWT 作为授权 header。 github 上有与此相关的问题,但我无法找到有关修复内容的明确答案。
所以,我的问题是,我是否错误地解释了如何让 Payara 为来电者生产可注射的 JsonWebToken
?我该如何修复此 SEND_FAILURE 响应?
对于可能遇到此问题的任何人,在查看 source code for Payara's SignedJWTIdentityStore, I realized that the META-INF/microprofile-config.properties
file isn't scanned by Payara to verify the issuer and public key. While Payara's guide to activating MP JWT Auth 后并没有明确说明这一点,您无法从该文件配置发行者或 public 密钥位置.要配置您接受的颁发者,您必须在 src/main/resources
目录中放置一个名为 payara-mp-jwt.properties
的文件,其中包含密钥 accepted.issuer
和预期值(例如 accepted.issuer = someDomain.com
)。此外,要配置您的 public 密钥,您必须将名为 publicKey.pem
的 public 密钥放在同一目录中。 Payara 将只扫描这些文件,不会扫描 META-INF/microprofile.config
您配置的 public 密钥和接受的发行者值:
public SignedJWTIdentityStore() {
config = ConfigProvider.getConfig();
Optional<Properties> properties = readVendorProperties();
acceptedIssuer = readVendorIssuer(properties)
.orElseGet(() -> config.getOptionalValue(ISSUER, String.class)
.orElseThrow(() -> new IllegalStateException("No issuer found")));
jwtTokenParser = new JwtTokenParser(readEnabledNamespace(properties), readCustomNamespace(properties));
}
private Optional<Properties> readVendorProperties() {
URL mpJwtResource = currentThread().getContextClassLoader().getResource("/payara-mp-jwt.properties");
Properties properties = null;
if (mpJwtResource != null) {
try {
properties = new Properties();
properties.load(mpJwtResource.openStream());
} catch (IOException e) {
throw new IllegalStateException("Failed to load Vendor properties from resource file", e);
}
}
return Optional.ofNullable(properties);
}
包括这些将导致 JsonWebToken
被正确注入。我不确定为什么 Payara 的文档在未扫描的情况下不厌其烦地包含其他文件配置选项。