We are running spring boot admin console inside Istio and trying to connect to pods with actuator end points. When SBAC tries to connect to a pod, it gives 502. We checked the logs and it sounds that SBAC is duplicating the IP address in the request URL
boot-admin-console 2023-01-12 19:26:54.480 DEBUG 1 --- [or-http-epoll-3] r.n.http.client.HttpClientOperations
: [db6578e5-10, L:/172.30.208.27:34026 - R:172.30.198.29/172.30.198.29:8080] Received response (auto-read:false) : RE
SPONSE(decodeResult: success, version: HTTP/1.1)
spring-boot-admin-console HTTP/1.1 200 OK
spring-boot-admin-console x-content-type-options:
spring-boot-admin-console x-xss-protection:
spring-boot-admin-console cache-control:
spring-boot-admin-console pragma:
spring-boot-admin-console expires:
spring-boot-admin-console x-frame-options:
spring-boot-admin-console content-type:
spring-boot-admin-console date:
spring-boot-admin-console x-envoy-upstream-service-time:
spring-boot-admin-console server:
spring-boot-admin-console transfer-encoding:
spring-boot-admin-console 2023-01-12 19:26:54.480 DEBUG 1 --- [or-http-epoll-3] r.n.r.DefaultPooledConnectionProvider
: [db6578e5-10, L:/172.30.208.27:34026 - R:172.30.198.29/172.30.198.29:8080] onStateChange(GET{uri=/actuator/health,
connection=PooledConnection{channel=[id: 0xdb6578e5, L:/172.30.208.27:34026 - R:172.30.198.29/172.30.198.29:8080]}}, [r
esponse_received])
Any ideas?
We have the headless service for the target service to allow connectivity between SBAC and the application pod. I curled the target pod actuator endpoint from the sbac pod and it is accessible but from the UI is giving 502
Version
Spring Boot Version: 2.6.5
Spring Cloud Version: 2021.0.0
The application built has the following characteristics:
It is a reactive application (Reactor + WebFlux)
It is an Oauth2 Client Application that uses Grant Type = Authorization Code.
It uses Spring Cloud Gateway to proxy Oauth2 Resources.
It uses Spring Session (Redis) to be able to accomplish transparent horizontal scaling.
Problem:
Requesting a resource that goes through the Spring Cloud Gateway to a downstream service returns a correct 200, but after the application is restarted while there is an active session saved in Redis it will return a 302. I find this to be a huge problem because it basically prevents horizontal scaling, which is my whole reason to implement Spring Session in Redis.
Steps to reproduce:
Start Application.
User requests loading application in the browser: https://localhost:9093/hello.html
Session is successfully created in Redis.
User redirected to the Oauth2 Server for Authentication
User Authenticates
User redirected to application after successful login.
Application loads in browser without a problem.
User requests a gateway resource: https://localhost:9093/entries
Resources loads fine with a 200.
Everything great so far.
Restart the server.
(Session still active, saved on Redis)
User requests the gateway resource (https://localhost:9093/entries).
Resources returns 302 and redirected to the Ouath Server (BAD)
Logs
The difference in logs when the resource loads vs does not load is the following:
Correct Loading
2022-03-30 18:25:06.655 [lettuce-kqueueEventLoop-5-1] DEBUG 90299 --- [PathPatternParserServerWebExchangeMatcher PathPatternParserServerWebExchangeMatcher.java:95->matches] : Checking match of request : '/wasd/selfservice/api/ccbConfiguration'; against '/wasd/**'
2022-03-30 18:25:06.655 [lettuce-kqueueEventLoop-5-1] DEBUG 90299 --- [OrServerWebExchangeMatcher OrServerWebExchangeMatcher.java:60->lambda$matches$2] : matched
2022-03-30 18:25:06.655 [lettuce-kqueueEventLoop-5-1] DEBUG 90299 --- [DelegatingReactiveAuthorizationManager DelegatingReactiveAuthorizationManager.java:55->lambda$check$1] : Checking authorization on '/wasd/selfservice/api/ccbConfiguration' using org.springframework.security.config.web.server.ServerHttpSecurity$AuthorizeExchangeSpec$Access$$Lambda$1069/0x0000000800995840#7ffc1fdb
2022-03-30 18:25:06.656 [lettuce-kqueueEventLoop-5-1] DEBUG 90299 --- [AuthorizationWebFilter AuthorizationWebFilter.java:52->lambda$filter$2] : Authorization successful
2022-03-30 18:25:06.656 [lettuce-kqueueEventLoop-5-1] DEBUG 90299 --- [RoutePredicateHandlerMapping RoutePredicateHandlerMapping.java:142->lambda$lookupRoute$6] : Route matched: ssa-resources
2022-03-30 18:25:06.656 [lettuce-kqueueEventLoop-5-1] DEBUG 90299 --- [RoutePredicateHandlerMapping RoutePredicateHandlerMapping.java:90->lambda$getHandlerInternal$0] : Mapping [Exchange: GET https://localhost:9093/wasdservice/wasd/selfservice/api/ccbConfiguration] to Route{id='ssa-resources', uri=https://wasd-api-int.company.com:443, order=0, predicate=Paths: [/wasdservice/wasd/**], match trailing slash: true, gatewayFilters=[[gov.miamidade.wasd.client.gateway.StripBasePathGatewayFilterFactory$$Lambda$1563/0x0000000800b4f040#4d05f800, order = 1], [org.springframework.cloud.gateway.filter.factory.TokenRelayGatewayFilterFactory$$Lambda$1564/0x0000000800b4f440#46c35be4, order = 2]], metadata={}}
2022-03-30 18:25:06.657 [lettuce-kqueueEventLoop-5-1] DEBUG 90299 --- [RoutePredicateHandlerMapping AbstractHandlerMapping.java:189->lambda$getHandler$1] : [cb1dc8d7-90] Mapped to org.springframework.cloud.gateway.handler.FilteringWebHandler#546da583
2022-03-30 18:25:06.657 [lettuce-kqueueEventLoop-5-1] DEBUG 90299 --- [FilteringWebHandler FilteringWebHandler.java:85->handle] : Sorted gatewayFilterFactories: [[GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.RemoveCachedBodyFilter#86e7c7a}, order = -2147483648], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter#29617475}, order = -2147482648], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyWriteResponseFilter#271df4c0}, order = -1], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ForwardPathFilter#22edca96}, order = 0], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.GatewayMetricsFilter#6e1bd2b}, order = 0], [gov.miamidade.wasd.client.gateway.StripBasePathGatewayFilterFactory$$Lambda$1563/0x0000000800b4f040#4d05f800, order = 1], [org.springframework.cloud.gateway.filter.factory.TokenRelayGatewayFilterFactory$$Lambda$1564/0x0000000800b4f440#46c35be4, order = 2], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter#6e863469}, order = 10000], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.config.GatewayNoLoadBalancerClientAutoConfiguration$NoLoadBalancerClientFilter#18a66406}, order = 10150], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.WebsocketRoutingFilter#591bff1d}, order = 2147483646], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyRoutingFilter#38379575}, order = 2147483647], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ForwardRoutingFilter#6a9e88f1}, order = 2147483647]]
.]]]' in WebSession: 'org.springframework.session.web.server.session.SpringSessionWebSessionStore$SpringSessionWebSession#118f2254'
2022-03-30 18:25:06.658 [reactor-http-kqueue-2] DEBUG 90299 --- [PooledConnectionProvider Loggers.java:250->debug] : [1bea583a, L:/192.168.1.11:56359 - R:wasd-api-int.company.com/10.82.52.4:443] Channel acquired, now: 1 active connections, 0 inactive connections and 0 pending acquire requests.
2022-03-30 18:25:06.659 [reactor-http-kqueue-2] DEBUG 90299 --- [HttpClientConnect Loggers.java:250->debug] : [1bea583a-5, L:/192.168.1.11:56359 - R:wasd-api-int.company.com/10.82.52.4:443] Handler is being applied: {uri=https://wasd-api-int.company.com/wasd/selfservice/api/ccbConfiguration, method=GET}
2022-03-30 18:25:06.659 [reactor-http-kqueue-2] DEBUG 90299 --- [DefaultPooledConnectionProvider Loggers.java:250->debug] : [1bea583a-5, L:/192.168.1.11:56359 - R:wasd-api-int.company.com/10.82.52.4:443] onStateChange(GET{uri=/wasd/selfservice/api/ccbConfiguration, connection=PooledConnection{channel=[id: 0x1bea583a, L:/192.168.1.11:56359 - R:wasd-api-int.company.com/10.82.52.4:443]}}, [request_prepared])
2022-03-30 18:25:06.659 [reactor-http-kqueue-4] DEBUG 90299 --- [FluxReceive Loggers.java:250->debug] : [cb1dc8d7-16, L:/0:0:0:0:0:0:0:1%0:9093 - R:/0:0:0:0:0:0:0:1%0:56346] FluxReceive{pending=0, cancelled=false, inboundDone=true, inboundError=null}: subscribing inbound receiver
2022-03-30 18:25:06.660 [reactor-http-kqueue-2] DEBUG 90299 --- [DefaultPooledConnectionProvider Loggers.java:250->debug] : [1bea583a-5, L:/192.168.1.11:56359 - R:wasd-api-int.company.com/10.82.52.4:443] onStateChange(GET{uri=/wasd/selfservice/api/ccbConfiguration, connection=PooledConnection{channel=[id: 0x1bea583a, L:/192.168.1.11:56359 - R:wasd-api-int.company.com/10.82.52.4:443]}}, [request_sent])
2022-03-30 18:25:06.779 [reactor-http-kqueue-2] DEBUG 90299 --- [HttpClientOperations Loggers.java:250->debug] : [1bea583a-5, L:/192.168.1.11:56359 - R:wasd-api-int.company.com/10.82.52.4:443] Received response (auto-read:false) : [Cache-Control=no-cache, no-store, max-age=0, must-revalidate, Pragma=no-cache, Transfer-Encoding=chunked, Content-Type=application/json, Expires=0, Vary=Access-Control-Request-Headers, Request-Context=appId=e6c5278f-0ac3-4701-9647-f98e97152b1f, X-Content-Type-Options=nosniff, X-XSS-Protection=1; mode=block, Strict-Transport-Security=max-age=31536000 ; includeSubDomains, X-Frame-Options=DENY, Set-Cookie=ARRAffinity=64fcec8b11e29841b77fe53d1af49439c9f64a9de8bd5c352f6db54bf06a60d1;Path=/;HttpOnly;Secure;Domain=wasd-api-int.company.com, Set-Cookie=ARRAffinitySameSite=64fcec8b11e29841b77fe53d1af49439c9f64a9de8bd5c352f6db54bf06a60d1;Path=/;HttpOnly;SameSite=None;Secure;Domain=wasd-api-int.company.com, Date=Wed, 30 Mar 2022 22:25:05 GMT]
2022-03-30 18:25:06.779 [reactor-http-kqueue-2] DEBUG 90299 --- [DefaultPooledConnectionProvider Loggers.java:250->debug] : [1bea583a-5, L:/192.168.1.11:56359 - R:wasd-api-int.company.com/10.82.52.4:443] onStateChange(GET{uri=/wasd/selfservice/api/ccbConfiguration, connection=PooledConnection{channel=[id: 0x1bea583a, L:/192.168.1.11:56359 - R:wasd-api-int.company.com/10.82.52.4:443]}}, [response_received])
2022-03-30 18:25:06.780 [reactor-http-kqueue-2] DEBUG 90299 --- [FluxReceive Loggers.java:250->debug] : [1bea583a-5, L:/192.168.1.11:56359 - R:wasd-api-int.company.com/10.82.52.4:443] FluxReceive{pending=0, cancelled=false, inboundDone=false, inboundError=null}: subscribing inbound receiver
Incorrect Loading
2022-03-30 18:31:56.074 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [PathPatternParserServerWebExchangeMatcher PathPatternParserServerWebExchangeMatcher.java:95->matches] : Checking match of request : '/wasd/selfservice/api/ccbConfiguration'; against '/wasd/**'
2022-03-30 18:31:56.075 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [OrServerWebExchangeMatcher OrServerWebExchangeMatcher.java:60->lambda$matches$2] : matched
2022-03-30 18:31:56.075 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [DelegatingReactiveAuthorizationManager DelegatingReactiveAuthorizationManager.java:55->lambda$check$1] : Checking authorization on '/wasd/selfservice/api/ccbConfiguration' using org.springframework.security.config.web.server.ServerHttpSecurity$AuthorizeExchangeSpec$Access$$Lambda$1070/0x000000080099e840#1107fca4
2022-03-30 18:31:56.075 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [AuthorizationWebFilter AuthorizationWebFilter.java:52->lambda$filter$2] : Authorization successful
2022-03-30 18:31:56.105 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [RoutePredicateHandlerMapping RoutePredicateHandlerMapping.java:142->lambda$lookupRoute$6] : Route matched: ssa-resources
2022-03-30 18:31:56.105 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [RoutePredicateHandlerMapping RoutePredicateHandlerMapping.java:90->lambda$getHandlerInternal$0] : Mapping [Exchange: GET https://localhost:9093/wasdservice/wasd/selfservice/api/ccbConfiguration] to Route{id='ssa-resources', uri=https://wasd-api-int.company.com:443, order=0, predicate=Paths: [/wasdservice/wasd/**], match trailing slash: true, gatewayFilters=[[com.mycompany.wasd.client.gateway.StripBasePathGatewayFilterFactory$$Lambda$1543/0x0000000800b38440#2f286cb, order = 1], [org.springframework.cloud.gateway.filter.factory.TokenRelayGatewayFilterFactory$$Lambda$1544/0x0000000800b38840#19401a72, order = 2]], metadata={}}
2022-03-30 18:31:56.106 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [RoutePredicateHandlerMapping AbstractHandlerMapping.java:189->lambda$getHandler$1] : [cc95c8f2-1] Mapped to org.springframework.cloud.gateway.handler.FilteringWebHandler#addc61b
2022-03-30 18:31:56.107 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [FilteringWebHandler FilteringWebHandler.java:85->handle] : Sorted gatewayFilterFactories: [[GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.RemoveCachedBodyFilter#142f0ac1}, order = -2147483648], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter#11544ddd}, order = -2147482648], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyWriteResponseFilter#3f7d20c5}, order = -1], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ForwardPathFilter#f4a21cb}, order = 0], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.GatewayMetricsFilter#456d3914}, order = 0], [com.mycompany.wasd.client.gateway.StripBasePathGatewayFilterFactory$$Lambda$1543/0x0000000800b38440#2f286cb, order = 1], [org.springframework.cloud.gateway.filter.factory.TokenRelayGatewayFilterFactory$$Lambda$1544/0x0000000800b38840#19401a72, order = 2], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter#1847da64}, order = 10000], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.config.GatewayNoLoadBalancerClientAutoConfiguration$NoLoadBalancerClientFilter#26a0068a}, order = 10150], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.WebsocketRoutingFilter#635f4be1}, order = 2147483646], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyRoutingFilter#27acd9a7}, order = 2147483647], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ForwardRoutingFilter#5967492a}, order = 2147483647]]
.]]]' in WebSession: 'org.springframework.session.web.server.session.SpringSessionWebSessionStore$SpringSessionWebSession#3d103de5'
2022-03-30 18:31:56.144 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [AndServerWebExchangeMatcher AndServerWebExchangeMatcher.java:61->lambda$matches$0] : Trying to match using OrServerWebExchangeMatcher{matchers=[PathMatcherServerWebExchangeMatcher{pattern='/**', method=GET}]}
2022-03-30 18:31:56.144 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [OrServerWebExchangeMatcher OrServerWebExchangeMatcher.java:57->lambda$matches$0] : Trying to match using PathMatcherServerWebExchangeMatcher{pattern='/**', method=GET}
2022-03-30 18:31:56.145 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [PathPatternParserServerWebExchangeMatcher PathPatternParserServerWebExchangeMatcher.java:95->matches] : Checking match of request : '/wasd/selfservice/api/ccbConfiguration'; against '/**'
2022-03-30 18:31:56.145 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [OrServerWebExchangeMatcher OrServerWebExchangeMatcher.java:60->lambda$matches$2] : matched
2022-03-30 18:31:56.146 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [AndServerWebExchangeMatcher AndServerWebExchangeMatcher.java:61->lambda$matches$0] : Trying to match using NegatedServerWebExchangeMatcher{matcher=OrServerWebExchangeMatcher{matchers=[PathMatcherServerWebExchangeMatcher{pattern='/favicon.*', method=null}]}}
2022-03-30 18:31:56.147 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [OrServerWebExchangeMatcher OrServerWebExchangeMatcher.java:57->lambda$matches$0] : Trying to match using PathMatcherServerWebExchangeMatcher{pattern='/favicon.*', method=null}
2022-03-30 18:31:56.148 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [PathPatternParserServerWebExchangeMatcher PathPatternParserServerWebExchangeMatcher.java:87->lambda$matches$1] : Request 'GET /wasd/selfservice/api/ccbConfiguration' doesn't match 'null /favicon.*'
2022-03-30 18:31:56.148 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [OrServerWebExchangeMatcher OrServerWebExchangeMatcher.java:60->lambda$matches$2] : No matches found
2022-03-30 18:31:56.149 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [NegatedServerWebExchangeMatcher NegatedServerWebExchangeMatcher.java:49->lambda$matches$0] : matches = true
2022-03-30 18:31:56.149 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [AndServerWebExchangeMatcher AndServerWebExchangeMatcher.java:61->lambda$matches$0] : Trying to match using MediaTypeRequestMatcher [matchingMediaTypes=[text/html], useEquals=false, ignoredMediaTypes=[*/*]]
2022-03-30 18:31:56.150 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [MediaTypeServerWebExchangeMatcher MediaTypeServerWebExchangeMatcher.java:84->matches] : httpRequestMediaTypes=[text/html, application/xhtml+xml, image/avif, image/webp, image/apng, application/xml;q=0.9, application/signed-exchange;v=b3;q=0.9, */*;q=0.8]
2022-03-30 18:31:56.150 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [MediaTypeServerWebExchangeMatcher MediaTypeServerWebExchangeMatcher.java:86->matches] : Processing text/html
2022-03-30 18:31:56.151 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [MediaTypeServerWebExchangeMatcher MediaTypeServerWebExchangeMatcher.java:98->matches] : text/html .isCompatibleWith text/html = true
2022-03-30 18:31:56.152 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [AndServerWebExchangeMatcher AndServerWebExchangeMatcher.java:66->lambda$matches$4] : All requestMatchers returned true
2022-03-30 18:31:56.152 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [WebSessionServerRequestCache WebSessionServerRequestCache.java:76->lambda$saveRequest$1] : Request added to WebSession: '/wasd/selfservice/api/ccbConfiguration'
2022-03-30 18:31:56.163 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [DefaultServerRedirectStrategy DefaultServerRedirectStrategy.java:54->lambda$sendRedirect$0] : Redirecting to 'https://s0144821.company.com:9999/uaa/oauth/authorize?response_type=code&client_id=wasdssa&state=CPaWn5UE9oBzjPm-efV2mlONTXbNrLz5i_DZAy9KrS4%3D&redirect_uri=https://localhost:9093/wasdservice/login/oauth2/code/mdc'
2022-03-30 18:31:56.166 [lettuce-kqueueEventLoop-5-1] DEBUG 90357 --- [HttpWebHandlerAdapter LogFormatUtils.java:119->traceDebug] : [cc95c8f2-1] Completed 302 FOUND
Sample Application
I created a minimalistic project where you can see
all the details of the configuration and execute it so that you can verify for yourself that indeed this is happening.
(you are going to need a Redis instance and an Oauth server)
https://github.com/fiallega/spring-session-oauth-gateway
Configuration
The WebSecurity Configuration is as follows:
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.csrf().disable();
http.headers()
.referrerPolicy(ReferrerPolicyServerHttpHeadersWriter.ReferrerPolicy.NO_REFERRER_WHEN_DOWNGRADE);
http.authorizeExchange()
.pathMatchers("/login**","/entries/**").permitAll()
.anyExchange()
.authenticated()
.and()
.oauth2Login();
return http.build();
}
and the gateway configuration is
cloud:
gateway:
routes:
- id: ssa-resources
uri: https://api.publicapis.org
predicates:
- Path=/entries
filters:
- TokenRelay=
What I see is that after reloading the application for some reason instead of making calls to the downstream servers, checks on mediatype happens and ends up with a 302 redirect when it should get a 200.
So why is it that a reload of an application that is using spring session is not able to "pickup" where it left, and this weird redirect happening after reloading the application if an active session exists in Redis?
Thanks
The solution was found adding the authorizedClientRepository bean to the WebSecurityConfig
#Bean
public ServerOAuth2AuthorizedClientRepository authorizedClientRepository() {
return new WebSessionServerOAuth2AuthorizedClientRepository();
}
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.csrf().disable();
http.headers()
.referrerPolicy(ReferrerPolicyServerHttpHeadersWriter.ReferrerPolicy.NO_REFERRER_WHEN_DOWNGRADE);
http.authorizeExchange()
.pathMatchers("/login**","/entries/**").permitAll()
.anyExchange()
.authenticated()
.and()
.oauth2Login(l -> l
.authorizedClientRepository(authorizedClientRepository()));
return http.build();
}
I want to give credit to the author of this article where I found the solution and the github repository he provided.
He provides the following comment to the solution:
/Most of the spring security classes that store data are already implemented using WebSession.
The only one that is not is {#link ServerOAuth2AuthorizedClientRepository} so we define that bean ourselves.
#return ServerOAuth2AuthorizedClientRepository/
I got the following exception during my Spring app.
I use Spring-boot-starter 2.2.1, with spring-boot-starer-reactor-netty 2.2.1. (Reactory netty 0.9.1 in it.)
I use spring-boot-admin-starter-client 2.2.0 within my application.
It is not thrown sequentially, and I can't see any pattern on the occurrences. I've just started my application as a unix service and after some time it threw. My idea is about the Spring admin and client, but I upgraded them to the latest but the exception still exists.
Can u give me ANY advice why is it throwing?
This exception is happened on thread or-http-epoll-1:
2019-12-12 17:24:46.756+0100 ERROR 15674 --- [or-http-epoll-1] i.n.u.ResourceLeakDetector
and on this thread everything seems alright earlier:
2019-12-12 17:23:16.763+0100 DEBUG 15674 --- [or-http-epoll-1] r.n.h.s.HttpServer : [id: 0x967afa3f, L:/127.0.1.1:8080 - R:/127.0.0.1:54646] Handler is being applied: org.springframework.http.server.reactive.ReactorHttpHandlerAdapter#33681b9d
2019-12-12 17:23:16.763+0100 DEBUG 15674 --- [or-http-epoll-1] o.s.w.s.a.HttpWebHandlerAdapter : [967afa3f] HTTP GET "/actuator/health"
2019-12-12 17:23:16.764+0100 DEBUG 15674 --- [or-http-epoll-1] .b.a.e.w.r.WebFluxEndpointHandlerMapping : [967afa3f] Mapped to org.springframework.boot.actuate.endpoint.web.reactive.AbstractWebFluxEndpointHandlerMapping$ReadOperationHandler#6de54b40
2019-12-12 17:23:17.768+0100 DEBUG 15674 --- [or-http-epoll-1] r.n.h.s.HttpServerOperations : [id: 0x967afa3f, L:/127.0.1.1:8080 - R:/127.0.0.1:54646] Decreasing pending responses, now 0
2019-12-12 17:23:17.768+0100 DEBUG 15674 --- [or-http-epoll-1] o.s.w.s.a.HttpWebHandlerAdapter : [967afa3f] Completed 200 OK
2019-12-12 17:23:17.768+0100 DEBUG 15674 --- [or-http-epoll-1] r.n.h.s.HttpServerOperations : [id: 0x967afa3f, L:/127.0.1.1:8080 - R:/127.0.0.1:54646] Last HTTP response frame
2019-12-12 17:23:17.768+0100 DEBUG 15674 --- [or-http-epoll-1] r.n.h.s.HttpServerOperations : [id: 0x967afa3f, L:/127.0.1.1:8080 - R:/127.0.0.1:54646] Last HTTP packet was sent, terminating the channel
2019-12-12 17:24:17.959+0100 DEBUG 15674 --- [or-http-epoll-1] r.n.h.s.HttpServerOperations : [id: 0x967afa3f, L:/127.0.1.1:8080 - R:/127.0.0.1:54646] Decreasing pending responses, now 0
2019-12-12 17:24:17.959+0100 DEBUG 15674 --- [or-http-epoll-1] r.n.h.s.HttpServerOperations : [id: 0x967afa3f, L:/127.0.1.1:8080 - R:/127.0.0.1:54646] Last HTTP packet was sent, terminating the channel
2019-12-12 17:24:46.756+0100 ERROR 15674 --- [or-http-epoll-1] i.n.u.ResourceLeakDetector : LEAK: ByteBuf.release() was not called before it's garbage-collected. See https://netty.i o/wiki/reference-counted-objects.html for more information.
Recent access records:
Created at:
#011io.netty.buffer.PooledByteBufAllocator.newDirectBuffer(PooledByteBufAllocator.java:349)
#011io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:187)
#011io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:178)
#011io.netty.buffer.AbstractByteBufAllocator.buffer(AbstractByteBufAllocator.java:115)
#011io.netty.buffer.ByteBufUtil.readBytes(ByteBufUtil.java:445) #011io.netty.handler.codec.http.websocketx.WebSocket08FrameDecoder.decode(WebSocket08FrameDecoder.java:314)
#011io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:503)
#011io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:442)
#011io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:281)
#011io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
#011io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
#011io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
#011io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1422)
#011io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
#011io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
#011io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:931)
#011io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:792)
#011io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:502)
#011io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:407) #011io.netty.util.concurrent.SingleThreadEventExecutor$6.run(SingleThreadEventExecutor.java:1050)
#011io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
#011io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
#011java.lang.Thread.run(Thread.java:748)
SOLUTION: I found a message.getPayload() what was not released, i wrote that there and the problem is not occurred anymore.
I am able to get a test running for a Spring Boot Project but I'm always getting a 404 on the #State test.
#TargetRequestFilter
public void exampleRequestFilter(HttpRequest request) {
System.out.println(request.toString());
request.addHeader("Authorization", JIMMY_CARTER_TOKEN);
}
#BeforeClass
public static void setupApplication() {
SpringApplication application = new SpringApplication(App.class);
application.setAdditionalProfiles("integration");
application.run("--server.port=9000");
}
#TestTarget
public final HttpTarget target = new HttpTarget("http", "127.0.0.1", 9000);
#State("user id") // Method will be run before testing interactions that require "default" or "no-data" state
public void toUserId() {
System.out.println("Test User Id");
}
What's strange is I can tell it's hitting the right endpoint by printing out the request information and the Authorization header. I put a debug statement in and verified that I can call with the same credentials and endpoint as the test. However the test is always failing with a 404. Is there something I'm missing in my setup?
"request": {
"method": "GET",
"path": "/api/user/XXXXXX"
},
"response": {
"status": 200,
"headers": {
"content-type": "application/vnd.api+json;charset=UTF-8"
},
"body": ...
},
"providerStates": [
{
"name": "user id"
}
]
}
You can see what requests are being made by enabling debug logging with the Apache HTTP Client and the pact-jvm libraries. For Apache HTTP Client, please refer to https://hc.apache.org/httpcomponents-client-ga/logging.html.
For an example of what the debug logs you are looking for, this is from the example ContractTest from pact-jvm (https://github.com/DiUS/pact-jvm/blob/master/pact-jvm-provider-junit/src/test/java/au/com/dius/pact/provider/junit/ContractTest.java):
13:09:20.012 [Test worker] DEBUG au.com.dius.pact.provider.ProviderClient - Making request for provider au.com.dius.pact.provider.ProviderInfo(http, localhost, 8332, /, myAwesomeService, null, null, au.com.dius.pact.provider.junit.target.HttpTarget$$Lambda$14/771479970#1dec1536, null, null, false, null, changeit, null, true, false, true, null, [], []):
13:09:20.018 [Test worker] DEBUG au.com.dius.pact.provider.ProviderClient - method: GET
path: /data
query: [:]
headers: [:]
matchers: MatchingRules(rules=[:])
generators: Generators(categories={})
body: OptionalBody(state=MISSING, value=null)
13:09:20.475 [Test worker] INFO au.com.dius.pact.provider.junit.ContractTest - exampleRequestFilter called: GET http://localhost:8332/data HTTP/1.1
13:09:20.537 [Test worker] DEBUG org.apache.http.headers - http-outgoing-0 >> GET /data HTTP/1.1
13:09:20.538 [Test worker] DEBUG org.apache.http.headers - http-outgoing-0 >> Host: localhost:8332
13:09:20.538 [Test worker] DEBUG org.apache.http.headers - http-outgoing-0 >> Connection: Keep-Alive
13:09:20.551 [Test worker] DEBUG org.apache.http.headers - http-outgoing-0 >> User-Agent: Apache-HttpClient/4.5.2 (Java/1.8.0_131)
13:09:20.553 [Test worker] DEBUG org.apache.http.headers - http-outgoing-0 >> Accept-Encoding: gzip,deflate
13:09:20.553 [Test worker] DEBUG org.apache.http.wire - http-outgoing-0 >> "GET /data HTTP/1.1[\r][\n]"
13:09:20.554 [Test worker] DEBUG org.apache.http.wire - http-outgoing-0 >> "Host: localhost:8332[\r][\n]"
13:09:20.555 [Test worker] DEBUG org.apache.http.wire - http-outgoing-0 >> "Connection: Keep-Alive[\r][\n]"
13:09:20.558 [Test worker] DEBUG org.apache.http.wire - http-outgoing-0 >> "User-Agent: Apache-HttpClient/4.5.2 (Java/1.8.0_131)[\r][\n]"
13:09:20.559 [Test worker] DEBUG org.apache.http.wire - http-outgoing-0 >> "Accept-Encoding: gzip,deflate[\r][\n]"
13:09:20.560 [Test worker] DEBUG org.apache.http.wire - http-outgoing-0 >> "[\r][\n]"
13:09:20.774 [Test worker] DEBUG org.apache.http.wire - http-outgoing-0 << "HTTP/1.1 204 No Content[\r][\n]"
13:09:20.775 [Test worker] DEBUG org.apache.http.wire - http-outgoing-0 << "Date: Sat, 23 Sep 2017 03:09:20 GMT[\r][\n]"
13:09:20.775 [Test worker] DEBUG org.apache.http.wire - http-outgoing-0 << "Server: rest-client-driver(1.1.45)[\r][\n]"
13:09:20.779 [Test worker] DEBUG org.apache.http.wire - http-outgoing-0 << "[\r][\n]"
13:09:20.784 [Test worker] DEBUG org.apache.http.headers - http-outgoing-0 << HTTP/1.1 204 No Content
13:09:20.785 [Test worker] DEBUG org.apache.http.headers - http-outgoing-0 << Date: Sat, 23 Sep 2017 03:09:20 GMT
13:09:20.785 [Test worker] DEBUG org.apache.http.headers - http-outgoing-0 << Server: rest-client-driver(1.1.45)
13:09:20.842 [Test worker] DEBUG au.com.dius.pact.provider.ProviderClient - Received response: HTTP/1.1 204 No Content
13:09:20.867 [Test worker] DEBUG au.com.dius.pact.provider.ProviderClient - Response: [statusCode:204, headers:[Date:Sat, 23 Sep 2017 03:09:20 GMT, Server:rest-client-driver(1.1.45)]]
13:09:21.724 [Test worker] DEBUG au.com.dius.pact.model.Matching$ - Found a matcher for text/plain -> Some((text/plain,au.com.dius.pact.matchers.PlainTextBodyMatcher#29c3e77b))
returns a response which
has status code 204 (OK)
has a matching body (OK)
Angular v. v4.0.2
Spring Boot v. 1.5.2.RELEASE
Keycloak v.2.4.0.Final (will upgrade later)
I read this mail converstion about the same problem: http://keycloak-user.88327.x6.nabble.com/keycloak-user-NOT-ATTEMPTED-bearer-only-error-while-trying-to-access-server-from-client-td927.html and this http://slackspace.de/articles/authentication-with-spring-boot-angularjs-and-keycloak/
I use the following http service for making authorized requests:
#Injectable()
export class AuthHttpService extends Http {
constructor(backend: ConnectionBackend, defaultOptions: RequestOptions, private authService: AuthService) {
super(backend, defaultOptions);
}
private setToken(options: RequestOptionsArgs) {
if (options == null || AuthService.auth == null || AuthService.auth.authz == null || AuthService.auth.authz.token == null) {
console.log("Need a token, but no token is available, not setting bearer token.");
return;
}
console.log(AuthService.auth.authz.token);
options.headers.set('Authorization', 'Bearer ' + AuthService.auth.authz.token);
}
private configureRequest(f:Function, url:string | Request, options:RequestOptionsArgs, body?: any):Observable<Response> {
let tokenPromise:Promise<string> = this.authService.getToken();
let tokenObservable:Observable<string> = Observable.fromPromise(tokenPromise);
let tokenUpdateObservable:Observable<any> = Observable.create((observer) => {
if (options == null) {
let headers = new Headers();
options = new RequestOptions({ headers: headers });
}
this.setToken(options);
observer.next();
observer.complete();
});
let requestObservable:Observable<Response> = Observable.create((observer) => {
let result;
if (body) {
result = f.apply(this, [url, body, options]);
} else {
result = f.apply(this, [url, options]);
}
result.subscribe((response) => {
observer.next(response);
observer.complete();
}, (err) => observer.error(err));
});
return <Observable<Response>>Observable
.merge(tokenObservable, tokenUpdateObservable, requestObservable, 1)
.filter((response) => response instanceof Response);
}
...
Application.properties
The token is correctly logged.
server.port = 8081
keycloak.realm = apprealm
keycloak.auth-server-url = http://localhost:8080/auth
keycloak.ssl-required = external
keycloak.resource = appbackend
keycloak.bearer-only = true
keycloak.credentials.secret = ...
keycloak.securityConstraints[0].securityCollections[0].name = secure
keycloak.securityConstraints[0].securityCollections[0].authRoles[0]=frontenduser
keycloak.securityConstraints[0].securityCollections[0].patterns[0] = /r/s/*
logging.level.org.keycloak=DEBUG
The user which I use in the frontend has that role.
Error in backend
2017-04-22 15:40:00.517 DEBUG 14088 --- [nio-8081-exec-1] o.k.adapters.PreAuthActionsHandler : adminRequest http://localhost:8081/r/s/e/p/m
2017-04-22 15:40:00.540 DEBUG 14088 --- [nio-8081-exec-1] o.k.a.a.ClientCredentialsProviderUtils : Using provider 'secret' for authentication of client 'appbackend'
2017-04-22 15:40:00.543 DEBUG 14088 --- [nio-8081-exec-1] o.k.a.a.ClientCredentialsProviderUtils : Loaded clientCredentialsProvider secret
2017-04-22 15:40:00.545 DEBUG 14088 --- [nio-8081-exec-1] o.k.a.a.ClientCredentialsProviderUtils : Loaded clientCredentialsProvider jwt
2017-04-22 15:40:00.552 DEBUG 14088 --- [nio-8081-exec-1] o.k.a.a.ClientCredentialsProviderUtils : Loaded clientCredentialsProvider secret
2017-04-22 15:40:00.553 DEBUG 14088 --- [nio-8081-exec-1] o.k.a.a.ClientCredentialsProviderUtils : Loaded clientCredentialsProvider jwt
2017-04-22 15:40:00.625 DEBUG 14088 --- [nio-8081-exec-1] o.keycloak.adapters.KeycloakDeployment : resolveUrls
2017-04-22 15:40:00.631 DEBUG 14088 --- [nio-8081-exec-1] o.k.adapters.KeycloakDeploymentBuilder : Use authServerUrl: http://localhost:8080/auth, tokenUrl: http://localhost:8080/auth/realms/apprealm/protocol/openid-connect/token, relativeUrls: NEVER
2017-04-22 15:40:00.662 DEBUG 14088 --- [nio-8081-exec-1] o.k.adapters.RequestAuthenticator : NOT_ATTEMPTED: bearer only
2017-04-22 15:40:00.681 INFO 14088 --- [nio-8081-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2017-04-22 15:40:00.681 INFO 14088 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
2017-04-22 15:40:00.723 INFO 14088 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 42 ms
2017-04-22 15:40:08.560 DEBUG 14088 --- [nio-8081-exec-2] o.k.adapters.PreAuthActionsHandler : adminRequest http://localhost:8081/r/s/e/p/m
2017-04-22 15:40:08.560 DEBUG 14088 --- [nio-8081-exec-2] o.k.adapters.RequestAuthenticator : NOT_ATTEMPTED: bearer only
Edit http
HTTP/1.1 401
Cache-Control: private
Expires: Thu, 01 Jan 1970 01:00:00 CET
WWW-Authenticate: Bearer realm="apprealm"
Access-Control-Allow-Origin: http://localhost:4200
Vary: Origin
Access-Control-Allow-Methods: GET,POST,PUT,DELETE
Access-Control-Allow-Headers: authorization, content-type
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1800
Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
Content-Length: 0
Date: Sun, 23 Apr 2017 17:04:07 GMT
Edit 2: http raw request
OPTIONS http://localhost:8081/r/p/main HTTP/1.1
Host: localhost:8081
Connection: keep-alive
Access-Control-Request-Method: PUT
Origin: http://localhost:4200
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
Access-Control-Request-Headers: authorization,content-type
Accept: */*
Referer: http://localhost:4200/b
Accept-Encoding: gzip, deflate, sdch
Accept-Language: nl-NL,nl;q=0.8,en-US;q=0.6,en;q=0.4
What could be the problem?
I've just had this error and it was because the Authorization header is missing the text "bearer " before the actual token.