首页 > 解决方案 > 如何诊断 Quarkus REST 服务 OIDC 错误(特别是 403 禁止)?

问题描述

我正在尝试使用 OIDC 承载来保护 Quarkus (v 1.13.7) REST 服务。我正在关注他们的Using OpenID Connect to Protect Service Applications 指南,但我运气不佳。

每当我Authorization: Bearer {validAccessToken}在对受保护资源的请求的标头中包含(使用@RolesAllowed注释)时,我都会收到一个403 Forbidden带有空正文的响应。如果我省略此标头,我可以很好地访问未受保护的资源,但不出所料,我可以401 Unauthorized再次使用空主体来访问受保护的资源。

这是我使用的应用程序属性:

# DB configuration
quarkus.datasource.db-kind = postgresql
quarkus.datasource.username = bla
quarkus.datasource.password = bla_bla
quarkus.hibernate-orm.database.generation = update
quarkus.datasource.jdbc.url = jdbc:postgresql://blablabla

# Logging
%dev.quarkus.log.level=ALL
%dev.quarkus.log.category."io.quarkus.oidc".level=FINEST
%dev.quarkus.log.category."io.quarkus.security".level=FINEST

# OIDC config
demo.oidc-provider=valid.provider.url.com/bla
demo.oidc-clientid=validClientId
demo.oidc-clientsecret=validClientSecret
demo.oidc-issuer=${demo.oidc-provider}

quarkus.oidc.application-type=service
quarkus.oidc.auth-server-url=${demo.oidc-provider}
quarkus.oidc.client-id=${demo.oidc-clientid}
quarkus.oidc.credentials.client-secret.value=${demo.oidc-clientsecret}
quarkus.oidc.token.issuer=${demo.oidc-issuer}

quarkus.oidc.authentication.user-info-required=true
quarkus.oidc.roles.source=userinfo
quarkus.oidc.roles.role-claim-path=userroles
quarkus.oidc.discovery-enabled=false
quarkus.oidc.introspection-path=/introspect
quarkus.oidc.user-info-path=/userinfo

注意日志记录部分。尽管它被配置为记录所有内容,但当我收到 403 响应时,这就是它所打印的全部内容:

2021-08-04 11:22:10,147 FINEST [io.ver.ext.web.imp.RouterImpl] (vert.x-eventloop-thread-6) Router: 1653352852 accepting request GET http://localhost:8080/my/api/resource/path
2021-08-04 11:22:10,171 FINEST [io.ver.ext.web.imp.RoutingContextImplBase] (vert.x-eventloop-thread-6) Route matches: RouteState{path='null', order=-2147483648, enabled=true, methods=null, consumes=null, emptyBodyPermittedWithConsumes=false, produces=null, contextHandlers=[io.quarkus.vertx.http.runtime.VertxHttpRecorder$5@76ec9770], failureHandlers=null, added=true, pattern=null, groups=null, useNormalisedPath=true, namedGroupsInRegex=null, virtualHostPattern=null, pathEndsWithSlash=false, exclusive=false, exactPath=false}
2021-08-04 11:22:10,171 FINEST [io.ver.ext.web.imp.RoutingContextImplBase] (vert.x-eventloop-thread-6) Calling the  handler
2021-08-04 11:22:10,200 FINEST [io.ver.ext.web.imp.RoutingContextImplBase] (vert.x-eventloop-thread-6) Route matches: RouteState{path='null', order=-200, enabled=true, methods=null, consumes=null, emptyBodyPermittedWithConsumes=false, produces=null, contextHandlers=[io.quarkus.vertx.http.runtime.security.HttpSecurityRecorder$2@79d5df9a], failureHandlers=null, added=true, pattern=null, groups=null, useNormalisedPath=true, namedGroupsInRegex=null, virtualHostPattern=null, pathEndsWithSlash=false, exclusive=false, exactPath=false}
2021-08-04 11:22:10,202 FINEST [io.ver.ext.web.imp.RoutingContextImplBase] (vert.x-eventloop-thread-6) Calling the  handler
2021-08-04 11:22:10,203 FINEST [io.ver.ext.web.imp.RoutingContextImplBase] (vert.x-eventloop-thread-6) Route matches: RouteState{path='null', order=-100, enabled=true, methods=null, consumes=null, emptyBodyPermittedWithConsumes=false, produces=null, contextHandlers=[io.quarkus.vertx.http.runtime.security.HttpSecurityRecorder$3@1e4e05ec], failureHandlers=null, added=true, pattern=null, groups=null, useNormalisedPath=true, namedGroupsInRegex=null, virtualHostPattern=null, pathEndsWithSlash=false, exclusive=false, exactPath=false}
2021-08-04 11:22:10,206 FINEST [io.ver.ext.web.imp.RoutingContextImplBase] (vert.x-eventloop-thread-6) Calling the  handler
2021-08-04 11:22:10,207 FINEST [io.ver.ext.web.imp.RoutingContextImplBase] (vert.x-eventloop-thread-6) Route matches: RouteState{path='null', order=-2, enabled=true, methods=null, consumes=null, emptyBodyPermittedWithConsumes=false, produces=null, contextHandlers=[io.quarkus.vertx.http.runtime.VertxHttpRecorder$4@6bf0fbda], failureHandlers=null, added=true, pattern=null, groups=null, useNormalisedPath=true, namedGroupsInRegex=null, virtualHostPattern=null, pathEndsWithSlash=false, exclusive=false, exactPath=false}
2021-08-04 11:22:10,208 FINEST [io.ver.ext.web.imp.RoutingContextImplBase] (vert.x-eventloop-thread-6) Calling the  handler
2021-08-04 11:22:10,209 FINEST [io.ver.ext.web.imp.RoutingContextImplBase] (vert.x-eventloop-thread-6) Route matches: RouteState{path='null', order=10000, enabled=true, methods=null, consumes=null, emptyBodyPermittedWithConsumes=false, produces=null, contextHandlers=[io.quarkus.vertx.http.runtime.StaticResourcesRecorder$$Lambda$785/0x0000000800786840@6ed633ec, io.quarkus.vertx.http.runtime.StaticResourcesRecorder$$Lambda$785/0x0000000800786840@57426aed, io.quarkus.vertx.http.runtime.StaticResourcesRecorder$$Lambda$789/0x0000000800785840@7d171a43], failureHandlers=null, added=true, pattern=null, groups=null, useNormalisedPath=true, namedGroupsInRegex=null, virtualHostPattern=null, pathEndsWithSlash=false, exclusive=false, exactPath=false}
2021-08-04 11:22:10,210 FINEST [io.ver.ext.web.imp.RoutingContextImplBase] (vert.x-eventloop-thread-6) Calling the  handler
2021-08-04 11:22:10,210 FINEST [io.ver.ext.web.han.imp.StaticHandlerImpl] (vert.x-eventloop-thread-6) File to serve is /my/api/resource/path
2021-08-04 11:22:10,215 FINEST [io.ver.ext.web.han.imp.StaticHandlerImpl] (vert.x-eventloop-thread-6) File to serve is /my/api/resource/path
2021-08-04 11:22:10,218 FINEST [io.ver.ext.web.imp.RoutingContextImplBase] (vert.x-eventloop-thread-6) Route matches: RouteState{path='/', order=10001, enabled=true, methods=null, consumes=null, emptyBodyPermittedWithConsumes=false, produces=null, contextHandlers=[io.quarkus.resteasy.runtime.standalone.VertxRequestHandler@43d422b0], failureHandlers=null, added=true, pattern=null, groups=null, useNormalisedPath=true, namedGroupsInRegex=null, virtualHostPattern=null, pathEndsWithSlash=true, exclusive=false, exactPath=false}
2021-08-04 11:22:10,219 FINEST [io.ver.ext.web.imp.RoutingContextImplBase] (vert.x-eventloop-thread-6) Calling the  handler
2021-08-04 11:22:10,222 DEBUG [org.jbo.res.res.i18n] (executor-thread-1) RESTEASY002315: PathInfo: /my/api/resource/path

