我正在开发一个在 Spring boot 中开发的微服务架构,并使用 Spring Cloud Gateway 提供 API 网关服务。我使用 Keycloak 作为身份提供者。一切正常,但我在用户登录时遇到中间 authorization_request_not_found 异常,并且出现可白色错误页面。 如果我们再试一次,就会成功。
以下是我在 Keycloak 服务器上收到的异常:
[org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider] (default task-264) Failed to make identity provider oauth callback: org.keycloak.broker.provider.IdentityBrokerException: No access_token from server.
at [email protected]//org.keycloak.broker.oidc.OIDCIdentityProvider.verifyAccessToken(OIDCIdentityProvider.java:495)
at [email protected]//org.keycloak.broker.oidc.OIDCIdentityProvider.getFederatedIdentity(OIDCIdentityProvider.java:360)
at [email protected]//org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider$Endpoint.authResponse(AbstractOAuth2IdentityProvider.java:472)
at jdk.internal.reflect.GeneratedMethodAccessor938.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at [email protected]//org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:138)
at [email protected]//org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:517)
at [email protected]//org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:406)
at [email protected]//org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$0(ResourceMethodInvoker.java:370)
at [email protected]//org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:356)
at [email protected]//org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:372)
at [email protected]//org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:344)
... more stack trace
以下是配置代码:
spring:
mvc:
favicon:
enabled: false
autoconfigure:
exclude: org.springframework.boot.actuate.autoconfigure.security.reactive.ReactiveManagementWebSecurityAutoConfiguration
thymeleaf:
cache: false
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri : http://localhost:8080/auth/realms/<realm-name>/protocol/openid-connect/certs
client:
registration:
keycloak:
client-id: <client-id>
client-secret: <client-secret>
clientName: <client-name>
authorization-grant-type: authorization_code
provider: keycloak
redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
scope:
- openid
- profile
- email
provider:
keycloak:
issuer-uri: http://localhost:8080/auth/realms/<realm-name>
user-name-attribute: preferred_username
cloud:
gateway:
httpclient:
connect-timeout: 6000000
response-timeout: 600s
ssl:
close-notify-read-timeout: 600s
close-notify-flush-timeout: 600s
handshake-timeout: 600s
pool:
acquire-timeout: 6000000
type: fixed
max-connections: 5000
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
下面是我的 pom.xml 的一部分
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.9.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR5</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
下面是网关服务器上异常的堆栈跟踪:
2020-06-13 08:04:17.237 ERROR 1 --- [or-http-epoll-3] a.w.r.e.AbstractErrorWebExceptionHandler : [47e02fef] 500 Server Error for HTTP GET "/login/oauth2/code/keycloak?state=YpBEDGlrHg1-podfMyIrKp02WYVPDIMRu_59vuRqado%3D&session_state=e8f4736b-1985-4730-af00-f55b38edf44a&code=1050f391-8438-4c18-ba52-d343ed25aa1c.e8f4736b-1985-4730-af00-f55b38edf44a.5b3ba9ec-3da5-4549-aaa8-79cf360f1d6d"
org.springframework.security.oauth2.core.OAuth2AuthorizationException: [authorization_request_not_found]
at org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizationCodeAuthenticationTokenConverter.lambda$oauth2AuthorizationException$1(ServerOAuth2AuthorizationCodeAuthenticationTokenConverter.java:82) ~[spring-security-oauth2-client-5.1.6.RELEASE.jar!/:5.1.6.RELEASE]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44) [reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
at reactor.core.publisher.Mono.subscribe(Mono.java:3858) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:75) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
at reactor.core.publisher.FluxHandle$HandleSubscriber.onComplete(FluxHandle.java:207) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
at reactor.core.publisher.FluxHandle$HandleSubscriber.onNext(FluxHandle.java:128) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
at reactor.core.publisher.FluxMap$MapConditionalSubscriber.onNext(FluxMap.java:213) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1515) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
at reactor.core.publisher.MonoProcessor.onNext(MonoProcessor.java:389) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:192) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:67) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1515) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
at reactor.core.publisher.MonoSupplier.subscribe(MonoSupplier.java:61) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
at reactor.core.publisher.Mono.subscribe(Mono.java:3858) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:75) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
at reactor.core.publisher.MonoNext$NextSubscriber.onComplete(MonoNext.java:96) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:360) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onComplete(FluxConcatMap.java:269) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
at reactor.core.publisher.Operators.complete(Operators.java:131) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:122) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:63) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
at reactor.core.publisher.FluxConcatMap.subscribe(FluxConcatMap.java:121) ~[reactor-core-3.2.12.RELEASE.jar!/:3.2.12.RELEASE]
... more stack trace.
据我所知,当用户会话超时时会出现此错误。
我认为一旦会话超时,cookie 就不会被删除。当用户从应用程序注销时,这个问题永远不会发生。
有什么办法可以在会话超时时自动删除cookie吗?或者,当我在用户路由到登录屏幕之前收到超时错误时,我可以重定向到注销 API 吗?
我访问了几个博客和其他堆栈溢出问题,但我无法弄清楚我的配置出了什么问题。
有什么办法可以找出问题所在吗?
非常感谢任何帮助。预先感谢!
您在 Azure 应用程序注册中的 client_secret 已过期。 让我们通过在应用程序注册的 Azure 门户中创建新的 client_secret 来更新 Keycloak 设置中的 client_secret