Keycloak + Spring Boot + Spring 如果令牌无效,安全性会以某种方式进行令牌验证 2 次
Keycloak + Spring Boot + Spring Security does somehow token validation 2 times if the token is invalid
我正在使用带有 keycloak 的 spring 安全性,如果我使用无效令牌在特定端点上发出请求,看起来令牌验证已完成 2 次,我也尝试实施我自己的身份验证提供程序使用 keycloak 身份验证提供程序的逻辑并覆盖了 BearerTokenRequestAuthenticator 进行令牌验证但它仍然做同样的事情..我不确定问题是否来自某种bean定义
这是相同的日志,您可以在其中看到字符串“Verifying access_token”出现了 2 次。
2021-04-16 16:35:18,220 DEBUG 16672 --- [org.keycloak.adapters.PreAuthActionsHandler]: adminRequest http://localhost:7006/gateway/core/rest/api/core/initPayment/100356
2021-04-16 16:35:18,220 DEBUG 16672 --- [org.apache.catalina.authenticator.AuthenticatorBase]: Security checking request POST /gateway/core/rest/api/core/initPayment/100356
2021-04-16 16:35:18,220 DEBUG 16672 --- [org.apache.catalina.realm.RealmBase]: No applicable constraints defined
2021-04-16 16:35:18,220 DEBUG 16672 --- [org.apache.catalina.authenticator.AuthenticatorBase]: Not subject to any constraint
2021-04-16 16:35:18,220 DEBUG 16672 --- [org.keycloak.adapters.tomcat.AbstractAuthenticatedActionsValve]: AuthenticatedActionsValve.invoke /gateway/core/rest/api/core/initPayment/100356
2021-04-16 16:35:18,220 DEBUG 16672 --- [org.keycloak.adapters.AuthenticatedActionsHandler]: AuthenticatedActionsValve.invoke http://localhost:7006/gateway/core/rest/api/core/initPayment/100356
2021-04-16 16:35:18,220 DEBUG 16672 --- [org.keycloak.adapters.AuthenticatedActionsHandler]: Policy enforcement is disabled.
2021-04-16 16:35:18,220 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /core/rest/api/core/initPayment/100356 at position 1 of 15 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2021-04-16 16:35:18,220 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /core/rest/api/core/initPayment/100356 at position 2 of 15 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /core/rest/api/core/initPayment/100356 at position 3 of 15 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /core/rest/api/core/initPayment/100356 at position 4 of 15 in additional filter chain; firing Filter: 'CorsFilter'
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /core/rest/api/core/initPayment/100356 at position 5 of 15 in additional filter chain; firing Filter: 'KeycloakPreAuthActionsFilter'
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.keycloak.adapters.PreAuthActionsHandler]: adminRequest http://localhost:7006/gateway/core/rest/api/core/initPayment/100356
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /core/rest/api/core/initPayment/100356 at position 6 of 15 in additional filter chain; firing Filter: 'KeycloakAuthenticationProcessingFilter'
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.springframework.security.web.util.matcher.OrRequestMatcher]: Trying to match using Ant [pattern='/sso/login']
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.springframework.security.web.util.matcher.AntPathRequestMatcher]: Checking match of request : '/core/rest/api/core/initPayment/100356'; against '/sso/login'
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.springframework.security.web.util.matcher.OrRequestMatcher]: Trying to match using RequestHeaderRequestMatcher [expectedHeaderName=Authorization, expectedHeaderValue=null]
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.springframework.security.web.util.matcher.OrRequestMatcher]: matched
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter]: Request is to process authentication
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter]: Attempting Keycloak authentication
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.keycloak.adapters.BearerTokenRequestAuthenticator]: Found [1] values in authorization header, selecting the first value for Bearer.
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.keycloak.adapters.BearerTokenRequestAuthenticator]: Verifying access_token
2021-04-16 16:35:18,222 DEBUG 16672 --- [org.keycloak.adapters.BearerTokenRequestAuthenticator]: Failed to verify token
2021-04-16 16:35:18,222 DEBUG 16672 --- [org.keycloak.adapters.RequestAuthenticator]: Bearer FAILED
2021-04-16 16:35:18,222 DEBUG 16672 --- [org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter]: Auth outcome: FAILED
2021-04-16 16:35:18,223 DEBUG 16672 --- [org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter]: Authentication request failed: org.keycloak.adapters.springsecurity.KeycloakAuthenticationException: Invalid authorization header, see WWW-Authenticate header for details
org.keycloak.adapters.springsecurity.KeycloakAuthenticationException: Invalid authorization header, see WWW-Authenticate header for details
at org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter.attemptAuthentication(KeycloakAuthenticationProcessingFilter.java:162) ~[keycloak-spring-security-adapter-9.0.0.jar:9.0.0]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter.doFilter(KeycloakPreAuthActionsFilter.java:96) ~[keycloak-spring-security-adapter-9.0.0.jar:9.0.0]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:92) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:93) ~[spring-boot-actuator-2.3.9.RELEASE.jar:2.3.9.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.keycloak.adapters.tomcat.AbstractAuthenticatedActionsValve.invoke(AbstractAuthenticatedActionsValve.java:67) ~[spring-boot-container-bundle-9.0.0.jar:9.0.0]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.keycloak.adapters.tomcat.AbstractKeycloakAuthenticatorValve.invoke(AbstractKeycloakAuthenticatorValve.java:181) ~[spring-boot-container-bundle-9.0.0.jar:9.0.0]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:346) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:887) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1684) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]
2021-04-16 16:35:18,223 DEBUG 16672 --- [org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter]: Updated SecurityContextHolder to contain null Authentication
2021-04-16 16:35:18,223 DEBUG 16672 --- [org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter]: Delegating to authentication failure handler org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationFailureHandler@331b3993
2021-04-16 16:35:18,223 DEBUG 16672 --- [org.springframework.security.web.header.writers.HstsHeaderWriter]: Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@784a6674
2021-04-16 16:35:18,223 DEBUG 16672 --- [org.springframework.security.web.context.SecurityContextPersistenceFilter]: SecurityContextHolder now cleared, as request processing completed
2021-04-16 16:35:18,223 DEBUG 16672 --- [org.apache.catalina.core.ContainerBase.[Tomcat].[localhost]]: Processing ErrorPage[errorCode=0, location=/error]
2021-04-16 16:35:18,223 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /error at position 1 of 15 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2021-04-16 16:35:18,224 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /error at position 2 of 15 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /error at position 3 of 15 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /error at position 4 of 15 in additional filter chain; firing Filter: 'CorsFilter'
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /error at position 5 of 15 in additional filter chain; firing Filter: 'KeycloakPreAuthActionsFilter'
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.keycloak.adapters.PreAuthActionsHandler]: adminRequest http://localhost:7006/gateway/error
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /error at position 6 of 15 in additional filter chain; firing Filter: 'KeycloakAuthenticationProcessingFilter'
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.springframework.security.web.util.matcher.OrRequestMatcher]: Trying to match using Ant [pattern='/sso/login']
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.springframework.security.web.util.matcher.AntPathRequestMatcher]: Checking match of request : '/error'; against '/sso/login'
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.springframework.security.web.util.matcher.OrRequestMatcher]: Trying to match using RequestHeaderRequestMatcher [expectedHeaderName=Authorization, expectedHeaderValue=null]
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.springframework.security.web.util.matcher.OrRequestMatcher]: matched
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter]: Request is to process authentication
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter]: Attempting Keycloak authentication
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.keycloak.adapters.BearerTokenRequestAuthenticator]: Found [1] values in authorization header, selecting the first value for Bearer.
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.keycloak.adapters.BearerTokenRequestAuthenticator]: Verifying access_token
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.keycloak.adapters.BearerTokenRequestAuthenticator]: Failed to verify token
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.keycloak.adapters.RequestAuthenticator]: Bearer FAILED
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter]: Auth outcome: FAILED
2021-04-16 16:35:18,226 DEBUG 16672 --- [org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter]: Authentication request failed: org.keycloak.adapters.springsecurity.KeycloakAuthenticationException: Invalid authorization header, see WWW-Authenticate header for details
org.keycloak.adapters.springsecurity.KeycloakAuthenticationException: Invalid authorization header, see WWW-Authenticate header for details
at org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter.attemptAuthentication(KeycloakAuthenticationProcessingFilter.java:162) ~[keycloak-spring-security-adapter-9.0.0.jar:9.0.0]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter.doFilter(KeycloakPreAuthActionsFilter.java:96) ~[keycloak-spring-security-adapter-9.0.0.jar:9.0.0]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:103) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:103) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:103) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:103) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:103) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:710) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:459) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:384) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:312) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.StandardHostValve.custom(StandardHostValve.java:398) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.StandardHostValve.status(StandardHostValve.java:257) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:179) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:346) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:887) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1684) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]
2021-04-16 16:35:18,226 DEBUG 16672 ---
这是我的安全配置 class
@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
@KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
private static final String[] SWAGGER_RESOURCES_WHITELIST = {
"/authenticate/rest/api/authenticate/v2/api-docs",
"/core/rest/api/core/v2/api-docs",
"/v2/api-docs",
"/swagger-resources",
"/swagger-resources/**",
"/configuration/ui",
"/configuration/security",
"/swagger-ui.html",
"/webjars/**",
"/v3/api-docs/**",
"/swagger-ui/**",
"/swagger-ui.html"
};
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.cors().and().csrf().disable().sessionManagement().
sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()
.antMatchers("/authenticate/rest/api/authenticate/token").permitAll()
.antMatchers("/authenticate/rest/api/authenticate/refreshToken").permitAll()
.anyRequest().authenticated();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return source;
}
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new NullAuthenticatedSessionStrategy();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
/* remove default spring "ROLE_" prefix appending to keycloak's roles*/
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
// *************************** Avoid Bean redefinition ********************************
@Bean
public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean(
KeycloakAuthenticationProcessingFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Bean
public FilterRegistrationBean keycloakPreAuthActionsFilterRegistrationBean(
KeycloakPreAuthActionsFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Bean
public FilterRegistrationBean keycloakAuthenticatedActionsFilterBean(
KeycloakAuthenticatedActionsFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Bean
public FilterRegistrationBean keycloakSecurityContextRequestFilterBean(
KeycloakSecurityContextRequestFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Bean
@Override
@ConditionalOnMissingBean(HttpSessionManager.class)
protected HttpSessionManager httpSessionManager() {
return new HttpSessionManager();
}
}
您可以添加 JwtAuthorizationTokenFilter 并在每次请求之前调用它
在您的安全配置中使用 addFilterBefore()
// SecurityConfig changes
private final JwtAuthenticationEntryPoint unauthorizedHandler;
private final JwtAuthorizationTokenFilter authenticationTokenFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// we don't need CSRF because our token is invulnerable
.csrf().disable()
.exceptionHandling()
.defaultAuthenticationEntryPointFor(
getRestAuthenticationEntryPoint(),
new AntPathRequestMatcher("/**")
)
.authenticationEntryPoint(unauthorizedHandler).and()
// don't create session
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// system state endpoint
.antMatchers("/ping").permitAll()
// User authentication actions
.antMatchers("/auth" + "/**").permitAll()
.antMatchers("/**/*.css").permitAll()
.anyRequest().authenticated()
;
http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
;
// disable page caching
http
.headers()
.frameOptions().sameOrigin()
.cacheControl();
}
JwtAuthorizationTokenFilter class
@Component
@AllArgsConstructor
public class JwtAuthorizationTokenFilter extends OncePerRequestFilter {
public static final String AUTHORIZATION_HEADER = HttpHeaders.AUTHORIZATION;
public static final String AUTHORIZATION_TOKEN_TYPE = "Bearer";
private final SSOManager ssoManager;
private final SimpleAuthorityMapper simpleAuthorityMapper = new SimpleAuthorityMapper();
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String requestURI = request.getRequestURI();
boolean isRefreshRequest = ("/auth" + "/refresh").equals(requestURI);
final String requestHeader = request.getHeader(AUTHORIZATION_HEADER);
final String authenticationHeader = AUTHORIZATION_TOKEN_TYPE + " ";
if (requestHeader != null && requestHeader.startsWith(authenticationHeader)) {
try {
String authToken = requestHeader.substring(authenticationHeader.length());
AccessToken accessToken = isRefreshRequest ?
ssoManager.loadAccessTokenFromRefreshToken(authToken) :
ssoManager.loadAccessToken(authToken);
if (accessToken != null) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication instanceof AnonymousAuthenticationToken || authentication == null) {
// security context was null, so authorizing user
JwtKeycloakAuthenticationToken jwtKeycloakAuthenticationToken = createJwtKeycloakAuthenticationToken(accessToken);
SecurityContextHolder.getContext().setAuthentication(jwtKeycloakAuthenticationToken);
}
}
} catch (Exception e) {
// ToDo: improve error handling
// the token is expired and not valid anymore, TokenNotActiveException
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
return;
}
}
chain.doFilter(request, response);
}
public JwtKeycloakAuthenticationToken createJwtKeycloakAuthenticationToken(AccessToken accessToken) {
String username = accessToken.getPreferredUsername();
Map<String, AccessToken.Access> resourceAccess = accessToken.getResourceAccess();
UserPrincipal userPrincipal = new UserPrincipal(username);
Set<GrantedAuthority> authorities = getAuthorities(resourceAccess);
UserProfileDto userProfile = buildUserProfile(accessToken);
JwtKeycloakAuthenticationToken jwtKeycloakAuthenticationToken = new JwtKeycloakAuthenticationToken(userPrincipal, authorities, accessToken, userProfile);
return jwtKeycloakAuthenticationToken;
}
Set<GrantedAuthority> getAuthorities(Map<String, AccessToken.Access> resourceAccess) {
Set<GrantedAuthority> roles = new HashSet<>();
resourceAccess.forEach((key, value) -> {
Set<SimpleGrantedAuthority> realmRoles = value.getRoles().stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toSet());
roles.addAll(realmRoles);
});
Set<GrantedAuthority> grantedAuthorities = simpleAuthorityMapper.mapAuthorities(roles);
return grantedAuthorities;
}
public UserProfileDto buildUserProfile(AccessToken accessToken) {
return UserProfileDto.builder()
.userId(accessToken.getSubject())
.username(accessToken.getPreferredUsername())
.fullName(accessToken.getName())
.firstName(accessToken.getGivenName())
.lastName(accessToken.getFamilyName())
.email(accessToken.getEmail())
.roles(getResourceRoles(accessToken))
.build();
}
private static Set<String> getResourceRoles(AccessToken accessToken) {
final String realmClient = accessToken.getIssuedFor();
final Map<String, AccessToken.Access> resourceAccess = accessToken.getResourceAccess();
if (resourceAccess.containsKey(realmClient)) {
final AccessToken.Access access = resourceAccess.get(realmClient);
return access.getRoles();
}
return Collections.emptySet();
}
}
我们使用的一些自定义 SSOManager 接口:
public interface SSOManager {
/**
* This method will verify user access token and provide userId if token is valid. in case of
* invalid access token it will throw ProjectCommon exception with 401.
*/
AccessToken loadAccessToken(String token) throws TokenNotActiveException, VerificationException, NoSuchFieldException;
AccessToken loadAccessTokenFromRefreshToken(String token) throws TokenNotActiveException, VerificationException, NoSuchFieldException;
/**
* this method will do the user login with key cloak. after login it will provide access token object.
*/
AccessTokenResponse login(String userName, String password);
AccessTokenResponse refresh(String refreshToken);
void logout(String refreshToken);
}
并且 KeyCloakServiceImpl 实现了具有所有身份验证魔法的 SSOManager:
@Component
@Slf4j
public class KeyCloakServiceImpl implements SSOManager {
private RestTemplate restTemplate;
private final KeyCloakConnectionProvider keyCloakConnectionProvider;
@Autowired
public KeyCloakServiceImpl(KeyCloakConnectionProvider keyCloakConnectionProvider,
RestTemplateBuilder restTemplateBuilder) throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
this.keyCloakConnectionProvider = keyCloakConnectionProvider;
TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
.loadTrustMaterial(null, acceptingTrustStrategy)
.build();
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(csf)
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
this.restTemplate = restTemplateBuilder
.requestFactory(requestFactory)
.messageConverters(new MappingJackson2HttpMessageConverter(), new FormHttpMessageConverter())
.build();
}
private AccessToken getAccessToken(String accessToken, boolean checkActive) throws VerificationException, NoSuchFieldException {
try {
PublicKey publicKey = getPublicKey();
if (publicKey != null) {
String realmUrl = keyCloakConnectionProvider.getRealmUrl();
AccessToken token =
RSATokenVerifier.verifyToken(
accessToken,
publicKey,
realmUrl,
checkActive,
true);
return token;
} else {
log.error("KeyCloakServiceImpl:verifyToken: SSO_PUBLIC_KEY is NULL.");
throw new NoSuchFieldException("KeyCloakServiceImpl:verifyToken: SSO_PUBLIC_KEY is NULL.");
}
} catch (TokenNotActiveException e) {
throw e;
} catch (VerificationException e) {
throw e;
} catch (NoSuchFieldException e) {
throw e;
} catch (Exception e) {
throw e;
}
}
@Override
public AccessToken loadAccessToken(String accessToken) throws TokenNotActiveException, VerificationException, NoSuchFieldException {
return getAccessToken(accessToken, true);
}
@Override
public AccessToken loadAccessTokenFromRefreshToken(String accessToken) throws TokenNotActiveException, VerificationException, NoSuchFieldException {
return getAccessToken(accessToken, false);
}
/**
* This method will call keycloak service to user login. after successful login it will provide
* access token.
*/
@Override
public AccessTokenResponse login(String username, String password) {
try {
MultiValueMap<String, String> requestParams = new LinkedMultiValueMap<>();
requestParams.add("client_id", keyCloakConnectionProvider.getResource());
requestParams.add("username", username);
requestParams.add("password", password);
requestParams.add("grant_type", "password");
requestParams.add("client_secret", keyCloakConnectionProvider.getClientSecret());
requestParams.add("scope", "openid");
AccessTokenResponse keycloakAccessToken = queryKeycloakByParams(requestParams);
return keycloakAccessToken;
} catch (Exception e) {
log.info(e.getMessage(), e);
throw e;
}
}
@Override
public AccessTokenResponse refresh(String refreshToken) {
try {
MultiValueMap<String, String> requestParams = new LinkedMultiValueMap<>();
requestParams.add("client_id", keyCloakConnectionProvider.getResource());
requestParams.add("grant_type", "refresh_token");
requestParams.add("client_secret", keyCloakConnectionProvider.getClientSecret());
requestParams.add("refresh_token", refreshToken);
AccessTokenResponse keycloakAccessToken = queryKeycloakByParams(requestParams);
return keycloakAccessToken;
} catch (Exception e) {
log.info(e.getMessage(), e);
throw e;
}
}
private AccessTokenResponse queryKeycloakByParams(MultiValueMap<String, String> requestParams) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(requestParams, headers);
String url = keyCloakConnectionProvider.getOpenIdConnectTokenUrl();
AccessTokenResponse keycloakAccessToken = getAccessTokenResponse(request, url);
return keycloakAccessToken;
}
private AccessTokenResponse getAccessTokenResponse(HttpEntity<MultiValueMap<String, String>> request, String url) {
try {
ResponseEntity<AccessTokenResponse> response = restTemplate.postForEntity(url, request, AccessTokenResponse.class);
return response.getBody();
} catch (ResourceAccessException e) {
log.error("KeyCloak getAccessTokenResponse: " + e.getMessage());
try {
ResponseEntity<AccessTokenResponse> response = restTemplate.postForEntity(url, request, AccessTokenResponse.class);
return response.getBody();
} catch (Exception ex) {
throw ex;
}
} catch (Exception e) {
throw e;
}
}
@Override
public void logout(String refreshToken) {
try {
MultiValueMap<String, String> requestParams = new LinkedMultiValueMap<>();
requestParams.add("client_id", keyCloakConnectionProvider.getResource());
requestParams.add("client_secret", keyCloakConnectionProvider.getClientSecret());
requestParams.add("refresh_token", refreshToken);
logoutUserSession(requestParams);
} catch (Exception e) {
log.info(e.getMessage(), e);
throw e;
}
}
private void logoutUserSession(MultiValueMap<String, String> requestParams) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(requestParams, headers);
String url = keyCloakConnectionProvider.getOpenIdConnectLogoutUrl();
restTemplate.postForEntity(url, request, Object.class);
}
private PublicKey getPublicKey() {
PublicKey publicKey = keyCloakConnectionProvider.getPublicKey();
if (publicKey == null) {
LinkedHashMap publicKeyMap = requestKeyFromKeycloak(keyCloakConnectionProvider.getOpenIdConnectCertsUrl());
publicKey = KeyCloakRsaKeyLoader.getPublicKeyFromKeyCloak(publicKeyMap);
keyCloakConnectionProvider.setPublicKey(publicKey);
}
return publicKey;
}
/**
* This method will connect to keycloak server using API call for getting public key.
*
* @param url A string value having keycloak base URL
* @return Public key JSON response string
*/
private LinkedHashMap requestKeyFromKeycloak(String url) {
try {
ResponseEntity<LinkedHashMap> response = restTemplate.getForEntity(url, LinkedHashMap.class);
LinkedHashMap body = response.getBody();
if (body != null) {
return body;
} else {
log.error("KeyCloakRsaKeyLoader:requestKeyFromKeycloak: Not able to fetch SSO public key from keycloak server");
}
} catch (Exception e) {
log.error("KeyCloakRsaKeyLoader:requestKeyFromKeycloak: Exception occurred with message = " + e.getMessage());
}
return null;
}
}
和 KeyCloakConnectionProvider 使用属性
@Component
@Slf4j
@AllArgsConstructor
public class KeyCloakConnectionProvider {
private static PropertiesCache cache = PropertiesCache.getInstance();
private KeycloakSpringBootProperties keycloakProperties;
public String getAuthServerUrl() {
return keycloakProperties.getAuthServerUrl();
}
public String getRealmUrl() {
return getAuthServerUrl()
+ "/realms/"
+ getRealm();
}
public String getOpenIdConnectUrl() {
return getRealmUrl() + "/protocol/openid-connect";
}
public String getOpenIdConnectTokenUrl() {
return getOpenIdConnectUrl() + "/token";
}
public String getOpenIdConnectLogoutUrl() {
return getOpenIdConnectUrl() + "/logout";
}
public String getOpenIdConnectCertsUrl() {
return getOpenIdConnectUrl() + "/certs";
}
public String getRealm() {
return keycloakProperties.getRealm();
}
public String getResource() {
return keycloakProperties.getResource();
}
public String getClientId() {
return getResource();
}
public String getClientSecret() {
return String.valueOf(keycloakProperties.getCredentials().get("secret"));
}
public int getConnectionPoolSize() {
return keycloakProperties.getConnectionPoolSize();
}
public PublicKey getPublicKey() {
return cache.getPublicKey();
}
public PublicKey setPublicKey(PublicKey publicKey) {
if (publicKey != null) {
cache.savePublicKey(publicKey);
}
return cache.getPublicKey();
}
}
尝试从这些文件中获取您正在寻找的内容:)
我能够间接解决这个问题。
我一直在寻找自定义 KeyCloak 身份验证异常的解决方案。
看我的回答:Keycloak get 401 error, but spring security does not handle this error
我还注意到,在进行故障排除时,无效的过期令牌验证进行了两次。
在我添加 CustomKeycloakAuthenticationFailureHandler 之后,我意识到无效令牌只被验证一次。
默认的 KeycloakAuthenticationFailureHandler 有一行:response.sendError(401, "Unable to authenticate using the Authorization header");
如果出于某种原因执行此行,您将遇到双重验证和响应 header。我为我的解决方案删除了这一行,并添加了我自己的输出响应。
我正在使用带有 keycloak 的 spring 安全性,如果我使用无效令牌在特定端点上发出请求,看起来令牌验证已完成 2 次,我也尝试实施我自己的身份验证提供程序使用 keycloak 身份验证提供程序的逻辑并覆盖了 BearerTokenRequestAuthenticator 进行令牌验证但它仍然做同样的事情..我不确定问题是否来自某种bean定义 这是相同的日志,您可以在其中看到字符串“Verifying access_token”出现了 2 次。
2021-04-16 16:35:18,220 DEBUG 16672 --- [org.keycloak.adapters.PreAuthActionsHandler]: adminRequest http://localhost:7006/gateway/core/rest/api/core/initPayment/100356
2021-04-16 16:35:18,220 DEBUG 16672 --- [org.apache.catalina.authenticator.AuthenticatorBase]: Security checking request POST /gateway/core/rest/api/core/initPayment/100356
2021-04-16 16:35:18,220 DEBUG 16672 --- [org.apache.catalina.realm.RealmBase]: No applicable constraints defined
2021-04-16 16:35:18,220 DEBUG 16672 --- [org.apache.catalina.authenticator.AuthenticatorBase]: Not subject to any constraint
2021-04-16 16:35:18,220 DEBUG 16672 --- [org.keycloak.adapters.tomcat.AbstractAuthenticatedActionsValve]: AuthenticatedActionsValve.invoke /gateway/core/rest/api/core/initPayment/100356
2021-04-16 16:35:18,220 DEBUG 16672 --- [org.keycloak.adapters.AuthenticatedActionsHandler]: AuthenticatedActionsValve.invoke http://localhost:7006/gateway/core/rest/api/core/initPayment/100356
2021-04-16 16:35:18,220 DEBUG 16672 --- [org.keycloak.adapters.AuthenticatedActionsHandler]: Policy enforcement is disabled.
2021-04-16 16:35:18,220 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /core/rest/api/core/initPayment/100356 at position 1 of 15 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2021-04-16 16:35:18,220 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /core/rest/api/core/initPayment/100356 at position 2 of 15 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /core/rest/api/core/initPayment/100356 at position 3 of 15 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /core/rest/api/core/initPayment/100356 at position 4 of 15 in additional filter chain; firing Filter: 'CorsFilter'
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /core/rest/api/core/initPayment/100356 at position 5 of 15 in additional filter chain; firing Filter: 'KeycloakPreAuthActionsFilter'
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.keycloak.adapters.PreAuthActionsHandler]: adminRequest http://localhost:7006/gateway/core/rest/api/core/initPayment/100356
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /core/rest/api/core/initPayment/100356 at position 6 of 15 in additional filter chain; firing Filter: 'KeycloakAuthenticationProcessingFilter'
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.springframework.security.web.util.matcher.OrRequestMatcher]: Trying to match using Ant [pattern='/sso/login']
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.springframework.security.web.util.matcher.AntPathRequestMatcher]: Checking match of request : '/core/rest/api/core/initPayment/100356'; against '/sso/login'
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.springframework.security.web.util.matcher.OrRequestMatcher]: Trying to match using RequestHeaderRequestMatcher [expectedHeaderName=Authorization, expectedHeaderValue=null]
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.springframework.security.web.util.matcher.OrRequestMatcher]: matched
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter]: Request is to process authentication
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter]: Attempting Keycloak authentication
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.keycloak.adapters.BearerTokenRequestAuthenticator]: Found [1] values in authorization header, selecting the first value for Bearer.
2021-04-16 16:35:18,221 DEBUG 16672 --- [org.keycloak.adapters.BearerTokenRequestAuthenticator]: Verifying access_token
2021-04-16 16:35:18,222 DEBUG 16672 --- [org.keycloak.adapters.BearerTokenRequestAuthenticator]: Failed to verify token
2021-04-16 16:35:18,222 DEBUG 16672 --- [org.keycloak.adapters.RequestAuthenticator]: Bearer FAILED
2021-04-16 16:35:18,222 DEBUG 16672 --- [org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter]: Auth outcome: FAILED
2021-04-16 16:35:18,223 DEBUG 16672 --- [org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter]: Authentication request failed: org.keycloak.adapters.springsecurity.KeycloakAuthenticationException: Invalid authorization header, see WWW-Authenticate header for details
org.keycloak.adapters.springsecurity.KeycloakAuthenticationException: Invalid authorization header, see WWW-Authenticate header for details
at org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter.attemptAuthentication(KeycloakAuthenticationProcessingFilter.java:162) ~[keycloak-spring-security-adapter-9.0.0.jar:9.0.0]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter.doFilter(KeycloakPreAuthActionsFilter.java:96) ~[keycloak-spring-security-adapter-9.0.0.jar:9.0.0]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:92) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:93) ~[spring-boot-actuator-2.3.9.RELEASE.jar:2.3.9.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.keycloak.adapters.tomcat.AbstractAuthenticatedActionsValve.invoke(AbstractAuthenticatedActionsValve.java:67) ~[spring-boot-container-bundle-9.0.0.jar:9.0.0]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.keycloak.adapters.tomcat.AbstractKeycloakAuthenticatorValve.invoke(AbstractKeycloakAuthenticatorValve.java:181) ~[spring-boot-container-bundle-9.0.0.jar:9.0.0]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:346) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:887) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1684) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]
2021-04-16 16:35:18,223 DEBUG 16672 --- [org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter]: Updated SecurityContextHolder to contain null Authentication
2021-04-16 16:35:18,223 DEBUG 16672 --- [org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter]: Delegating to authentication failure handler org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationFailureHandler@331b3993
2021-04-16 16:35:18,223 DEBUG 16672 --- [org.springframework.security.web.header.writers.HstsHeaderWriter]: Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@784a6674
2021-04-16 16:35:18,223 DEBUG 16672 --- [org.springframework.security.web.context.SecurityContextPersistenceFilter]: SecurityContextHolder now cleared, as request processing completed
2021-04-16 16:35:18,223 DEBUG 16672 --- [org.apache.catalina.core.ContainerBase.[Tomcat].[localhost]]: Processing ErrorPage[errorCode=0, location=/error]
2021-04-16 16:35:18,223 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /error at position 1 of 15 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2021-04-16 16:35:18,224 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /error at position 2 of 15 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /error at position 3 of 15 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /error at position 4 of 15 in additional filter chain; firing Filter: 'CorsFilter'
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /error at position 5 of 15 in additional filter chain; firing Filter: 'KeycloakPreAuthActionsFilter'
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.keycloak.adapters.PreAuthActionsHandler]: adminRequest http://localhost:7006/gateway/error
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.springframework.security.web.FilterChainProxy]: /error at position 6 of 15 in additional filter chain; firing Filter: 'KeycloakAuthenticationProcessingFilter'
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.springframework.security.web.util.matcher.OrRequestMatcher]: Trying to match using Ant [pattern='/sso/login']
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.springframework.security.web.util.matcher.AntPathRequestMatcher]: Checking match of request : '/error'; against '/sso/login'
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.springframework.security.web.util.matcher.OrRequestMatcher]: Trying to match using RequestHeaderRequestMatcher [expectedHeaderName=Authorization, expectedHeaderValue=null]
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.springframework.security.web.util.matcher.OrRequestMatcher]: matched
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter]: Request is to process authentication
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter]: Attempting Keycloak authentication
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.keycloak.adapters.BearerTokenRequestAuthenticator]: Found [1] values in authorization header, selecting the first value for Bearer.
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.keycloak.adapters.BearerTokenRequestAuthenticator]: Verifying access_token
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.keycloak.adapters.BearerTokenRequestAuthenticator]: Failed to verify token
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.keycloak.adapters.RequestAuthenticator]: Bearer FAILED
2021-04-16 16:35:18,225 DEBUG 16672 --- [org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter]: Auth outcome: FAILED
2021-04-16 16:35:18,226 DEBUG 16672 --- [org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter]: Authentication request failed: org.keycloak.adapters.springsecurity.KeycloakAuthenticationException: Invalid authorization header, see WWW-Authenticate header for details
org.keycloak.adapters.springsecurity.KeycloakAuthenticationException: Invalid authorization header, see WWW-Authenticate header for details
at org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter.attemptAuthentication(KeycloakAuthenticationProcessingFilter.java:162) ~[keycloak-spring-security-adapter-9.0.0.jar:9.0.0]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter.doFilter(KeycloakPreAuthActionsFilter.java:96) ~[keycloak-spring-security-adapter-9.0.0.jar:9.0.0]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:103) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:103) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:103) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) ~[spring-security-web-5.3.8.RELEASE.jar:5.3.8.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:103) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:103) ~[spring-web-5.2.13.RELEASE.jar:5.2.13.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:710) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:459) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:384) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:312) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.StandardHostValve.custom(StandardHostValve.java:398) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.StandardHostValve.status(StandardHostValve.java:257) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:179) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:346) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:887) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1684) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.43.jar:9.0.43]
at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]
2021-04-16 16:35:18,226 DEBUG 16672 ---
这是我的安全配置 class
@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
@KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
private static final String[] SWAGGER_RESOURCES_WHITELIST = {
"/authenticate/rest/api/authenticate/v2/api-docs",
"/core/rest/api/core/v2/api-docs",
"/v2/api-docs",
"/swagger-resources",
"/swagger-resources/**",
"/configuration/ui",
"/configuration/security",
"/swagger-ui.html",
"/webjars/**",
"/v3/api-docs/**",
"/swagger-ui/**",
"/swagger-ui.html"
};
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.cors().and().csrf().disable().sessionManagement().
sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()
.antMatchers("/authenticate/rest/api/authenticate/token").permitAll()
.antMatchers("/authenticate/rest/api/authenticate/refreshToken").permitAll()
.anyRequest().authenticated();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return source;
}
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new NullAuthenticatedSessionStrategy();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
/* remove default spring "ROLE_" prefix appending to keycloak's roles*/
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
// *************************** Avoid Bean redefinition ********************************
@Bean
public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean(
KeycloakAuthenticationProcessingFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Bean
public FilterRegistrationBean keycloakPreAuthActionsFilterRegistrationBean(
KeycloakPreAuthActionsFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Bean
public FilterRegistrationBean keycloakAuthenticatedActionsFilterBean(
KeycloakAuthenticatedActionsFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Bean
public FilterRegistrationBean keycloakSecurityContextRequestFilterBean(
KeycloakSecurityContextRequestFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Bean
@Override
@ConditionalOnMissingBean(HttpSessionManager.class)
protected HttpSessionManager httpSessionManager() {
return new HttpSessionManager();
}
}
您可以添加 JwtAuthorizationTokenFilter 并在每次请求之前调用它
在您的安全配置中使用 addFilterBefore()
// SecurityConfig changes
private final JwtAuthenticationEntryPoint unauthorizedHandler;
private final JwtAuthorizationTokenFilter authenticationTokenFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// we don't need CSRF because our token is invulnerable
.csrf().disable()
.exceptionHandling()
.defaultAuthenticationEntryPointFor(
getRestAuthenticationEntryPoint(),
new AntPathRequestMatcher("/**")
)
.authenticationEntryPoint(unauthorizedHandler).and()
// don't create session
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// system state endpoint
.antMatchers("/ping").permitAll()
// User authentication actions
.antMatchers("/auth" + "/**").permitAll()
.antMatchers("/**/*.css").permitAll()
.anyRequest().authenticated()
;
http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
;
// disable page caching
http
.headers()
.frameOptions().sameOrigin()
.cacheControl();
}
JwtAuthorizationTokenFilter class
@Component
@AllArgsConstructor
public class JwtAuthorizationTokenFilter extends OncePerRequestFilter {
public static final String AUTHORIZATION_HEADER = HttpHeaders.AUTHORIZATION;
public static final String AUTHORIZATION_TOKEN_TYPE = "Bearer";
private final SSOManager ssoManager;
private final SimpleAuthorityMapper simpleAuthorityMapper = new SimpleAuthorityMapper();
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String requestURI = request.getRequestURI();
boolean isRefreshRequest = ("/auth" + "/refresh").equals(requestURI);
final String requestHeader = request.getHeader(AUTHORIZATION_HEADER);
final String authenticationHeader = AUTHORIZATION_TOKEN_TYPE + " ";
if (requestHeader != null && requestHeader.startsWith(authenticationHeader)) {
try {
String authToken = requestHeader.substring(authenticationHeader.length());
AccessToken accessToken = isRefreshRequest ?
ssoManager.loadAccessTokenFromRefreshToken(authToken) :
ssoManager.loadAccessToken(authToken);
if (accessToken != null) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication instanceof AnonymousAuthenticationToken || authentication == null) {
// security context was null, so authorizing user
JwtKeycloakAuthenticationToken jwtKeycloakAuthenticationToken = createJwtKeycloakAuthenticationToken(accessToken);
SecurityContextHolder.getContext().setAuthentication(jwtKeycloakAuthenticationToken);
}
}
} catch (Exception e) {
// ToDo: improve error handling
// the token is expired and not valid anymore, TokenNotActiveException
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
return;
}
}
chain.doFilter(request, response);
}
public JwtKeycloakAuthenticationToken createJwtKeycloakAuthenticationToken(AccessToken accessToken) {
String username = accessToken.getPreferredUsername();
Map<String, AccessToken.Access> resourceAccess = accessToken.getResourceAccess();
UserPrincipal userPrincipal = new UserPrincipal(username);
Set<GrantedAuthority> authorities = getAuthorities(resourceAccess);
UserProfileDto userProfile = buildUserProfile(accessToken);
JwtKeycloakAuthenticationToken jwtKeycloakAuthenticationToken = new JwtKeycloakAuthenticationToken(userPrincipal, authorities, accessToken, userProfile);
return jwtKeycloakAuthenticationToken;
}
Set<GrantedAuthority> getAuthorities(Map<String, AccessToken.Access> resourceAccess) {
Set<GrantedAuthority> roles = new HashSet<>();
resourceAccess.forEach((key, value) -> {
Set<SimpleGrantedAuthority> realmRoles = value.getRoles().stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toSet());
roles.addAll(realmRoles);
});
Set<GrantedAuthority> grantedAuthorities = simpleAuthorityMapper.mapAuthorities(roles);
return grantedAuthorities;
}
public UserProfileDto buildUserProfile(AccessToken accessToken) {
return UserProfileDto.builder()
.userId(accessToken.getSubject())
.username(accessToken.getPreferredUsername())
.fullName(accessToken.getName())
.firstName(accessToken.getGivenName())
.lastName(accessToken.getFamilyName())
.email(accessToken.getEmail())
.roles(getResourceRoles(accessToken))
.build();
}
private static Set<String> getResourceRoles(AccessToken accessToken) {
final String realmClient = accessToken.getIssuedFor();
final Map<String, AccessToken.Access> resourceAccess = accessToken.getResourceAccess();
if (resourceAccess.containsKey(realmClient)) {
final AccessToken.Access access = resourceAccess.get(realmClient);
return access.getRoles();
}
return Collections.emptySet();
}
}
我们使用的一些自定义 SSOManager 接口:
public interface SSOManager {
/**
* This method will verify user access token and provide userId if token is valid. in case of
* invalid access token it will throw ProjectCommon exception with 401.
*/
AccessToken loadAccessToken(String token) throws TokenNotActiveException, VerificationException, NoSuchFieldException;
AccessToken loadAccessTokenFromRefreshToken(String token) throws TokenNotActiveException, VerificationException, NoSuchFieldException;
/**
* this method will do the user login with key cloak. after login it will provide access token object.
*/
AccessTokenResponse login(String userName, String password);
AccessTokenResponse refresh(String refreshToken);
void logout(String refreshToken);
}
并且 KeyCloakServiceImpl 实现了具有所有身份验证魔法的 SSOManager:
@Component
@Slf4j
public class KeyCloakServiceImpl implements SSOManager {
private RestTemplate restTemplate;
private final KeyCloakConnectionProvider keyCloakConnectionProvider;
@Autowired
public KeyCloakServiceImpl(KeyCloakConnectionProvider keyCloakConnectionProvider,
RestTemplateBuilder restTemplateBuilder) throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
this.keyCloakConnectionProvider = keyCloakConnectionProvider;
TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
.loadTrustMaterial(null, acceptingTrustStrategy)
.build();
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(csf)
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
this.restTemplate = restTemplateBuilder
.requestFactory(requestFactory)
.messageConverters(new MappingJackson2HttpMessageConverter(), new FormHttpMessageConverter())
.build();
}
private AccessToken getAccessToken(String accessToken, boolean checkActive) throws VerificationException, NoSuchFieldException {
try {
PublicKey publicKey = getPublicKey();
if (publicKey != null) {
String realmUrl = keyCloakConnectionProvider.getRealmUrl();
AccessToken token =
RSATokenVerifier.verifyToken(
accessToken,
publicKey,
realmUrl,
checkActive,
true);
return token;
} else {
log.error("KeyCloakServiceImpl:verifyToken: SSO_PUBLIC_KEY is NULL.");
throw new NoSuchFieldException("KeyCloakServiceImpl:verifyToken: SSO_PUBLIC_KEY is NULL.");
}
} catch (TokenNotActiveException e) {
throw e;
} catch (VerificationException e) {
throw e;
} catch (NoSuchFieldException e) {
throw e;
} catch (Exception e) {
throw e;
}
}
@Override
public AccessToken loadAccessToken(String accessToken) throws TokenNotActiveException, VerificationException, NoSuchFieldException {
return getAccessToken(accessToken, true);
}
@Override
public AccessToken loadAccessTokenFromRefreshToken(String accessToken) throws TokenNotActiveException, VerificationException, NoSuchFieldException {
return getAccessToken(accessToken, false);
}
/**
* This method will call keycloak service to user login. after successful login it will provide
* access token.
*/
@Override
public AccessTokenResponse login(String username, String password) {
try {
MultiValueMap<String, String> requestParams = new LinkedMultiValueMap<>();
requestParams.add("client_id", keyCloakConnectionProvider.getResource());
requestParams.add("username", username);
requestParams.add("password", password);
requestParams.add("grant_type", "password");
requestParams.add("client_secret", keyCloakConnectionProvider.getClientSecret());
requestParams.add("scope", "openid");
AccessTokenResponse keycloakAccessToken = queryKeycloakByParams(requestParams);
return keycloakAccessToken;
} catch (Exception e) {
log.info(e.getMessage(), e);
throw e;
}
}
@Override
public AccessTokenResponse refresh(String refreshToken) {
try {
MultiValueMap<String, String> requestParams = new LinkedMultiValueMap<>();
requestParams.add("client_id", keyCloakConnectionProvider.getResource());
requestParams.add("grant_type", "refresh_token");
requestParams.add("client_secret", keyCloakConnectionProvider.getClientSecret());
requestParams.add("refresh_token", refreshToken);
AccessTokenResponse keycloakAccessToken = queryKeycloakByParams(requestParams);
return keycloakAccessToken;
} catch (Exception e) {
log.info(e.getMessage(), e);
throw e;
}
}
private AccessTokenResponse queryKeycloakByParams(MultiValueMap<String, String> requestParams) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(requestParams, headers);
String url = keyCloakConnectionProvider.getOpenIdConnectTokenUrl();
AccessTokenResponse keycloakAccessToken = getAccessTokenResponse(request, url);
return keycloakAccessToken;
}
private AccessTokenResponse getAccessTokenResponse(HttpEntity<MultiValueMap<String, String>> request, String url) {
try {
ResponseEntity<AccessTokenResponse> response = restTemplate.postForEntity(url, request, AccessTokenResponse.class);
return response.getBody();
} catch (ResourceAccessException e) {
log.error("KeyCloak getAccessTokenResponse: " + e.getMessage());
try {
ResponseEntity<AccessTokenResponse> response = restTemplate.postForEntity(url, request, AccessTokenResponse.class);
return response.getBody();
} catch (Exception ex) {
throw ex;
}
} catch (Exception e) {
throw e;
}
}
@Override
public void logout(String refreshToken) {
try {
MultiValueMap<String, String> requestParams = new LinkedMultiValueMap<>();
requestParams.add("client_id", keyCloakConnectionProvider.getResource());
requestParams.add("client_secret", keyCloakConnectionProvider.getClientSecret());
requestParams.add("refresh_token", refreshToken);
logoutUserSession(requestParams);
} catch (Exception e) {
log.info(e.getMessage(), e);
throw e;
}
}
private void logoutUserSession(MultiValueMap<String, String> requestParams) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(requestParams, headers);
String url = keyCloakConnectionProvider.getOpenIdConnectLogoutUrl();
restTemplate.postForEntity(url, request, Object.class);
}
private PublicKey getPublicKey() {
PublicKey publicKey = keyCloakConnectionProvider.getPublicKey();
if (publicKey == null) {
LinkedHashMap publicKeyMap = requestKeyFromKeycloak(keyCloakConnectionProvider.getOpenIdConnectCertsUrl());
publicKey = KeyCloakRsaKeyLoader.getPublicKeyFromKeyCloak(publicKeyMap);
keyCloakConnectionProvider.setPublicKey(publicKey);
}
return publicKey;
}
/**
* This method will connect to keycloak server using API call for getting public key.
*
* @param url A string value having keycloak base URL
* @return Public key JSON response string
*/
private LinkedHashMap requestKeyFromKeycloak(String url) {
try {
ResponseEntity<LinkedHashMap> response = restTemplate.getForEntity(url, LinkedHashMap.class);
LinkedHashMap body = response.getBody();
if (body != null) {
return body;
} else {
log.error("KeyCloakRsaKeyLoader:requestKeyFromKeycloak: Not able to fetch SSO public key from keycloak server");
}
} catch (Exception e) {
log.error("KeyCloakRsaKeyLoader:requestKeyFromKeycloak: Exception occurred with message = " + e.getMessage());
}
return null;
}
}
和 KeyCloakConnectionProvider 使用属性
@Component
@Slf4j
@AllArgsConstructor
public class KeyCloakConnectionProvider {
private static PropertiesCache cache = PropertiesCache.getInstance();
private KeycloakSpringBootProperties keycloakProperties;
public String getAuthServerUrl() {
return keycloakProperties.getAuthServerUrl();
}
public String getRealmUrl() {
return getAuthServerUrl()
+ "/realms/"
+ getRealm();
}
public String getOpenIdConnectUrl() {
return getRealmUrl() + "/protocol/openid-connect";
}
public String getOpenIdConnectTokenUrl() {
return getOpenIdConnectUrl() + "/token";
}
public String getOpenIdConnectLogoutUrl() {
return getOpenIdConnectUrl() + "/logout";
}
public String getOpenIdConnectCertsUrl() {
return getOpenIdConnectUrl() + "/certs";
}
public String getRealm() {
return keycloakProperties.getRealm();
}
public String getResource() {
return keycloakProperties.getResource();
}
public String getClientId() {
return getResource();
}
public String getClientSecret() {
return String.valueOf(keycloakProperties.getCredentials().get("secret"));
}
public int getConnectionPoolSize() {
return keycloakProperties.getConnectionPoolSize();
}
public PublicKey getPublicKey() {
return cache.getPublicKey();
}
public PublicKey setPublicKey(PublicKey publicKey) {
if (publicKey != null) {
cache.savePublicKey(publicKey);
}
return cache.getPublicKey();
}
}
尝试从这些文件中获取您正在寻找的内容:)
我能够间接解决这个问题。 我一直在寻找自定义 KeyCloak 身份验证异常的解决方案。
看我的回答:Keycloak get 401 error, but spring security does not handle this error
我还注意到,在进行故障排除时,无效的过期令牌验证进行了两次。
在我添加 CustomKeycloakAuthenticationFailureHandler 之后,我意识到无效令牌只被验证一次。
默认的 KeycloakAuthenticationFailureHandler 有一行:response.sendError(401, "Unable to authenticate using the Authorization header");
如果出于某种原因执行此行,您将遇到双重验证和响应 header。我为我的解决方案删除了这一行,并添加了我自己的输出响应。