在尝试使用 Keycloak、Apache Shiro 和 Pac4j 与 Apache Zeppelin 集成进行身份验证时,我遇到了 PKCE 验证问题。我收到的错误是:
HTTP ERROR 500 javax.servlet.ServletException: org.pac4j.core.exception.TechnicalException: Bad token response, error=invalid_grant, description=PKCE verification failed: Code mismatch
URI: /api/callback
STATUS: 500
MESSAGE: javax.servlet.ServletException: org.pac4j.core.exception.TechnicalException: Bad token response, error=invalid_grant, description=PKCE verification failed: Code mismatch
SERVLET: rest
CAUSED BY: javax.servlet.ServletException: org.pac4j.core.exception.TechnicalException: Bad token response, error=invalid_grant, description=PKCE verification failed: Code mismatch
CAUSED BY: org.pac4j.core.exception.TechnicalException: Bad token response, error=invalid_grant, description=PKCE verification failed: Code mismatch
Caused by:
javax.servlet.ServletException: org.pac4j.core.exception.TechnicalException: Bad token response, error=invalid_grant, description=PKCE verification failed: Code mismatch
at org.apache.shiro.web.servlet.AdviceFilter.cleanup(AdviceFilter.java:196)
at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:148)
at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:154)
at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66)
at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:458)
at org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:373)
at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90)
at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83)
at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:387)
at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:370)
at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:154)
at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193)
at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1626)
at org.apache.zeppelin.server.CorsFilter.doFilter(CorsFilter.java:64)
at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193)
at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1626)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:552)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:600)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:235)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1624)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1440)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:505)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1594)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1355)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:234)
at io.micrometer.core.instrument.binder.jetty.TimedHandler.handle(TimedHandler.java:120)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
at org.eclipse.jetty.server.Server.handle(Server.java:516)
at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:487)
at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:732)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:479)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:277)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105)
at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:883)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1034)
at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: org.pac4j.core.exception.TechnicalException: Bad token response, error=invalid_grant, description=PKCE verification failed: Code mismatch
at org.pac4j.oidc.credentials.authenticator.OidcAuthenticator.executeTokenRequest(OidcAuthenticator.java:206)
at org.pac4j.oidc.credentials.authenticator.OidcAuthenticator.validate(OidcAuthenticator.java:165)
at org.pac4j.core.client.BaseClient.lambda$retrieveCredentials$0(BaseClient.java:75)
at java.base/java.util.Optional.ifPresent(Optional.java:183)
at org.pac4j.core.client.BaseClient.retrieveCredentials(BaseClient.java:72)
at org.pac4j.core.client.IndirectClient.getCredentials(IndirectClient.java:145)
at org.pac4j.core.engine.DefaultCallbackLogic.perform(DefaultCallbackLogic.java:75)
at org.pac4j.jee.filter.CallbackFilter.internalFilter(CallbackFilter.java:71)
at org.pac4j.jee.config.AbstractConfigFilter.doFilter(AbstractConfigFilter.java:72)
at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66)
at org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:108)
at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:137)
... 42 more
Powered by Jetty:// 9.4.52.v20230823
这是我的 shiro.ini:
[main]
roleAdminAuthGenerator = org.pac4j.core.authorization.generator.FromAttributesAuthorizationGenerator
sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
securityManager.sessionManager = $sessionManager
securityManager.sessionManager.globalSessionTimeout = 86400000
### Define CodeChallengeMethod
#S256 = com.nimbusds.oauth2.sdk.pkce.CodeChallengeMethod
#S256.name = S256
### OIDC Pac4j Config
oidcConfig = org.pac4j.oidc.config.OidcConfiguration
oidcConfig.withState = false
oidcConfig.discoveryURI = http://localhost:8323/realms/zeppelin/.well-known/openid-configuration
oidcConfig.clientAuthenticationMethodAsString = client_secret_basic
oidcConfig.clientId = zeppelin-client
oidcConfig.secret = <secret>
oidcConfig.scope = openid
oidcConfig.useNonce = true
oidcConfig.responseType = code
oidcConfig.logoutUrl = http://localhost:8323/realms/zeppelin/protocol/openid-connect/logout
oidcClient = org.pac4j.oidc.client.OidcClient
oidcClient.name = oidcClient
oidcClient.configuration = $oidcConfig
oidcClient.authorizationGenerator = $roleAdminAuthGenerator
### Pac4J Client Details
clients = org.pac4j.core.client.Clients
requireRoleAdmin = org.pac4j.core.authorization.authorizer.RequireAnyRoleAuthorizer
requireRoleAdmin.elements = admin_role
requireRoleUser = org.pac4j.core.authorization.authorizer.RequireAnyRoleAuthorizer
requireRoleUser.elements = admin_role, user_role
### Pac4j Config
config = org.pac4j.core.config.Config
config.clients = $clients
config.authorizers = admin:$requireRoleAdmin, user:$requireRoleUser
### Pac4jRealm and SecurityFilter
pac4jRealm = io.buji.pac4j.realm.Pac4jRealm
pac4jRealm.principalNameAttribute = preferred_username
pac4jSubjectFactory = io.buji.pac4j.subject.Pac4jSubjectFactory
securityManager.realms = $pac4jRealm
securityManager.subjectFactory = $pac4jSubjectFactory
oidcSecurityFilter = org.pac4j.jee.filter.SecurityFilter
oidcSecurityFilter.clients = oidcClient
oidcSecurityFilter.config = $config
### Logout Filter
logoutFilter = org.pac4j.jee.filter.LogoutFilter
logoutFilter.localLogout = true
logoutFilter.centralLogout = true
logoutFilter.config = $config
logoutFilter.defaultUrl = http://localhost:8323/realms/zeppelin/protocol/openid-connect/logout
### Ajax Resolvers
ajaxRequestResolver = org.pac4j.core.http.ajax.DefaultAjaxRequestResolver
ajaxRequestResolver.addRedirectionUrlAsHeader = true
oidcClient.ajaxRequestResolver = $ajaxRequestResolver
### Callback Filters
callbackFilter = org.pac4j.jee.filter.CallbackFilter
callbackFilter.defaultUrl = http://localhost:8080
callbackFilter.config = $config
clients.callbackUrl = http://localhost:8080/api/callback
clients.clients = $oidcClient
[urls]
/api/version = anon
/api/callback = callbackFilter
/api/login/logout = logoutFilter
/** = oidcSecurityFilter
齐柏林飞艇版本:0.11.1 使用中的图书馆
asm-1.0.2.jar
buji-pac4j-8.1.0.jar
content-type-2.3.jar
javaee-pac4j-7.1.0.jar
json-smart-2.5.1.jar
lang-tag-1.7.jar
pac4j-config-5.7.6.jar
pac4j-core-5.7.6.jar
pac4j-http-5.7.6.jar
pac4j-javaee-5.7.6.jar
pac4j-oauth-5.7.6.jar
pac4j-oidc-5.7.6.jar
oauth2-oidc-sdk-11.13.jar
我在Google上搜索过,但发现之前没有人遇到过这个错误。
事实上,错误来自 Keycloack,当 pac4j 想要将接收到的代码交换为访问令牌时,它会回复:
PKCE verification failed: Code mismatch
。
您可以尝试删除此配置吗:
oidcConfig.secret=xxx
? PKCE 不需要秘密。