WebFlux httpBasic() matches on XHR requests
Closes gh-9660
This commit is contained in:
parent
b97e93a486
commit
6725b1324a
|
@ -41,6 +41,7 @@ import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||||
import org.springframework.core.convert.converter.Converter;
|
import org.springframework.core.convert.converter.Converter;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
import org.springframework.security.authentication.DelegatingReactiveAuthenticationManager;
|
import org.springframework.security.authentication.DelegatingReactiveAuthenticationManager;
|
||||||
|
@ -113,6 +114,7 @@ import org.springframework.security.web.server.authentication.AnonymousAuthentic
|
||||||
import org.springframework.security.web.server.authentication.AuthenticationConverterServerWebExchangeMatcher;
|
import org.springframework.security.web.server.authentication.AuthenticationConverterServerWebExchangeMatcher;
|
||||||
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
|
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
|
||||||
import org.springframework.security.web.server.authentication.HttpBasicServerAuthenticationEntryPoint;
|
import org.springframework.security.web.server.authentication.HttpBasicServerAuthenticationEntryPoint;
|
||||||
|
import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint;
|
||||||
import org.springframework.security.web.server.authentication.ReactivePreAuthenticatedAuthenticationManager;
|
import org.springframework.security.web.server.authentication.ReactivePreAuthenticatedAuthenticationManager;
|
||||||
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationEntryPoint;
|
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationEntryPoint;
|
||||||
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationFailureHandler;
|
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationFailureHandler;
|
||||||
|
@ -1911,13 +1913,25 @@ public class ServerHttpSecurity {
|
||||||
*/
|
*/
|
||||||
public final class HttpBasicSpec {
|
public final class HttpBasicSpec {
|
||||||
|
|
||||||
|
private final ServerWebExchangeMatcher xhrMatcher = (exchange) -> Mono.just(exchange.getRequest().getHeaders())
|
||||||
|
.filter((h) -> h.getOrEmpty("X-Requested-With").contains("XMLHttpRequest"))
|
||||||
|
.flatMap((h) -> ServerWebExchangeMatcher.MatchResult.match())
|
||||||
|
.switchIfEmpty(ServerWebExchangeMatcher.MatchResult.notMatch());
|
||||||
|
|
||||||
private ReactiveAuthenticationManager authenticationManager;
|
private ReactiveAuthenticationManager authenticationManager;
|
||||||
|
|
||||||
private ServerSecurityContextRepository securityContextRepository;
|
private ServerSecurityContextRepository securityContextRepository;
|
||||||
|
|
||||||
private ServerAuthenticationEntryPoint entryPoint = new HttpBasicServerAuthenticationEntryPoint();
|
private ServerAuthenticationEntryPoint entryPoint;
|
||||||
|
|
||||||
private HttpBasicSpec() {
|
private HttpBasicSpec() {
|
||||||
|
List<DelegateEntry> entryPoints = new ArrayList<>();
|
||||||
|
entryPoints
|
||||||
|
.add(new DelegateEntry(this.xhrMatcher, new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED)));
|
||||||
|
DelegatingServerAuthenticationEntryPoint defaultEntryPoint = new DelegatingServerAuthenticationEntryPoint(
|
||||||
|
entryPoints);
|
||||||
|
defaultEntryPoint.setDefaultEntryPoint(new HttpBasicServerAuthenticationEntryPoint());
|
||||||
|
this.entryPoint = defaultEntryPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1982,7 +1996,13 @@ public class ServerHttpSecurity {
|
||||||
MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_XML, MediaType.MULTIPART_FORM_DATA,
|
MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_XML, MediaType.MULTIPART_FORM_DATA,
|
||||||
MediaType.TEXT_XML);
|
MediaType.TEXT_XML);
|
||||||
restMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
|
restMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
|
||||||
ServerHttpSecurity.this.defaultEntryPoints.add(new DelegateEntry(restMatcher, this.entryPoint));
|
ServerWebExchangeMatcher notHtmlMatcher = new NegatedServerWebExchangeMatcher(
|
||||||
|
new MediaTypeServerWebExchangeMatcher(MediaType.TEXT_HTML));
|
||||||
|
ServerWebExchangeMatcher restNotHtmlMatcher = new AndServerWebExchangeMatcher(
|
||||||
|
Arrays.asList(notHtmlMatcher, restMatcher));
|
||||||
|
ServerWebExchangeMatcher preferredMatcher = new OrServerWebExchangeMatcher(
|
||||||
|
Arrays.asList(this.xhrMatcher, restNotHtmlMatcher));
|
||||||
|
ServerHttpSecurity.this.defaultEntryPoints.add(new DelegateEntry(preferredMatcher, this.entryPoint));
|
||||||
AuthenticationWebFilter authenticationFilter = new AuthenticationWebFilter(this.authenticationManager);
|
AuthenticationWebFilter authenticationFilter = new AuthenticationWebFilter(this.authenticationManager);
|
||||||
authenticationFilter
|
authenticationFilter
|
||||||
.setAuthenticationFailureHandler(new ServerAuthenticationEntryPointFailureHandler(this.entryPoint));
|
.setAuthenticationFailureHandler(new ServerAuthenticationEntryPointFailureHandler(this.entryPoint));
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2019 the original author or authors.
|
* Copyright 2002-2021 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -32,6 +32,7 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.test.publisher.TestPublisher;
|
import reactor.test.publisher.TestPublisher;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.security.authentication.ReactiveAuthenticationManager;
|
import org.springframework.security.authentication.ReactiveAuthenticationManager;
|
||||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
import org.springframework.security.config.annotation.web.reactive.ServerHttpSecurityConfigurationBuilder;
|
import org.springframework.security.config.annotation.web.reactive.ServerHttpSecurityConfigurationBuilder;
|
||||||
|
@ -45,9 +46,11 @@ import org.springframework.security.oauth2.core.endpoint.TestOAuth2Authorization
|
||||||
import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
|
import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
|
||||||
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
|
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
|
||||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||||
|
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
|
||||||
import org.springframework.security.web.server.WebFilterChainProxy;
|
import org.springframework.security.web.server.WebFilterChainProxy;
|
||||||
import org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilterTests;
|
import org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilterTests;
|
||||||
import org.springframework.security.web.server.authentication.HttpBasicServerAuthenticationEntryPoint;
|
import org.springframework.security.web.server.authentication.HttpBasicServerAuthenticationEntryPoint;
|
||||||
|
import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint;
|
||||||
import org.springframework.security.web.server.authentication.ServerX509AuthenticationConverter;
|
import org.springframework.security.web.server.authentication.ServerX509AuthenticationConverter;
|
||||||
import org.springframework.security.web.server.authentication.logout.DelegatingServerLogoutHandler;
|
import org.springframework.security.web.server.authentication.logout.DelegatingServerLogoutHandler;
|
||||||
import org.springframework.security.web.server.authentication.logout.LogoutWebFilter;
|
import org.springframework.security.web.server.authentication.logout.LogoutWebFilter;
|
||||||
|
@ -184,6 +187,25 @@ public class ServerHttpSecurityTests {
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void basicWhenXHRRequestThenUnauthorized() {
|
||||||
|
ServerAuthenticationEntryPoint authenticationEntryPoint = spy(
|
||||||
|
new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED));
|
||||||
|
this.http.httpBasic().authenticationEntryPoint(authenticationEntryPoint);
|
||||||
|
this.http.authorizeExchange().anyExchange().authenticated();
|
||||||
|
WebTestClient client = buildClient();
|
||||||
|
// @formatter:off
|
||||||
|
client.get().uri("/")
|
||||||
|
.header("X-Requested-With", "XMLHttpRequest")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isUnauthorized()
|
||||||
|
.expectHeader().doesNotExist("WWW-Authenticate")
|
||||||
|
.expectHeader().valueMatches(HttpHeaders.CACHE_CONTROL, ".+")
|
||||||
|
.expectBody().isEmpty();
|
||||||
|
// @formatter:on
|
||||||
|
verify(authenticationEntryPoint).commence(any(), any());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void buildWhenServerWebExchangeFromContextThenFound() {
|
public void buildWhenServerWebExchangeFromContextThenFound() {
|
||||||
SecurityWebFilterChain filter = this.http.build();
|
SecurityWebFilterChain filter = this.http.build();
|
||||||
|
|
Loading…
Reference in New Issue