Skip to content

SecurityContextHolderFilter does not apply to async dispatch #11962

Closed
@jkjome

Description

@jkjome

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

  1. 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
  2. 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));
}

Metadata

Metadata

Labels

in: webAn issue in web modules (web, webmvc)type: bugA general bug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions