如何诊断 Quarkus REST 服务 OIDC 错误(特别是 403 禁止)?
Posted
技术标签:
【中文标题】如何诊断 Quarkus REST 服务 OIDC 错误(特别是 403 禁止)?【英文标题】:How does one diagnose Quarkus REST service OIDC errors (specifically 403 forbidden)? 【发布时间】:2021-10-09 08:52:35 【问题描述】:我正在尝试使用 OIDC 承载来保护 Quarkus (v 1.13.7) REST 服务。我正在关注他们的Using OpenID Connect to Protect Service Applications guide,但我运气不佳。
每当我在对受保护资源的请求的标头中包含 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: RouteStatepath='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: RouteStatepath='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: RouteStatepath='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: RouteStatepath='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: RouteStatepath='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: RouteStatepath='/', 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 exception mapping 来尝试捕获问题并检查异常,但是无论我如何配置异常映射,它都不会被调用。根据issue #8570 on the quarkus git repo:
[...] 映射身份验证失败以及授权然后您需要 禁用主动身份验证,然后您可以使用异常映射器 对于产生的 AuthenticationFailedException,因为它将是 从拦截器生成,而不是在安全处理的早期生成。
所以我在我的应用程序属性中添加了quarkus.http.auth.proactive=false
,现在 Quarkus 返回一个500 Internal Error
,它表示它不能在我不知道哪个线程上调用getIdentity()
,我应该改为注入这个身份或其他一些这样的。问题是,我并没有试图在任何地方获得这个身份(至少现在还没有),所以这个失败是在他们自己的内部代码中。因此我放弃了尝试使用异常映射。
编辑:我还应该提到 OIDC 信息(客户端 ID、网址等)是有效的。我在我的 Angular 应用程序中使用它们,它们工作得很好。
【问题讨论】:
您使用的是什么版本的 quarkus,您计划使用什么安全架构,我的意思是您是使用保护资源路由还是只是使用 @RolesAllowed 注释您的端点? 我使用的是版本 1.13.7 和 @RolesAllowed 注释。 你使用的是resteasy-reactive还是normal? 我尝试使用两者(不是同时使用),但结果相同。 您是否验证过您的令牌包含所需的组,因此到角色的映射是正确的?我看到您正在使用角色声明路径属性 【参考方案1】:此时,验证令牌的 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 错误时,我通常检查的内容是确保受众、声明和角色(组)正确地存在于提供商颁发的令牌中。大多数情况下,错误是未正确设置的这三个错误之一。
我将打开一个增强功能以统一或至少包含尽可能多的错误或改进有关此主题的文档。
也许他们可以做点什么或为我们指明一个更好的方向。
【讨论】:
以上是关于如何诊断 Quarkus REST 服务 OIDC 错误(特别是 403 禁止)?的主要内容,如果未能解决你的问题,请参考以下文章
是否有具有多个命名 Oidc 客户端的示例 quarkus 项目来调用多个服务调用?
如何在 Kubernetes 上的 Quarkus 中设置与 Keycloak 的 OIDC 连接