也许对某人,某处,上述信息很有用。对我来说,它们没有用。有趣的是,如果令牌已过期,我实际上会收到一条表明这一点的消息。

我应该如何在上帝的绿色地球(或您喜欢的任何神灵和行星属性)上诊断这个问题?

我已经尝试使用Quarkus 异常映射来尝试捕获问题并检查异常,但是无论我如何配置异常映射,它都不会被调用。根据quarkus git repo 上的 issue #8570

[...] 来映射身份验证失败和授权,那么您需要禁用主动身份验证,然后您可以为生成的 AuthenticationFailedException 使用异常映射器,因为它将从拦截器而不是在安全处理的早期生成。

所以我添加quarkus.http.auth.proactive=false到我的应用程序属性中,现在 Quarkus 返回一个500 Internal Error它说它不能调用getIdentity()我不知道什么线程,我应该注入这个身份或其他类似的东西。问题是,我并没有试图在任何地方获得这个身份(至少现在还没有),所以这个失败是在他们自己的内部代码中。因此我放弃了尝试使用异常映射。

编辑:我还应该提到 OIDC 信息(客户端 ID、网址等)是有效的。我在我的 Angular 应用程序中使用它们,它们工作得很好。

标签: quarkusquarkus-oidc

解决方案


此时,验证令牌的 OidcProvider 类将一些验证委托给底层的 jose-jwt 验证器。

通过启用以下日志记录类别,可以查看一些说明令牌可能无效的信息:

quarkus.log.category."io.quarkus.oidc".level=TRACE

# The category io.quarkus.oidc already contains the errors generated by the jose-jwt library

出现的错误通常与配置错误、过期令牌或无效签名有关。您将看不到有关允许访问资源的角色的错误。

所以会出现一些 403 错误的原因,而其他的则不会。

通常,当我过去在开发新应用程序时遇到 403 错误时,我检查的内容是确保受众、声明和角色(组)正确地存在于提供商颁发的令牌中。大多数情况下,错误是未正确设置的这三个错误之一。

我将打开一个增强功能以​​统一或至少包含尽可能多的错误或改进有关此主题的文档。

也许他们可以为此做点什么,或者为我们指明一个更好的方向。


推荐阅读