From b3ca5986791ac5c97022d64646df1a6e1ec2a8b7 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Fri, 25 May 2018 15:17:08 -0500 Subject: [PATCH] Add WebClient Bearer token support Fixes: gh-5389 --- .../DefaultReactiveOAuth2UserService.java | 5 ++- ...uthorizedClientExchangeFilterFunction.java | 16 +++---- .../security/web/http/SecurityHeaders.java | 43 +++++++++++++++++++ .../web/http/SecurityHeadersTests.java | 42 ++++++++++++++++++ 4 files changed, 95 insertions(+), 11 deletions(-) create mode 100644 web/src/main/java/org/springframework/security/web/http/SecurityHeaders.java create mode 100644 web/src/test/groovy/org/springframework/security/web/http/SecurityHeadersTests.java diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserService.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserService.java index 90a79a7bf8..1f9bc10c14 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserService.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserService.java @@ -16,6 +16,8 @@ package org.springframework.security.oauth2.client.userinfo; +import static org.springframework.security.web.http.SecurityHeaders.bearerToken; + import java.net.UnknownHostException; import java.util.HashSet; import java.util.Map; @@ -99,8 +101,7 @@ public class DefaultReactiveOAuth2UserService implements ReactiveOAuth2UserServi Mono> userAttributes = this.webClient.get() .uri(userInfoUri) - .header(HttpHeaders.AUTHORIZATION, - "Bearer " + userRequest.getAccessToken().getTokenValue()) + .headers(bearerToken(userRequest.getAccessToken().getTokenValue())) .retrieve() .onStatus(s -> s != HttpStatus.OK, response -> { return parse(response).map(userInfoErrorResponse -> { diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/OAuth2AuthorizedClientExchangeFilterFunction.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/OAuth2AuthorizedClientExchangeFilterFunction.java index 178868c39c..8df207778a 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/OAuth2AuthorizedClientExchangeFilterFunction.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/OAuth2AuthorizedClientExchangeFilterFunction.java @@ -16,17 +16,19 @@ package org.springframework.security.oauth2.client.web.reactive.function.client; -import org.springframework.http.HttpHeaders; +import static org.springframework.security.web.http.SecurityHeaders.bearerToken; + +import java.util.Map; +import java.util.Optional; +import java.util.function.Consumer; + import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; 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 reactor.core.publisher.Mono; -import java.util.Map; -import java.util.Optional; -import java.util.function.Consumer; +import reactor.core.publisher.Mono; /** * Provides an easy mechanism for using an {@link OAuth2AuthorizedClient} to make OAuth2 requests by including the @@ -77,8 +79,4 @@ public final class OAuth2AuthorizedClientExchangeFilterFunction implements Excha .headers(bearerToken(authorizedClient.getAccessToken().getTokenValue())) .build(); } - - private Consumer bearerToken(String token) { - return headers -> headers.set(HttpHeaders.AUTHORIZATION, "Bearer " + token); - } } diff --git a/web/src/main/java/org/springframework/security/web/http/SecurityHeaders.java b/web/src/main/java/org/springframework/security/web/http/SecurityHeaders.java new file mode 100644 index 0000000000..ef373724fc --- /dev/null +++ b/web/src/main/java/org/springframework/security/web/http/SecurityHeaders.java @@ -0,0 +1,43 @@ +/* + * 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.web.http; + +import org.springframework.http.HttpHeaders; +import org.springframework.util.Assert; + +import java.util.function.Consumer; + +/** + * Utilities for interacting with {@link HttpHeaders} + * + * @author Rob Winch + * @since 5.1 + */ +public final class SecurityHeaders { + + /** + * Sets the provided value as a Bearer token in a header with the name of {@link HttpHeaders#AUTHORIZATION} + * @param bearerTokenValue the bear token value + * @return a {@link Consumer} that sets the header. + */ + public static Consumer bearerToken(String bearerTokenValue) { + Assert.hasText(bearerTokenValue, "bearerTokenValue cannot be null"); + return headers -> headers.set(HttpHeaders.AUTHORIZATION, "Bearer " + bearerTokenValue); + } + + private SecurityHeaders() {} +} diff --git a/web/src/test/groovy/org/springframework/security/web/http/SecurityHeadersTests.java b/web/src/test/groovy/org/springframework/security/web/http/SecurityHeadersTests.java new file mode 100644 index 0000000000..164a4ba39f --- /dev/null +++ b/web/src/test/groovy/org/springframework/security/web/http/SecurityHeadersTests.java @@ -0,0 +1,42 @@ +/* + * 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.web.http; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.*; + +/** + * @author Rob Winch + * @since 5.1 + */ +public class SecurityHeadersTests { + + @Test + public void bearerTokenWhenNullThenIllegalArgumentException() { + String bearerTokenValue = null; + assertThatThrownBy(() -> SecurityHeaders.bearerToken(bearerTokenValue)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void bearerTokenWhenEmptyStringThenIllegalArgumentException() { + assertThatThrownBy(() -> SecurityHeaders.bearerToken("")) + .isInstanceOf(IllegalArgumentException.class); + } + +}