spring-security
spring-security copied to clipboard
WebClient client cert auth broken by spring-security:6.0.0-M6 (worked in 6.0.0-M5)
Describe the bug
As mentioned in gitter...
My app currently runs on Spring Boot 2.7.4. I was testing compatibility with 3.0.0-M5. All appeared to work well except one aspect of spring-security, as part of presenting a client certificate using WebClient. I end up with "AccessDeniedException: Access is denied" thrown by AffirmativeBased.decide(AffirmativeBased.java:73). The handshake appears to work fine, and the handshake logging looks nearly identical to that under 2.7.4. But I got it to work after downgrading to spring-security:6.0.0-M5 (from M7). So, it seems something broke as of spring-security:6.0.0-M6. Is this a known issue, and will it be fixed in the next spring-security release?
To Reproduce
- Under standard configuration of Spring Boot 3.0.0-M5, which includes spring-security:6.0.0-M7 (or even downgrading to M6), run the code below against a URL resource protected by client certificate authorization
- Witness AccessDeniedException: Access is denied" thrown by AffirmativeBased.decide(AffirmativeBased.java:73)
Expected behavior
Client certificate authentication succeeds... as it does under both Spring Boot 2.7.4 and 3.0.0-M5 with spring-security downgraded to 6.0.0-M5 (from 6.0.0-M7)
Sample
private StreamingResponseBody streamUrl(final String url) {
final String keyStoreResourcePath = "[url resource path to key/trust store]", keyStorePass = "[some password]";
final HttpClient httpClient = HttpClient.create().secure(spec -> {
try {
final char[] keyStorePassChars = keyStorePass.toCharArray();
final URL keyStoreUrl = ResourceUtils.getURL(keyStoreResourcePath);
final KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(keyStoreUrl.openStream(), keyStorePassChars);
final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, keyStorePassChars);
final TrustManagerFactory trustManagerFactory = InsecureTrustManagerFactory.INSTANCE;
spec.sslContext(Http2SslContextSpec.forClient().configure(sslContextBuilder -> sslContextBuilder.keyManager(keyManagerFactory).trustManager(trustManagerFactory)));
} catch (final NoSuchAlgorithmException | KeyStoreException | CertificateException | UnrecoverableKeyException | IOException e) {
throw new IllegalStateException("Boom!", e);
}
});
final WebClient client = WebClient.builder().clientConnector(new ReactorClientHttpConnector(httpClient)).baseUrl(url).build();
final Flux<DataBuffer> dataBufferFlux = client.get()
.accept(MediaType.APPLICATION_PDF)
.retrieve()
.bodyToFlux(DataBuffer.class);
return out -> DataBufferUtils.write(dataBufferFlux, out).doOnNext(DataBufferUtils.releaseConsumer()).blockLast(Duration.ofSeconds(20));
}
@GetMapping(path = "/docs/{docId}", produces = MediaType.APPLICATION_PDF_VALUE)
public ResponseEntity<StreamingResponseBody> stream(@PathVariable final String docId) {
final String url = lookupUrlGivenDocId(docId); //Some client cert auth protected document URL being streamed on behalf of the client
return ResponseEntity.ok().contentType(MediaType.APPLICATION_PDF).body(streamUrl(url));
}
Hi @jkjome!
Is this a known issue, and will it be fixed in the next spring-security release?
Not that I'm aware of. Thanks for reaching out. There have been a number of changes recently to update defaults for various aspects of the framework.
As we're heads down preparing for this release, it would be helpful if you could provide a minimal, reproducible sample. The above code sample does not provide a way to run a reproducible example, such as a unit test or a self-contained Spring Boot application (with instructions for reproducing). Can you provide one?
It occurred to me that my Spring Security inbound rules were affecting outbound WebClient calls. I did a search and found issue #10589 that confirmed my suspicion that something like that might be occurring. I played with some settings and found a new API that appears to resolve my issue under spring-security:6.0.0-M6+ (and wasn't necessary prior to that). It seems to me that this just shouldn't happen... and doesn't under prior versions of Spring Security.
If I provide .shouldFilterAllDispatcherTypes(false)
, it seems to prevent the Spring Security inbound rules from affecting the WebClient outbound calls. Making that call prior to .anyRequest().authenticated()
seems to do the trick. I found this late yesterday, so I haven't had time to create a minimal reproducible example. But, I figured I'd mention the solution now, and work on the reproducible example later, to get it on your radar.
Of course, I don't know if this is the appropriate approach, nor whether something else in my config should just be changed so that .shouldFilterAllDispatcherTypes(false)
becomes unnecessary? I'd appreciate if you could critique my security config and let me know if it should be changed in some fundamental way.
@Bean
SecurityFilterChain filterChain(final HttpSecurity http) throws Exception {
http.authorizeHttpRequests(cstmzr -> cstmzr
.shouldFilterAllDispatcherTypes(false) //<-- Fixes WebClient issue, but what does it do???
.anyRequest().authenticated()
)
.csrf(cstmzr -> cstmzr.disable())
.httpBasic(withDefaults());
return http.build();
}
@marcusdacoregio do we have any documentation on the new method, or thoughts about the above issue?
Yes, you can refer to this link for the docs (Example 6). I cannot see exactly what is going on right now since I'm not that familiar with WebClient
but if @jkjome is able to provide a minimal, reproducible sample I can take a look at it early next week.
Thanks @marcusdacoregio. @jkjome it seems you’re experiencing symptoms of a new default in 6.0, but I’d like to understand the scenario as well.
I’m less familiar with dispatcher types in Spring, but I’m guessing you’re operating under another type, such as ASYNC. I think the minimal sample will help quite a bit because there’s a number of things that could be going on. I wonder for example what your client security configuration looks like, as well as whether the server you’re accessing is affecting things. I can’t tell just glancing at the code provided.
Just wanted to mention that I can replace shouldFilterAllDispatcherTypes(false)
with dispatcherTypeMatchers(DispatcherType.ASYNC).permitAll()
and it also resolves my issue.
However, it's been difficult to come up with a minimal testcase. I tried a minimal setup where I made an outbound WebClient
request to an arbitrary, unprotected, HTTPS URL. It worked fine with/out having to implement either of the above workarounds. The remaining difference with my app is that the URLs I'm calling in my app are protected by Client Cert-based Auth. In fact, when I use my own certificate to call one of these protected URLs, I can reproduce the behavior. So, it appears as if the key distinction is connecting to a URL that is protected by Cient Cert-based Auth.
I've been trying to come up with my own self-signed cert to try to create a minimal testcase, but I keep running into various certificate errors attempting to use it. So far, the only way I can reproduce this is by using my own real certiificate against a URL that accepts my certificate for Client Cert-based Auth. And, obviously, I can't publish that. So, I'm hoping somone on the spring-security team will have more certificate knowledge/mojo to re-create the scenario I've described.
Below are the complete contents of the log, including stacktraces, that result when I don't use one of the above two workarounds when calling one of my Client Cert-based Auth protected URLs using WebClient
. Note that doing the same with RestTemplate
does not result in any errors... presumably because WebClient
makes use of the ASYNC
dispatcher while RestTemplate
does not. The errors occur after successful completion of the TLS handshake (not included in the log below). The log ends with two SSL debug entries that, actually, occur shortly after the request whether I use one of the workarounds or not. I left them in just in case they might be of interest to someone on the spring-security team....
2022-10-12T02:10:52.280-05:00 ERROR 28772 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] threw exception
org.springframework.security.access.AccessDeniedException: Access Denied
at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilterInternal(AuthorizationFilter.java:75) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:91) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:98) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:224) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:189) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:351) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:691) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.ApplicationDispatcher.doDispatch(ApplicationDispatcher.java:612) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.ApplicationDispatcher.dispatch(ApplicationDispatcher.java:582) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.AsyncContextImpl$AsyncRunnable.run(AsyncContextImpl.java:588) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.AsyncContextImpl.doInternalDispatch(AsyncContextImpl.java:354) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:194) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:119) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:246) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.coyote.AbstractProcessor.dispatch(AbstractProcessor.java:241) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:59) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:867) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1762) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
2022-10-12T02:10:52.282-05:00 ERROR 28772 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Unable to handle the Spring Security Exception because the response is already committed.] with root cause
org.springframework.security.access.AccessDeniedException: Access Denied
at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilterInternal(AuthorizationFilter.java:75) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:91) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:98) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:360) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:224) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:189) ~[spring-security-web-6.0.0-M7.jar:6.0.0-M7]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:351) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:691) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.ApplicationDispatcher.doDispatch(ApplicationDispatcher.java:612) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.ApplicationDispatcher.dispatch(ApplicationDispatcher.java:582) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.AsyncContextImpl$AsyncRunnable.run(AsyncContextImpl.java:588) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.AsyncContextImpl.doInternalDispatch(AsyncContextImpl.java:354) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:194) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:119) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:246) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.coyote.AbstractProcessor.dispatch(AbstractProcessor.java:241) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:59) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:867) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1762) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
2022-10-12T02:10:52.288-05:00 WARN 28772 --- [nio-8080-exec-2] o.apache.catalina.core.AsyncContextImpl : onError() call failed for listener of type [org.apache.catalina.core.AsyncListenerWrapper]
java.lang.IllegalArgumentException: Cannot dispatch without an AsyncContext
at org.springframework.util.Assert.notNull(Assert.java:201) ~[spring-core-6.0.0-M6.jar:6.0.0-M6]
at org.springframework.web.context.request.async.StandardServletAsyncWebRequest.dispatch(StandardServletAsyncWebRequest.java:131) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.springframework.web.context.request.async.WebAsyncManager.setConcurrentResultAndDispatch(WebAsyncManager.java:400) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.springframework.web.context.request.async.WebAsyncManager.lambda$startCallableProcessing$2(WebAsyncManager.java:322) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.springframework.web.context.request.async.StandardServletAsyncWebRequest.lambda$onError$0(StandardServletAsyncWebRequest.java:146) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) ~[na:na]
at org.springframework.web.context.request.async.StandardServletAsyncWebRequest.onError(StandardServletAsyncWebRequest.java:146) ~[spring-web-6.0.0-M6.jar:6.0.0-M6]
at org.apache.catalina.core.AsyncListenerWrapper.fireOnError(AsyncListenerWrapper.java:49) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.core.AsyncContextImpl.setErrorState(AsyncContextImpl.java:413) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:250) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.coyote.AbstractProcessor.dispatch(AbstractProcessor.java:241) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:59) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:867) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1762) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-10.0.23.jar:10.0.23]
at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
javax.net.ssl|DEBUG|53|reactor-http-nio-2|2022-10-12 02:11:22.252 CDT|Alert.java:238|Received alert message (
"Alert": {
"level" : "warning",
"description": "close_notify"
}
)
javax.net.ssl|WARNING|53|reactor-http-nio-2|2022-10-12 02:11:22.252 CDT|SSLEngineOutputRecord.java:182|outbound has closed, ignore outbound application data
Thanks @jkjome. At this point, if you could at least share the your client code, mainly the entire controller and the Spring Security configuration, that would be helpful. I also think the log entries leading up to the stacktraces would be useful, but do make sure to set the log level to trace (logger.level.org.springframework.security=trace
). The above seems to be missing much of the context around the request that triggers the error.
Hi @sjohnr. See the attached zip file containing the code, as well as a text file containing relevant trace logging you requested: wcauthfail.zip
After further testing, it looks like there are 3 ingredients required to reproduce this issue...
- Use spring-security:6.0.0-M6+
- Set up spring-security config to enforce some sort of server authentication, e.g., BASIC Auth
- Host an API (protected in step 2) that, internally, uses
WebClient
to make an outbound call to a URL protected by Client-Cert Auth (and present the relevant certificate), and then stream that data back to the caller of the hosted API viaDataBuffer/StreamingResponseBody
The zip file contains an application.properties
file with some properties you'll need to provide values for, including keystore path, keystore password, and a test URL. There are a few API paths, including /hello
, /fetch
, and /stream
. Two relevant APIs to look at in regard to the issue at hand are...
The /fetch
path does NOT trigger the issue, even though it uses WebClient
to make the outbound call. So, clearly, there is more to this than just using WebClient
.
The /stream
path DOES trigger the issue. The key difference to /fetch
is that it responds with a DataBuffer/StreamingResponseBody
. You'll notice in the log tracing that spring-security appears to apply authentication twice; once for the initial /stream
call (expected), and then again during the attempted streaming of the response back to original caller of /stream
(unexpected).
Hopefully that is all you will need to determine the root cause of this issue and prevent it from leaking into a non-milestone release.
Thanks @jkjome. After looking at the provided sample and creating a simple parallel example of my own, I believe I have partially reproduced it without Client-Cert auth.
I can reproduce an AccessDeniedException
when returning a similar ResponseEntity<StreamingResponseBody>
from the controller. The difference is that in my case, the response is already committed and so the 403 Forbidden
does not show up in the response.
I believe the AccessDeniedException
is related to Async integration in Spring Security (see Spring's documentation Asynchronous Requests for more info).
Specifically, Spring Security does support Callable
return types, but does not support other async return types, such as DeferredResult
(and I'm assuming StreamingResponseBody
as well). Please note that this has been documented for some time in Spring Security and is not a new issue.
Prior to spring-security:6.0.0-M6
, other dispatcher types were not affected by security rules. However, after gh-11492, other dispatcher types will be handled by the Spring Security filter chain, including ASYNC. But because the return type is not automatically handled by Spring Security's Async integration, your SecurityContext
is lost and the async dispatch is considered unauthenticated/anonymous when it is processed by the filter chain (this shows up as a "second" request in your logs).
I'm not yet sure why your sample case is allowed write a 403
to the response, and why it appears to succeed in earlier releases (despite the AccessDeniedException
), but I don't believe the overall issue here is new. It's also plausible that the workarounds you mentioned are actually viable in certain cases.
Short of adding an enhancement to support an expanded set of return types for Asynchronous request processing in Spring Security, I'm not sure this case would require fixing in time for the release (or indeed is even a bug, but an expected outcome in 6.0). I could be wrong, of course. We'll keep the issue open and revisit this after RC1
is released on Monday.
Hello, I am preparing for migration to spring-boot 3, I am using spring-security 6.0.0-SNAPSHOT.
I think I have the same issue, but with a controller, returning a ResponseEntity<StreamingResponseBody>
.
I have a custom BearerTokenResolver
that also checks token in query parameter:
@Bean
BearerTokenResolver bearerTokenResolver() {
final DefaultBearerTokenResolver bearerTokenResolver = new DefaultBearerTokenResolver();
bearerTokenResolver.setAllowUriQueryParameter(true);
return bearerTokenResolver;
}
When I am issuing a GET with a token in parameter, it is accepted but then an exception is thrown:
2022-11-17 09:03:35,698 ERROR [http-nio-8084-exec-1] - org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/recordings].[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] threw exception
org.springframework.security.access.AccessDeniedException: Access Denied
at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:98) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:91) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0.jar:6.0.0]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0.jar:6.0.0]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0.jar:6.0.0]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0.jar:6.0.0]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0.jar:6.0.0]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0.jar:6.0.0]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:275) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:169) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:351) ~[spring-web-6.0.0.jar:6.0.0]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267) ~[spring-web-6.0.0.jar:6.0.0]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.0.0.jar:6.0.0]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.0.jar:6.0.0]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0.jar:6.0.0]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:108) ~[spring-web-6.0.0.jar:6.0.0]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.0.jar:6.0.0]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0.jar:6.0.0]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:691) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.ApplicationDispatcher.doDispatch(ApplicationDispatcher.java:612) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.ApplicationDispatcher.dispatch(ApplicationDispatcher.java:582) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.AsyncContextImpl$AsyncRunnable.run(AsyncContextImpl.java:588) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.AsyncContextImpl.doInternalDispatch(AsyncContextImpl.java:354) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:194) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:119) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:741) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:247) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.coyote.AbstractProcessor.dispatch(AbstractProcessor.java:243) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:59) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1739) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at java.lang.Thread.run(Thread.java:833) ~[?:?]
2022-11-17 09:03:35,704 ERROR [http-nio-8084-exec-1] - org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/recordings].[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [/recordings] threw exception [Unable to handle the Spring Security Exception because the response is already committed.] with root cause
org.springframework.security.access.AccessDeniedException: Access Denied
at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:98) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:91) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0.jar:6.0.0]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0.jar:6.0.0]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0.jar:6.0.0]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0.jar:6.0.0]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0.jar:6.0.0]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0.jar:6.0.0]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:183) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:275) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:169) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) ~[spring-security-web-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:351) ~[spring-web-6.0.0.jar:6.0.0]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267) ~[spring-web-6.0.0.jar:6.0.0]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.0.0.jar:6.0.0]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.0.jar:6.0.0]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0.jar:6.0.0]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:108) ~[spring-web-6.0.0.jar:6.0.0]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.0.jar:6.0.0]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0.jar:6.0.0]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:691) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.ApplicationDispatcher.doDispatch(ApplicationDispatcher.java:612) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.ApplicationDispatcher.dispatch(ApplicationDispatcher.java:582) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.AsyncContextImpl$AsyncRunnable.run(AsyncContextImpl.java:588) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.AsyncContextImpl.doInternalDispatch(AsyncContextImpl.java:354) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:194) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:119) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:741) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:247) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.coyote.AbstractProcessor.dispatch(AbstractProcessor.java:243) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:59) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1739) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-10.1.1.jar:10.1.1]
at java.lang.Thread.run(Thread.java:833) ~[?:?]
2022-11-17 09:03:35,720 ERROR [http-nio-8084-exec-1] - org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$StaticView - Cannot render error page for request [null] as the response has already been committed. As a result, the response may have the wrong status code.
2022-11-17 09:03:35,722 WARN [http-nio-8084-exec-1] - org.apache.catalina.core.AsyncContextImpl - onError() call failed for listener of type [org.apache.catalina.core.AsyncListenerWrapper]
2022-11-17 09:03:35,722 WARN [http-nio-8084-exec-1] - org.apache.catalina.core.AsyncContextImpl - onError() call failed for listener of type [org.apache.catalina.core.AsyncListenerWrapper]
java.lang.IllegalStateException: Cannot dispatch without an AsyncContext
at org.springframework.util.Assert.state(Assert.java:76) ~[spring-core-6.0.0.jar:6.0.0]
at org.springframework.web.context.request.async.StandardServletAsyncWebRequest.dispatch(StandardServletAsyncWebRequest.java:131) ~[spring-web-6.0.0.jar:6.0.0]
at org.springframework.web.context.request.async.WebAsyncManager.setConcurrentResultAndDispatch(WebAsyncManager.java:400) ~[spring-web-6.0.0.jar:6.0.0]
at org.springframework.web.context.request.async.WebAsyncManager.lambda$startCallableProcessing$2(WebAsyncManager.java:322) ~[spring-web-6.0.0.jar:6.0.0]
at org.springframework.web.context.request.async.StandardServletAsyncWebRequest.lambda$onError$0(StandardServletAsyncWebRequest.java:146) ~[spring-web-6.0.0.jar:6.0.0]
at java.util.ArrayList.forEach(ArrayList.java:1511) ~[?:?]
The current workaround is to add .dispatcherTypeMatchers(DispatcherType.ASYNC).permitAll()
:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.cors(Customizer.withDefaults());
// Disable csrf, this is a stateless API
http.csrf().disable();
// Disable cookies
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// All the requests that target the access API are set up with a custom JWT
// authentication
http.authorizeHttpRequests(authorize -> authorize
.requestMatchers(apiConfig.getPermitAllWhitelist()).permitAll()
.dispatcherTypeMatchers(DispatcherType.ASYNC).permitAll()
.anyRequest().authenticated())
.oauth2ResourceServer(oauth2ResourceServer -> oauth2ResourceServer
.jwt(jwt -> jwt.decoder(jwtDecoder()))
.authenticationEntryPoint(this::onAuthenticationError));
return http.build();
}
I believe that this is not related only to client-cert authentication. The problem is around the behavior of async processing (emphasis by me):
When a controller returns a DeferredResult, the Filter-Servlet chain is exited, and the Servlet container thread is released. Later, when the DeferredResult is set, an ASYNC dispatch (to the same URL) is made, ...
When the Filter chain is exited, Spring Security saves the SecurityContext
to the RequestAttributeSecurityContextRepository
by default, actually called by DelegatingSecurityContextRepository
. When the ASYNC dispatch is made, the SecurityContextHolderFilter
should load the SecurityContext
from the request attributes, but, the filter does not override OncePerRequestFilter#shouldNotFilterAsyncDispatch
and, therefore, does not apply to that ASYNC dispatch, resulting in the SecurityContext
not loaded into the current thread.
To exemplify better, here is my view of what is happening:
sequenceDiagram
participant User
participant Server
participant ServerX509
User->>Server: REQUEST /stream
activate Server
Server->>ServerX509: REQUEST /x509-protected
activate ServerX509
ServerX509-->>Server:
deactivate ServerX509
Note over Server: Saves SecurityContext
Server-->>User:
deactivate Server
Server->>Server: ASYNC /stream
Note over Server: No SecurityContext
- User performs a REQUEST to /stream
- The Server setup
WebClient
with Client Cert credentials and REQUEST /x509-protected - The Server returns the REQUEST /stream to the client and saves the
SecurityContext
toRequestAttributeSecurityContextRepository
- The Server performs an ASYNC dispatch when the response is written, but there is no
SecurityContext
available, resulting in 401 #11027
@jkjome and @croudet, since this is a complex scenario to simulate, can you folks try something for me?
Create this filter:
public class MyFilter extends SecurityContextHolderFilter {
public MyFilter() {
super(new DelegatingSecurityContextRepository(new RequestAttributeSecurityContextRepository(), new HttpSessionSecurityContextRepository()));
}
@Override
protected boolean shouldNotFilterAsyncDispatch() {
return false;
}
}
and register it in your HttpSecurity
:
http.addFilterBefore(new MyFilter(), SecurityContextHolderFilter.class);
Please, let me know the outcome of this test so we can proceed with a fix. Thanks.
@marcusdacoregio So with the filter I no longer have the exception.
@marcusdacoregio I tried the filter as well, and it works for me too.
I'm not sure this is really fixed on v5.8.2 in Servlets. I've been trying to enable as much of the v6 in v5 behavior as possible, so my code looks something like
.authorizeHttpRequests(auth -> auth
.shouldFilterAllDispatcherTypes(true) // the future v6 default
//.dispatcherTypeMatchers(DispatcherType.ASYNC).permitAll() // uncommenting this line fixes it
.anyRequest().hasRole(role)
)
This breaks Controller methods that use e.g. CompletableFuture, returning a 401 despite the request having been executed.
I'm not married to v5.8 and intend to upgrade to v6 right afterwards, so I'm happy to just add .dispatcherTypeMatchers(DispatcherType.ASYNC).permitAll()
to fix it immediately, but thought I'd let you know that the v5.8 upgrade did not 100% work for me when enabling the v6 defaults.
Hi @napstr,
Are you using requireExplicitSave(true)
? Additionally, were you able to debug the SecurityContextHolderFilter
and check if it's trying to load the SecurityContext
? It is also important to verify what implementation of SecurityContextRepository
you are using.
With that said, if you can confirm that this is not fixed and provide a minimal sample where we could go directly to the problem I recommend that you open a new issue and provide all the details there.
@napstr, in addition to requireExplicitSave(true)
make sure you specify DelegatingSecurityContextRepository
:
http
// ...
.securityContext((securityContext) -> securityContext
.requireExplicitSave(true)
.securityContextRepository(new DelegatingSecurityContextRepository(
new RequestAttributeSecurityContextRepository(),
new HttpSessionSecurityContextRepository()
))
);
Apologies for the late response. I tried specifying the DelegatingSecurityContextRepository
but to no effect afaik. In the end, when we updated to v6 shortly after, I could remove the .dispatcherTypeMatchers(DispatcherType.ASYNC).permitAll()
workaround.
Although I applied all the solutions on the internet, I still could not solve the problem.
Synchronous method in my controller works normally. But the asynchronous method gives an authentication error every time. However, both methods do the same job and get the same Token. The codes of my project are available below. I think the bug is still not resolved.
https://github.com/fkbeys/javasprinasyncauthproblem
Hi @fkbeys, it would be better if you could provide a minimal, reproducible sample. But I guess that you forget to save the SecurityContext
into the SecurityContextRepository
when you authenticate users, see the migration docs.
Since it looks like you are not using HTTP sessions, you might want to use the RequestAttributeSecurityContextRepository
.
Hi @fkbeys, it would be better if you could provide a minimal, reproducible sample. But I guess that you forget to save the
SecurityContext
into theSecurityContextRepository
when you authenticate users, see the migration docs.Since it looks like you are not using HTTP sessions, you might want to use the
RequestAttributeSecurityContextRepository
.
Thank you for your help. I added the below code under the doFilterInternal
RequestAttributeSecurityContextRepository requestAttributeSecurityContextRepository = new RequestAttributeSecurityContextRepository();
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(authentication);
requestAttributeSecurityContextRepository.saveContext(securityContext, request, response);
and also, in filter chain, i set the requireExplicitSave option as true. finally it worked. thank you.
Hi @fkbeys Could you please add detailed solution how you fixed this : @marcusdacoregio : Could you please help here.
I am using it like this but could not fix the issue : @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { if (SecurityContextHolder.getContext().getAuthentication() != null && SecurityContextHolder.getContext().getAuthentication().isAuthenticated()) {
SecurityContext context = SecurityContextHolder.getContext();
this.repository.saveContext(context,request,response);
}
filterChain.doFilter(request, response);
}
and SecurityFilterChain looks like this:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http.cors().disable().authorizeHttpRequests()
.requestMatchers("/configuration/ui",
"/swagger-resources/**",
"/configuration/security",
"/swagger-ui.html",
"/webjars/**",
"/actuator/**").permitAll().anyRequest().authenticated().and().headers((headers -> headers.withObjectPostProcessor(new ObjectPostProcessor<HeaderWriterFilter>() {
@Override
public HeaderWriterFilter postProcess(HeaderWriterFilter headerWriterFilter){
headerWriterFilter.setShouldWriteHeadersEagerly(true);
return headerWriterFilter;
}
}))).headers().frameOptions().sameOrigin().and()
.oauth2ResourceServer(oauth2 -> oauth2
.authenticationManagerResolver(authenticationManagerResolver))
// .securityContext((securityContext -> securityContext.securityContextRepository(new RequestAttributeSecurityContextRepository()))) .build(); }
Hi @fkbeys Could you please add detailed solution how you fixed this : @marcusdacoregio : Could you please help here.
I am using it like this but could not fix the issue : @OverRide protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { if (SecurityContextHolder.getContext().getAuthentication() != null && SecurityContextHolder.getContext().getAuthentication().isAuthenticated()) {
SecurityContext context = SecurityContextHolder.getContext(); this.repository.saveContext(context,request,response); } filterChain.doFilter(request, response); } and SecurityFilterChain looks like this: @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return http.cors().disable().authorizeHttpRequests() .requestMatchers("/configuration/ui", "/swagger-resources/**", "/configuration/security", "/swagger-ui.html", "/webjars/**", "/actuator/**").permitAll().anyRequest().authenticated().and().headers((headers -> headers.withObjectPostProcessor(new ObjectPostProcessor<HeaderWriterFilter>() { @Override public HeaderWriterFilter postProcess(HeaderWriterFilter headerWriterFilter){ headerWriterFilter.setShouldWriteHeadersEagerly(true); return headerWriterFilter; } }))).headers().frameOptions().sameOrigin().and() .oauth2ResourceServer(oauth2 -> oauth2 .authenticationManagerResolver(authenticationManagerResolver))
// .securityContext((securityContext -> securityContext.securityContextRepository(new RequestAttributeSecurityContextRepository()))) .build(); } AuthEntryPointJwt.txt AuthTokenFilter.txt JwtUtils.txt WebSecurityConfig.txt
I have added 4 txt files for the authentication. they are actually .java files.
Thank you @fkbeys for the detailed solution of yours.
I believe that this is not related only to client-cert authentication. The problem is around the behavior of async processing (emphasis by me):
When a controller returns a DeferredResult, the Filter-Servlet chain is exited, and the Servlet container thread is released. Later, when the DeferredResult is set, an ASYNC dispatch (to the same URL) is made, ...
When the Filter chain is exited, Spring Security saves the
SecurityContext
to theRequestAttributeSecurityContextRepository
by default, actually called byDelegatingSecurityContextRepository
. When the ASYNC dispatch is made, theSecurityContextHolderFilter
should load theSecurityContext
from the request attributes, but, the filter does not overrideOncePerRequestFilter#shouldNotFilterAsyncDispatch
and, therefore, does not apply to that ASYNC dispatch, resulting in theSecurityContext
not loaded into the current thread.To exemplify better, here is my view of what is happening:
sequenceDiagram participant User participant Server participant ServerX509 User->>Server: REQUEST /stream activate Server Server->>ServerX509: REQUEST /x509-protected activate ServerX509 ServerX509-->>Server: deactivate ServerX509 Note over Server: Saves SecurityContext Server-->>User: deactivate Server Server->>Server: ASYNC /stream Note over Server: No SecurityContext
- User performs a REQUEST to /stream
- The Server setup
WebClient
with Client Cert credentials and REQUEST /x509-protected- The Server returns the REQUEST /stream to the client and saves the
SecurityContext
toRequestAttributeSecurityContextRepository
- The Server performs an ASYNC dispatch when the response is written, but there is no
SecurityContext
available, resulting in 401 Authorization on Every Dispatch Type #11027@jkjome and @croudet, since this is a complex scenario to simulate, can you folks try something for me?
Create this filter:
public class MyFilter extends SecurityContextHolderFilter { public MyFilter() { super(new DelegatingSecurityContextRepository(new RequestAttributeSecurityContextRepository(), new HttpSessionSecurityContextRepository())); } @Override protected boolean shouldNotFilterAsyncDispatch() { return false; } }
and register it in your
HttpSecurity
:http.addFilterBefore(new MyFilter(), SecurityContextHolderFilter.class);
Please, let me know the outcome of this test so we can proceed with a fix. Thanks.
Hi @marcusdacoregio
For Spring Security 6.1.2 , I cannot override the below method:
@Override
protected boolean shouldNotFilterAsyncDispatch() {
return false;
}
its no longer there in SecurityContextHolderFilter
SecurityContextHolderFilter
Hi @marcusdacoregio , the overridden method : @Override protected boolean shouldNotFilterAsyncDispatch() { return false; }
only works for spring 3.0.0 and not for 3.0.6 or 3.0.1. because with Spring Security 6.0.1 onwards , SecurityContextHolderFilter doest not extends OncePerRequestFilter.
How can I fix the issue in this case? Can we have another fix for higher versions?
Hi @kotharikrati, you should not need the workaround anymore if you are using the latest version of Spring Security or at least >= 5.8.1 or >= 6.0.1. If you have the same error it is likely another thing is happening, please add logging.level.org.springframework.security=TRACE
to your application.properties
and check what is happening.
Hi @marcusdacoregio , I will check again . Thank you!
If anyone is experiencing a similar problem, it may be helpful to check the document below.
https://docs.spring.io/spring-security/reference/5.8/migration/servlet/session-management.html https://docs.spring.io/spring-security/reference/servlet/authentication/session-management.html