Estou construindo um aplicativo Spring Boot usando o padrão Backend for Frontend (BFF) . Estou usando React para o frontend, Spring Cloud Gateway como o cliente OAuth, Keycloak como o servidor de autorização e um projeto Spring Boot como o servidor de recursos OAuth. Estou usando token relay para enviar o token para o servidor de recursos.
Primeiro, esse é um padrão bom e seguro?
Em segundo lugar, estou recebendo este erro no servidor de recursos
2025-02-13T16:43:00.494+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Trying to match request against DefaultSecurityFilterChain [RequestMatcher=any request, Filters=[org.springframework.security.web.session.DisableEncodeUrlFilter@20d99e58, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@21011db6, org.springframework.security.web.context.SecurityContextHolderFilter@2e86807a, org.springframework.security.web.header.HeaderWriterFilter@43b5021c, org.springframework.security.web.csrf.CsrfFilter@3451f01d, org.springframework.security.web.authentication.logout.LogoutFilter@49a6f486, org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter@705a8dbc, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@590f0c50, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@3a4ab7f7, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@728d949d, org.springframework.security.web.access.ExceptionTranslationFilter@6bee793f, org.springframework.security.web.access.intercept.AuthorizationFilter@4ecd00b5]] (1/1)
2025-02-13T16:43:00.494+05:30 DEBUG 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Securing GET /
2025-02-13T16:43:00.494+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Invoking DisableEncodeUrlFilter (1/12)
...................other filters
2025-02-13T16:43:00.495+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.csrf.CsrfFilter : Did not protect against CSRF since request did not match And [CsrfNotRequired [TRACE, HEAD, GET, OPTIONS], Not [Or [org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer$BearerTokenRequestMatcher@7cfb4736]]]
2025-02-13T16:43:00.495+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Invoking LogoutFilter (6/12)
2025-02-13T16:43:00.495+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.s.w.a.logout.LogoutFilter : Did not match request to Ant [pattern='/logout', POST]
2025-02-13T16:43:00.495+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Invoking BearerTokenAuthenticationFilter (7/12)
2025-02-13T16:43:00.510+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.s.authentication.ProviderManager : Authenticating request with JwtAuthenticationProvider (1/2)
2025-02-13T16:43:00.543+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] s.o.s.r.a.JwtGrantedAuthoritiesConverter : Looking for scopes in claim resource_access.spring-client.roles
2025-02-13T16:43:00.544+05:30 DEBUG 20028 --- [resource-server] [nio-9092-exec-8] o.s.s.o.s.r.a.JwtAuthenticationProvider : Authenticated token
2025-02-13T16:43:00.544+05:30 DEBUG 20028 --- [resource-server] [nio-9092-exec-8] .s.r.w.a.BearerTokenAuthenticationFilter : Set SecurityContextHolder to JwtAuthenticationToken [Principal=org.springframework.security.oauth2.jwt.Jwt@e76a89ac, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=127.0.0.1, SessionId=null], Granted Authorities=[]]
2025-02-13T16:43:00.544+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Invoking RequestCacheAwareFilter (8/12)
2025-02-13T16:43:00.544+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.s.w.s.HttpSessionRequestCache : matchingRequestParameterName is required for getMatchingRequest to lookup a value, but not provided
2025-02-13T16:43:00.544+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Invoking SecurityContextHolderAwareRequestFilter (9/12)
2025-02-13T16:43:00.544+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Invoking AnonymousAuthenticationFilter (10/12)
2025-02-13T16:43:00.544+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Invoking ExceptionTranslationFilter (11/12)
2025-02-13T16:43:00.544+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Invoking AuthorizationFilter (12/12)
2025-02-13T16:43:00.544+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] estMatcherDelegatingAuthorizationManager : Authorizing GET /
2025-02-13T16:43:00.544+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] estMatcherDelegatingAuthorizationManager : Checking authorization on GET / using org.springframework.security.authorization.AuthenticatedAuthorizationManager@357287af
2025-02-13T16:43:00.544+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.s.w.a.AnonymousAuthenticationFilter : Did not set SecurityContextHolder since already authenticated JwtAuthenticationToken [Principal=org.springframework.security.oauth2.jwt.Jwt@e76a89ac, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=127.0.0.1, SessionId=null], Granted Authorities=[]]
2025-02-13T16:43:00.544+05:30 DEBUG 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Secured GET /
2025-02-13T16:43:00.549+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match request to [Is Secure]
2025-02-13T16:43:00.549+05:30 ERROR 20028 --- [resource-server] [nio-9092-exec-8] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.oauth2.jwt.Jwt]: Constructor threw exception] with root cause
java.lang.IllegalArgumentException: tokenValue cannot be empty
at org.springframework.util.Assert.hasText(Assert.java:240) ~[spring-core-6.1.12.jar:6.1.12]
at org.springframework.security.oauth2.core.AbstractOAuth2Token.<init>(AbstractOAuth2Token.java:61) ~[spring-security-oauth2-core-6.3.3.jar:6.3.3]
at org.springframework.security.oauth2.jwt.Jwt.<init>(Jwt.java:67) ~[spring-security-oauth2-jose-6.3.3.jar:6.3.3]
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:na]
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) ~[na:na]
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480) ~[na:na]
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:208) ~[spring-beans-6.1.12.jar:6.1.12]
at org.springframework.validation.DataBinder.createObject(DataBinder.java:994) ~[spring-context-6.1.12.jar:6.1.12]
at org.springframework.validation.DataBinder.construct(DataBinder.java:903) ~[spring-context-6.1.12.jar:6.1.12]
at org.springframework.web.bind.ServletRequestDataBinder.construct(ServletRequestDataBinder.java:116) ~[spring-web-6.1.12.jar:6.1.12]
at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.constructAttribute(ServletModelAttributeMethodProcessor.java:157) ~[spring-webmvc-6.1.12.jar:6.1.12]
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:148) ~[spring-web-6.1.12.jar:6.1.12]
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122) ~[spring-web-6.1.12.jar:6.1.12]
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:224) ~[spring-web-6.1.12.jar:6.1.12]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:178) ~[spring-web-6.1.12.jar:6.1.12]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) ~[spring-webmvc-6.1.12.jar:6.1.12]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:926) ~[spring-webmvc-6.1.12.jar:6.1.12]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:831) ~[spring-webmvc-6.1.12.jar:6.1.12]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-6.1.12.jar:6.1.12]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) ~[spring-webmvc-6.1.12.jar:6.1.12]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[spring-webmvc-6.1.12.jar:6.1.12]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-6.1.12.jar:6.1.12]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) ~[spring-webmvc-6.1.12.jar:6.1.12]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) ~[tomcat-embed-core-10.1.28.jar:6.0]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[spring-webmvc-6.1.12.jar:6.1.12]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[tomcat-embed-core-10.1.28.jar:6.0]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) ~[tomcat-embed-core-10.1.28.jar:10.1.28]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.28.jar:10.1.28]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-10.1.28.jar:10.1.28]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.28.jar:10.1.28]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.28.jar:10.1.28]
at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) ~[spring-web-6.1.12.jar:6.1.12]
at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:365) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:100) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter.doFilterInternal(BearerTokenAuthenticationFilter.java:145) ~[spring-security-oauth2-resource-server-6.3.3.jar:6.3.3]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.12.jar:6.1.12]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:117) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.12.jar:6.1.12]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.12.jar:6.1.12]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.12.jar:6.1.12]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.12.jar:6.1.12]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) ~[spring-security-web-6.3.3.jar:6.3.3]
at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) ~[spring-web-6.1.12.jar:6.1.12]
at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$3(HandlerMappingIntrospector.java:195) ~[spring-webmvc-6.1.12.jar:6.1.12]
at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) ~[spring-web-6.1.12.jar:6.1.12]
at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) ~[spring-web-6.1.12.jar:6.1.12]
at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:230) ~[spring-security-config-6.3.3.jar:6.3.3]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:352) ~[spring-web-6.1.12.jar:6.1.12]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:268) ~[spring-web-6.1.12.jar:6.1.12]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.28.jar:10.1.28]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.28.jar:10.1.28]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.1.12.jar:6.1.12]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.12.jar:6.1.12]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.28.jar:10.1.28]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.28.jar:10.1.28]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.1.12.jar:6.1.12]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.12.jar:6.1.12]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.28.jar:10.1.28]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.28.jar:10.1.28]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.1.12.jar:6.1.12]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:1
--- other errros
2025-02-13T16:43:00.555+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Trying to match request against DefaultSecurityFilterChain [RequestMatcher=any request, Filters=[org.springframework.security.web.session.DisableEncodeUrlFilter@20d99e58, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@21011db6, org.springframework.security.web.context.SecurityContextHolderFilter@2e86807a, org.springframework.security.web.header.HeaderWriterFilter@43b5021c, org.springframework.security.web.csrf.CsrfFilter@3451f01d, org.springframework.security.web.authentication.logout.LogoutFilter@49a6f486, org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter@705a8dbc, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@590f0c50, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@3a4ab7f7, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@728d949d, org.springframework.security.web.access.ExceptionTranslationFilter@6bee793f, org.springframework.security.web.access.intercept.AuthorizationFilter@4ecd00b5]] (1/1)
2025-02-13T16:43:00.557+05:30 DEBUG 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Securing GET /error
2025-02-13T16:43:00.557+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Invoking DisableEncodeUrlFilter (1/12)
2025-02-13T16:43:00.557+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Invoking WebAsyncManagerIntegrationFilter (2/12)
2025-02-13T16:43:00.557+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Invoking SecurityContextHolderFilter (3/12)
2025-02-13T16:43:00.557+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Invoking HeaderWriterFilter (4/12)
2025-02-13T16:43:00.557+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Invoking CsrfFilter (5/12)
2025-02-13T16:43:00.557+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Invoking LogoutFilter (6/12)
2025-02-13T16:43:00.557+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.s.w.a.logout.LogoutFilter : Did not match request to Ant [pattern='/logout', POST]
2025-02-13T16:43:00.557+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Invoking BearerTokenAuthenticationFilter (7/12)
2025-02-13T16:43:00.557+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Invoking RequestCacheAwareFilter (8/12)
2025-02-13T16:43:00.557+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.s.w.s.HttpSessionRequestCache : matchingRequestParameterName is required for getMatchingRequest to lookup a value, but not provided
2025-02-13T16:43:00.557+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Invoking SecurityContextHolderAwareRequestFilter (9/12)
2025-02-13T16:43:00.557+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Invoking AnonymousAuthenticationFilter (10/12)
2025-02-13T16:43:00.557+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Invoking ExceptionTranslationFilter (11/12)
2025-02-13T16:43:00.558+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Invoking AuthorizationFilter (12/12)
2025-02-13T16:43:00.558+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] estMatcherDelegatingAuthorizationManager : Authorizing GET /error
2025-02-13T16:43:00.558+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] estMatcherDelegatingAuthorizationManager : Checking authorization on GET /error using org.springframework.security.authorization.AuthenticatedAuthorizationManager@357287af
2025-02-13T16:43:00.558+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2025-02-13T16:43:00.558+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] .s.s.w.c.SupplierDeferredSecurityContext : Created SecurityContextImpl [Null authentication]
2025-02-13T16:43:00.558+05:30 TRACE 20028 --- [resource-server] [nio-9092-exec-8] o.s.s.w.a.AnonymousAuthenticationFilter : Did not set SecurityContextHolder since already authenticated JwtAuthenticationToken [Principal=org.springframework.security.oauth2.jwt.Jwt@e76a89ac, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=127.0.0.1, SessionId=null], Granted Authorities=[]]
2025-02-13T16:43:00.558+05:30 DEBUG 20028 --- [resource-server] [nio-9092-exec-8] o.s.security.web.FilterChainProxy : Secured GET /error
Aqui está meu cliente OAuth (porta 9091) application.yml:
spring:
application:
name: oauth2-client
security:
oauth2:
client:
registration:
spring-client:
client-id: "spring-client"
client-secret: "dumDmmCxJm0oTH4aUnGpYbocDxLceSOq"
client-name: "Spring Boot Client 1"
client-authentication-method: "client_secret_basic"
authorization-grant-type: "authorization_code"
provider: "spring-client"
scope:
- openid
- read
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
provider:
spring-client:
issuer-uri: "http://localhost:8080/realms/test"
cloud:
gateway:
routes:
- id: resource-server
uri: http://localhost:9092
predicates:
- Path=/**
filters:
- TokenRelay=
server:
port: 9091
logging:
level:
org.springframework.security: trace
# 8080 - auth server, keycloak
# 9091 - client, gateway
# 9092 - resource server
Aqui está o código do meu servidor de recursos (porta 9092) (por favor, esqueça o estilo do código; este é apenas um POC rápido):
@SpringBootApplication
public class ResourceServerApplication {
public static void main(String[] args) {
SpringApplication.run(ResourceServerApplication.class, args);
}
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity
.authorizeHttpRequests(req -> req.anyRequest().authenticated())
.oauth2ResourceServer(rs -> rs.jwt(
jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter())
))
.build();
}
@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
grantedAuthoritiesConverter.setAuthoritiesClaimName("resource_access.spring-client.roles");
JwtAuthenticationConverter authenticationConverter = new JwtAuthenticationConverter();
authenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
return authenticationConverter;
}
}
@RestController
class DemoController {
@GetMapping("/")
public String demo(Authentication authentication, Jwt jwt) {
System.out.println("Principal: " + authentication);
System.out.println("JWT Token: " + jwt.getTokenValue());
System.out.println("JWT Claims: " + jwt.getClaims());
return "Hello " + jwt.getSubject();
}
}
Aqui está meu aplicativo de servidor de recursos application.properties:
spring.application.name=resource-server
server.port=9092
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/realms/test
logging.level.org.springframework.security=TRACE
Aqui está a carga decodificada do token JWT que obtive ao depurar o filtro na primeira vez que ele foi chamado:
{
"exp": 1739443481,
"iat": 1739443181,
"auth_time": 1739442183,
"jti": "9359c44e-d352-4bf9-93a5-7b80954f418e",
"iss": "http://localhost:8080/realms/test",
"aud": "account",
"sub": "593ca545-528f-4fe9-9fcd-067f5b6e7a51",
"typ": "Bearer",
"azp": "spring-client",
"sid": "13830f59-6cd5-4e7c-b4e7-c5ac35d37cca",
"acr": "0",
"allowed-origins": [
"*"
],
"realm_access": {
"roles": [
"default-roles-test",
"offline_access",
"uma_authorization"
]
},
"resource_access": {
"spring-client": {
"roles": [
"read",
"openid"
]
},
"account": {
"roles": [
"manage-account",
"manage-account-links",
"view-profile"
]
}
},
"scope": "openid profile email",
"email_verified": false,
"name": "kush p",
"preferred_username": "kush",
"given_name": "kush",
"family_name": "p",
"email": "[email protected]"
}
Estou acessando a porta 9091 e sendo redirecionado para o Keycloak. Após o login, sou redirecionado de volta para a 9091 e recebo um erro 500.
Por que cada filtro está sendo chamado duas vezes? Tentei colocar um ponteiro de depuração no BearerTokenAuthenticationFilter. Na primeira vez, há um token, e na segunda, há um token nulo. Acho que o contexto de segurança está sendo perdido entre as chamadas. Por que isso está acontecendo?
O erro que você relata é devido ao Spring ignorar como injetar o
Jwt jwt
parâmetro no handler for@GetMapping("/")
. A implementação a seguir removerá esse erro:A propósito, esta implementação mostra que o seu
AuthenticationConverter
não funciona como esperado:JwtAuthenticationConverter
não usa o caminho JSON para interpretar o nome da reivindicação das autoridades, então suas funções de cliente não são mapeadas.O código que você fornece funcionaria com tal carga de token:
Não com um token emitido pelo Keycloak com mapeador de funções de cliente, como você incluiu na sua pergunta:
(Sim, nomes de propriedades JSON podem incluir pontos, e o 1º não é equivalente ao 2º)
Sim. Até onde eu sei, esse é o padrão mais seguro, e o que o Google, Facebook, Amazon, Microsoft, LinkedIn, etc. usam. Por que ele é melhor do que configurar Single-Page Applications como clientes OAuth2 públicos é explicado neste post da equipe do Spring Security (com 4 anos, mas ainda preciso).
Leia a documentação do
TokenRelay=
filtro novamente, não é assim que funciona. Os tokens são armazenados na sessão do gateway e o filtro substitui o cookie da sessão por um token Bearer (na sessão, não no cookie que contém não mais do que um ID de sessão, ou um novo se o token na sessão tiver expirado) ao rotear uma solicitação.Recomendo ler este artigo de Baedung que escrevi . Você está longe de dominar seu assunto e terá muitas surpresas ruins sem uma orientação forte.