diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.java
new file mode 100644
index 0000000000..7001ecd891
--- /dev/null
+++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.java
@@ -0,0 +1,408 @@
+/*
+ * Copyright 2002-2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.oauth2.client.web.reactive.function.client;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.context.ReactiveSecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
+import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
+import org.springframework.security.oauth2.client.registration.ClientRegistration;
+import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
+import org.springframework.security.oauth2.core.OAuth2RefreshToken;
+import org.springframework.util.Assert;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.reactive.function.client.ClientRequest;
+import org.springframework.web.reactive.function.client.ClientResponse;
+import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
+import org.springframework.web.reactive.function.client.ExchangeFunction;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Mono;
+import reactor.core.scheduler.Schedulers;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.time.Clock;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Base64;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Consumer;
+
+import static org.springframework.security.oauth2.core.web.reactive.function.OAuth2BodyExtractors.oauth2AccessTokenResponse;
+import static org.springframework.security.web.http.SecurityHeaders.bearerToken;
+
+/**
+ * Provides an easy mechanism for using an {@link OAuth2AuthorizedClient} to make OAuth2 requests by including the
+ * token as a Bearer Token. It also provides mechanisms for looking up the {@link OAuth2AuthorizedClient}. This class is
+ * intended to be used in a servlet environment.
+ *
+ * Example usage:
+ *
+ *
+ * OAuth2AuthorizedClientExchangeFilterFunction oauth2 = new OAuth2AuthorizedClientExchangeFilterFunction(authorizedClientService);
+ * WebClient webClient = WebClient.builder()
+ * .apply(oauth2.oauth2Configuration())
+ * .build();
+ * Mono response = webClient
+ * .get()
+ * .uri(uri)
+ * .attributes(oauth2AuthorizedClient(authorizedClient))
+ * // ...
+ * .retrieve()
+ * .bodyToMono(String.class);
+ *
+ *
+ * An attempt to automatically refresh the token will be made if all of the following
+ * are true:
+ *
+ *
+ * - The ReactiveOAuth2AuthorizedClientService on the
+ * {@link ServletOAuth2AuthorizedClientExchangeFilterFunction} is not null
+ * - A refresh token is present on the OAuth2AuthorizedClient
+ * - The access token will be expired in
+ * {@link #setAccessTokenExpiresSkew(Duration)}
+ * - The {@link ReactiveSecurityContextHolder} will be used to attempt to save
+ * the token. If it is empty, then the principal name on the OAuth2AuthorizedClient
+ * will be used to create an Authentication for saving.
+ *
+ *
+ * @author Rob Winch
+ * @since 5.1
+ */
+public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implements ExchangeFilterFunction {
+ /**
+ * The request attribute name used to locate the {@link OAuth2AuthorizedClient}.
+ */
+ private static final String OAUTH2_AUTHORIZED_CLIENT_ATTR_NAME = OAuth2AuthorizedClient.class.getName();
+ private static final String CLIENT_REGISTRATION_ID_ATTR_NAME = OAuth2AuthorizedClient.class.getName().concat(".CLIENT_REGISTRATION_ID");
+ private static final String AUTHENTICATION_ATTR_NAME = Authentication.class.getName();
+ private static final String HTTP_SERVLET_REQUEST_ATTR_NAME = HttpServletRequest.class.getName();
+ private static final String HTTP_SERVLET_RESPONSE_ATTR_NAME = HttpServletResponse.class.getName();
+
+ private Clock clock = Clock.systemUTC();
+
+ private Duration accessTokenExpiresSkew = Duration.ofMinutes(1);
+
+ private OAuth2AuthorizedClientRepository authorizedClientRepository;
+
+ public ServletOAuth2AuthorizedClientExchangeFilterFunction() {}
+
+ public ServletOAuth2AuthorizedClientExchangeFilterFunction(OAuth2AuthorizedClientRepository authorizedClientRepository) {
+ this.authorizedClientRepository = authorizedClientRepository;
+ }
+
+ /**
+ * Configures the builder with {@link #defaultRequest()} and adds this as a {@link ExchangeFilterFunction}
+ * @return the {@link Consumer} to configure the builder
+ */
+ public Consumer oauth2Configuration() {
+ return builder -> builder.defaultRequest(defaultRequest()).filter(this);
+ }
+
+ /**
+ * Provides defaults for the {@link HttpServletRequest} and the {@link HttpServletResponse} using
+ * {@link RequestContextHolder}. It also provides defaults for the {@link Authentication} using
+ * {@link SecurityContextHolder}. It also can default the {@link OAuth2AuthorizedClient} using the
+ * {@link #clientRegistrationId(String)} or the {@link #authentication(Authentication)}.
+ * @return the {@link Consumer} to populate the attributes
+ */
+ public Consumer> defaultRequest() {
+ return spec -> {
+ spec.attributes(attrs -> {
+ populateDefaultRequestResponse(attrs);
+ populateDefaultAuthentication(attrs);
+ populateDefaultOAuth2AuthorizedClient(attrs);
+ });
+ };
+ }
+
+ /**
+ * Modifies the {@link ClientRequest#attributes()} to include the {@link OAuth2AuthorizedClient} to be used for
+ * providing the Bearer Token.
+ *
+ * @param authorizedClient the {@link OAuth2AuthorizedClient} to use.
+ * @return the {@link Consumer} to populate the attributes
+ */
+ public static Consumer