From a47053a64825cbb605bdcab3d0084e4877605522 Mon Sep 17 00:00:00 2001 From: kostya05983 Date: Sun, 4 Aug 2019 17:11:24 +0700 Subject: [PATCH] Replace streams gh-7154 First version of replacing streams fix wwwAuthenticate and codestyle fix errors in implementation to pass tests Fix review notes Remove uneccessary final to align with cb Short circuit way to authorize Simplify error message, make code readably Return error while duplicate key found Delete check for duplicate, checkstyle issues Return duplicate error --- .../AuthenticationConfiguration.java | 22 ++++++++++---- ...AuthorityReactiveAuthorizationManager.java | 14 +++++---- .../security/converter/RsaKeyConverters.java | 26 ++++++++++------- .../MapReactiveUserDetailsService.java | 8 +++-- .../MapReactiveUserDetailsServiceTests.java | 7 +++++ ...egatingOAuth2AuthorizedClientProvider.java | 13 +++++---- ...OAuth2AuthorizedClientProviderBuilder.java | 14 ++++----- .../authentication/OidcIdTokenValidator.java | 6 +--- .../InMemoryClientRegistrationRepository.java | 23 +++++++++------ ...yReactiveClientRegistrationRepository.java | 15 ++++++---- .../OidcIdTokenValidatorTests.java | 10 +++++++ ...moryClientRegistrationRepositoryTests.java | 12 ++++---- .../ObjectToListStringConverter.java | 14 +++++---- .../endpoint/OAuth2AuthorizationRequest.java | 11 ++++--- ...cessTokenResponseHttpMessageConverter.java | 29 ++++++++++--------- .../oauth2/core/user/DefaultOAuth2User.java | 25 ++++++++-------- .../oauth2/jose/jws/MacAlgorithm.java | 11 +++---- .../oauth2/jose/jws/SignatureAlgorithm.java | 12 ++++---- .../jwt/MappedJwtClaimSetConverter.java | 20 ++++++++----- .../NimbusOpaqueTokenIntrospector.java | 10 ++++--- ...NimbusReactiveOpaqueTokenIntrospector.java | 10 ++++--- .../BearerTokenAuthenticationEntryPoint.java | 26 +++++++++++------ .../BearerTokenAccessDeniedHandler.java | 27 ++++++++++------- .../BearerTokenServerAccessDeniedHandler.java | 18 ++++++++---- ...erTokenServerAuthenticationEntryPoint.java | 21 +++++++++----- .../writers/ClearSiteDataHeaderWriter.java | 14 ++++++--- ...ingServerAuthenticationSuccessHandler.java | 10 ++++--- .../logout/DelegatingServerLogoutHandler.java | 14 ++++----- .../ClearSiteDataServerHttpHeadersWriter.java | 12 ++++---- .../CompositeServerHttpHeadersWriter.java | 11 +++---- 30 files changed, 282 insertions(+), 183 deletions(-) diff --git a/config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/AuthenticationConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/AuthenticationConfiguration.java index 60c4ec3873f..dc5ec528b3f 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/AuthenticationConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/AuthenticationConfiguration.java @@ -43,12 +43,11 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.util.Assert; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.ArrayList; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; /** * Exports the authentication {@link Configuration} @@ -153,10 +152,7 @@ private T lazyBean(Class interfaceName) { } String beanName; if (beanNamesForType.length > 1) { - List primaryBeanNames = Arrays.stream(beanNamesForType) - .filter(i -> applicationContext instanceof ConfigurableApplicationContext) - .filter(n -> ((ConfigurableApplicationContext) applicationContext).getBeanFactory().getBeanDefinition(n).isPrimary()) - .collect(Collectors.toList()); + List primaryBeanNames = getPrimaryBeanNames(beanNamesForType); Assert.isTrue(primaryBeanNames.size() != 0, () -> "Found " + beanNamesForType.length + " beans for type " + interfaceName + ", but none marked as primary"); @@ -175,6 +171,20 @@ private T lazyBean(Class interfaceName) { return (T) proxyFactory.getObject(); } + private List getPrimaryBeanNames(String[] beanNamesForType) { + List list = new ArrayList<>(); + if (!(applicationContext instanceof ConfigurableApplicationContext)) { + return Collections.emptyList(); + } + for (String beanName : beanNamesForType) { + if (((ConfigurableApplicationContext) applicationContext).getBeanFactory() + .getBeanDefinition(beanName).isPrimary()) { + list.add(beanName); + } + } + return list; + } + private AuthenticationManager getAuthenticationManagerBean() { return lazyBean(AuthenticationManager.class); } diff --git a/core/src/main/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManager.java index 6189ae08e72..5ab05c960c0 100644 --- a/core/src/main/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManager.java +++ b/core/src/main/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManager.java @@ -22,7 +22,6 @@ import java.util.Arrays; import java.util.List; -import java.util.stream.Stream; /** * A {@link ReactiveAuthorizationManager} that determines if the current user is @@ -109,9 +108,14 @@ public static AuthorityReactiveAuthorizationManager hasAnyRole(String... Assert.notNull(role, "role cannot be null"); } - return hasAnyAuthority(Stream.of(roles) - .map(r -> "ROLE_" + r) - .toArray(String[]::new) - ); + return hasAnyAuthority(toNamedRolesArray(roles)); + } + + private static String[] toNamedRolesArray(String... roles) { + String[] result = new String[roles.length]; + for (int i=0; i < roles.length; i++) { + result[i] = "ROLE_" + roles[i]; + } + return result; } } diff --git a/core/src/main/java/org/springframework/security/converter/RsaKeyConverters.java b/core/src/main/java/org/springframework/security/converter/RsaKeyConverters.java index d499a8d19ef..c0aeb2a53dd 100644 --- a/core/src/main/java/org/springframework/security/converter/RsaKeyConverters.java +++ b/core/src/main/java/org/springframework/security/converter/RsaKeyConverters.java @@ -16,8 +16,8 @@ package org.springframework.security.converter; -import java.io.BufferedReader; import java.io.InputStream; +import java.io.BufferedReader; import java.io.InputStreamReader; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; @@ -25,8 +25,8 @@ import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; -import java.util.Base64; import java.util.List; +import java.util.Base64; import java.util.stream.Collectors; import org.springframework.core.convert.converter.Converter; @@ -66,10 +66,13 @@ public static Converter pkcs8() { Assert.isTrue(!lines.isEmpty() && lines.get(0).startsWith(PKCS8_PEM_HEADER), "Key is not in PEM-encoded PKCS#8 format, " + "please check that the header begins with -----" + PKCS8_PEM_HEADER + "-----"); - String base64Encoded = lines.stream() - .filter(RsaKeyConverters::isNotPkcs8Wrapper) - .collect(Collectors.joining()); - byte[] pkcs8 = Base64.getDecoder().decode(base64Encoded); + StringBuilder base64Encoded = new StringBuilder(); + for (String line : lines) { + if (RsaKeyConverters.isNotPkcs8Wrapper(line)) { + base64Encoded.append(line); + } + } + byte[] pkcs8 = Base64.getDecoder().decode(base64Encoded.toString()); try { return (RSAPrivateKey) keyFactory.generatePrivate( @@ -97,10 +100,13 @@ public static Converter x509() { Assert.isTrue(!lines.isEmpty() && lines.get(0).startsWith(X509_PEM_HEADER), "Key is not in PEM-encoded X.509 format, " + "please check that the header begins with -----" + X509_PEM_HEADER + "-----"); - String base64Encoded = lines.stream() - .filter(RsaKeyConverters::isNotX509Wrapper) - .collect(Collectors.joining()); - byte[] x509 = Base64.getDecoder().decode(base64Encoded); + StringBuilder base64Encoded = new StringBuilder(); + for (String line : lines) { + if (RsaKeyConverters.isNotX509Wrapper(line)) { + base64Encoded.append(line); + } + } + byte[] x509 = Base64.getDecoder().decode(base64Encoded.toString()); try { return (RSAPublicKey) keyFactory.generatePublic( diff --git a/core/src/main/java/org/springframework/security/core/userdetails/MapReactiveUserDetailsService.java b/core/src/main/java/org/springframework/security/core/userdetails/MapReactiveUserDetailsService.java index c05f22a8275..f0d8c12e569 100644 --- a/core/src/main/java/org/springframework/security/core/userdetails/MapReactiveUserDetailsService.java +++ b/core/src/main/java/org/springframework/security/core/userdetails/MapReactiveUserDetailsService.java @@ -19,8 +19,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; +import java.util.concurrent.ConcurrentHashMap; import org.springframework.util.Assert; import reactor.core.publisher.Mono; @@ -56,7 +55,10 @@ public MapReactiveUserDetailsService(UserDetails... users) { */ public MapReactiveUserDetailsService(Collection users) { Assert.notEmpty(users, "users cannot be null or empty"); - this.users = users.stream().collect(Collectors.toConcurrentMap( u -> getKey(u.getUsername()), Function.identity())); + this.users = new ConcurrentHashMap<>(); + for (UserDetails user : users) { + this.users.put(getKey(user.getUsername()), user); + } } @Override diff --git a/core/src/test/java/org/springframework/security/core/userdetails/MapReactiveUserDetailsServiceTests.java b/core/src/test/java/org/springframework/security/core/userdetails/MapReactiveUserDetailsServiceTests.java index e8b33ef3bc0..97154ac4166 100644 --- a/core/src/test/java/org/springframework/security/core/userdetails/MapReactiveUserDetailsServiceTests.java +++ b/core/src/test/java/org/springframework/security/core/userdetails/MapReactiveUserDetailsServiceTests.java @@ -46,6 +46,13 @@ public void constructorEmptyUsers() { new MapReactiveUserDetailsService(users); } + @Test + public void constructorCaseIntensiveKey() { + UserDetails userDetails = User.withUsername("USER").password("password").roles("USER").build(); + MapReactiveUserDetailsService userDetailsService = new MapReactiveUserDetailsService(userDetails); + assertThat(userDetailsService.findByUsername("user").block()).isEqualTo(userDetails); + } + @Test public void findByUsernameWhenFoundThenReturns() { assertThat((users.findByUsername(USER_DETAILS.getUsername()).block())).isEqualTo(USER_DETAILS); diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/DelegatingOAuth2AuthorizedClientProvider.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/DelegatingOAuth2AuthorizedClientProvider.java index 0343b96071c..d57bcf8153f 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/DelegatingOAuth2AuthorizedClientProvider.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/DelegatingOAuth2AuthorizedClientProvider.java @@ -22,7 +22,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Objects; /** * An implementation of an {@link OAuth2AuthorizedClientProvider} that simply delegates @@ -64,10 +63,12 @@ public DelegatingOAuth2AuthorizedClientProvider(List authorizedClientProvider.authorize(context)) - .filter(Objects::nonNull) - .findFirst() - .orElse(null); + for (OAuth2AuthorizedClientProvider authorizedClientProvider : authorizedClientProviders) { + OAuth2AuthorizedClient oauth2AuthorizedClient = authorizedClientProvider.authorize(context); + if (oauth2AuthorizedClient != null) { + return oauth2AuthorizedClient; + } + } + return null; } } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizedClientProviderBuilder.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizedClientProviderBuilder.java index fd67555e945..7c5345ad5f6 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizedClientProviderBuilder.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizedClientProviderBuilder.java @@ -23,11 +23,11 @@ import java.time.Clock; import java.time.Duration; import java.time.Instant; -import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; +import java.util.List; +import java.util.LinkedHashMap; +import java.util.ArrayList; import java.util.function.Consumer; -import java.util.stream.Collectors; /** * A builder that builds a {@link DelegatingOAuth2AuthorizedClientProvider} composed of @@ -286,10 +286,10 @@ public OAuth2AuthorizedClientProvider build() { * @return the {@link DelegatingOAuth2AuthorizedClientProvider} */ public OAuth2AuthorizedClientProvider build() { - List authorizedClientProviders = - this.builders.values().stream() - .map(Builder::build) - .collect(Collectors.toList()); + List authorizedClientProviders = new ArrayList<>(); + for (Builder builder : this.builders.values()) { + authorizedClientProviders.add(builder.build()); + } return new DelegatingOAuth2AuthorizedClientProvider(authorizedClientProviders); } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenValidator.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenValidator.java index 4cd9e3f90d5..806c837112f 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenValidator.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenValidator.java @@ -32,7 +32,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; /** * An {@link OAuth2TokenValidator} responsible for @@ -137,11 +136,8 @@ public void setClockSkew(Duration clockSkew) { } private static OAuth2Error invalidIdToken(Map invalidClaims) { - String claimsDetail = invalidClaims.entrySet().stream() - .map(it -> it.getKey() + " (" + it.getValue() + ")") - .collect(Collectors.joining(", ")); return new OAuth2Error("invalid_id_token", - "The ID Token contains invalid claims: " + claimsDetail, + "The ID Token contains invalid claims: " + invalidClaims, "https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation"); } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/InMemoryClientRegistrationRepository.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/InMemoryClientRegistrationRepository.java index 456c68be7c7..7378d5fe4f3 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/InMemoryClientRegistrationRepository.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/InMemoryClientRegistrationRepository.java @@ -22,12 +22,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentMap; -import java.util.function.Function; -import java.util.stream.Collector; - -import static java.util.stream.Collectors.collectingAndThen; -import static java.util.stream.Collectors.toConcurrentMap; +import java.util.concurrent.ConcurrentHashMap; /** * A {@link ClientRegistrationRepository} that stores {@link ClientRegistration}(s) in-memory. @@ -62,9 +57,19 @@ public InMemoryClientRegistrationRepository(List registratio private static Map createRegistrationsMap(List registrations) { Assert.notEmpty(registrations, "registrations cannot be empty"); - Collector> collector = - toConcurrentMap(ClientRegistration::getRegistrationId, Function.identity()); - return registrations.stream().collect(collectingAndThen(collector, Collections::unmodifiableMap)); + return toUnmodifiableConcurrentMap(registrations); + } + + private static Map toUnmodifiableConcurrentMap(List registrations) { + ConcurrentHashMap result = new ConcurrentHashMap<>(); + for (ClientRegistration registration : registrations) { + if (result.containsKey(registration.getRegistrationId())) { + throw new IllegalStateException(String.format("Duplicate key %s", + registration.getRegistrationId())); + } + result.put(registration.getRegistrationId(), registration); + } + return Collections.unmodifiableMap(result); } /** diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/InMemoryReactiveClientRegistrationRepository.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/InMemoryReactiveClientRegistrationRepository.java index c3b50a8d23e..1cddacc26c3 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/InMemoryReactiveClientRegistrationRepository.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/InMemoryReactiveClientRegistrationRepository.java @@ -19,8 +19,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; -import java.util.stream.Collectors; +import java.util.concurrent.ConcurrentHashMap; import org.springframework.util.Assert; @@ -61,11 +60,9 @@ public InMemoryReactiveClientRegistrationRepository(ClientRegistration... regist */ public InMemoryReactiveClientRegistrationRepository(List registrations) { Assert.notEmpty(registrations, "registrations cannot be null or empty"); - this.clientIdToClientRegistration = registrations.stream() - .collect(Collectors.toConcurrentMap(ClientRegistration::getRegistrationId, Function.identity())); + this.clientIdToClientRegistration = toConcurrentMap(registrations); } - @Override public Mono findByRegistrationId(String registrationId) { return Mono.justOrEmpty(this.clientIdToClientRegistration.get(registrationId)); @@ -80,4 +77,12 @@ public Mono findByRegistrationId(String registrationId) { public Iterator iterator() { return this.clientIdToClientRegistration.values().iterator(); } + + private ConcurrentHashMap toConcurrentMap(List registrations) { + ConcurrentHashMap result = new ConcurrentHashMap<>(); + for (ClientRegistration registration : registrations) { + result.put(registration.getRegistrationId(), registration); + } + return result; + } } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenValidatorTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenValidatorTests.java index 5ef5f7d25ff..bad2f80969f 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenValidatorTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenValidatorTests.java @@ -229,6 +229,16 @@ public void validateWhenMissingClaimsThenHasErrors() { .allMatch(msg -> msg.contains(IdTokenClaimNames.EXP)); } + @Test + public void validateFormatError() { + this.claims.remove(IdTokenClaimNames.SUB); + this.claims.remove(IdTokenClaimNames.AUD); + assertThat(this.validateIdToken()) + .hasSize(1) + .extracting(OAuth2Error::getDescription) + .allMatch(msg -> msg.equals("The ID Token contains invalid claims: {sub=null, aud=null}")); + } + private Collection validateIdToken() { Jwt idToken = new Jwt("token123", this.issuedAt, this.expiresAt, this.headers, this.claims); OidcIdTokenValidator validator = new OidcIdTokenValidator(this.registration.build()); diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/InMemoryClientRegistrationRepositoryTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/InMemoryClientRegistrationRepositoryTests.java index f48b9210b51..0778afafecd 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/InMemoryClientRegistrationRepositoryTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/InMemoryClientRegistrationRepositoryTests.java @@ -50,12 +50,6 @@ public void constructorListClientRegistrationWhenEmptyThenIllegalArgumentExcepti new InMemoryClientRegistrationRepository(registrations); } - @Test(expected = IllegalStateException.class) - public void constructorListClientRegistrationWhenDuplicateIdThenIllegalArgumentException() { - List registrations = Arrays.asList(this.registration, this.registration); - new InMemoryClientRegistrationRepository(registrations); - } - @Test(expected = IllegalArgumentException.class) public void constructorMapClientRegistrationWhenNullThenIllegalArgumentException() { new InMemoryClientRegistrationRepository((Map) null); @@ -67,6 +61,12 @@ public void constructorMapClientRegistrationWhenEmptyMapThenRepositoryIsEmpty() assertThat(clients).isEmpty(); } + @Test(expected = IllegalStateException.class) + public void constructorListClientRegistrationWhenDuplicateIdThenIllegalArgumentException() { + List registrations = Arrays.asList(this.registration, this.registration); + new InMemoryClientRegistrationRepository(registrations); + } + @Test public void findByRegistrationIdWhenFoundThenFound() { String id = this.registration.getRegistrationId(); diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/converter/ObjectToListStringConverter.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/converter/ObjectToListStringConverter.java index 54564b0c606..daba913f25b 100644 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/converter/ObjectToListStringConverter.java +++ b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/converter/ObjectToListStringConverter.java @@ -23,9 +23,8 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; -import java.util.Objects; import java.util.Set; -import java.util.stream.Collectors; +import java.util.ArrayList; /** * @author Joe Grandja @@ -64,10 +63,13 @@ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor t } } if (source instanceof Collection) { - return ((Collection) source).stream() - .filter(Objects::nonNull) - .map(Objects::toString) - .collect(Collectors.toList()); + Collection results = new ArrayList<>(); + for (Object object : ((Collection) source)) { + if (object != null) { + results.add(object.toString()); + } + } + return results; } return Collections.singletonList(source.toString()); } diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/OAuth2AuthorizationRequest.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/OAuth2AuthorizationRequest.java index a8eb477f6b9..5c10438d1b5 100644 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/OAuth2AuthorizationRequest.java +++ b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/OAuth2AuthorizationRequest.java @@ -26,13 +26,11 @@ import java.io.Serializable; import java.nio.charset.StandardCharsets; -import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; /** * A representation of an OAuth 2.0 Authorization Request @@ -275,8 +273,7 @@ public Builder redirectUri(String redirectUri) { */ public Builder scope(String... scope) { if (scope != null && scope.length > 0) { - return this.scopes(Arrays.stream(scope).collect( - Collectors.toCollection(LinkedHashSet::new))); + return this.scopes(toLinkedHashSet(scope)); } return this; } @@ -401,5 +398,11 @@ private String buildAuthorizationRequestUri() { .build() .toUriString(); } + + private LinkedHashSet toLinkedHashSet(String... scope) { + LinkedHashSet result = new LinkedHashSet<>(); + Collections.addAll(result, scope); + return result; + } } } diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/OAuth2AccessTokenResponseHttpMessageConverter.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/OAuth2AccessTokenResponseHttpMessageConverter.java index eb731d2ca0c..1180464d6c8 100644 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/OAuth2AccessTokenResponseHttpMessageConverter.java +++ b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/OAuth2AccessTokenResponseHttpMessageConverter.java @@ -37,14 +37,13 @@ import java.nio.charset.StandardCharsets; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import java.util.HashMap; /** * A {@link HttpMessageConverter} for an {@link OAuth2AccessTokenResponse OAuth 2.0 Access Token Response}. @@ -132,12 +131,13 @@ public final void setTokenResponseParametersConverter(Converter, OAuth2AccessTokenResponse> { - private static final Set TOKEN_RESPONSE_PARAMETER_NAMES = Stream.of( + private static final Set TOKEN_RESPONSE_PARAMETER_NAMES = new HashSet<>(Arrays.asList( OAuth2ParameterNames.ACCESS_TOKEN, OAuth2ParameterNames.TOKEN_TYPE, OAuth2ParameterNames.EXPIRES_IN, OAuth2ParameterNames.REFRESH_TOKEN, - OAuth2ParameterNames.SCOPE).collect(Collectors.toSet()); + OAuth2ParameterNames.SCOPE + )); @Override public OAuth2AccessTokenResponse convert(Map tokenResponseParameters) { @@ -159,15 +159,17 @@ public OAuth2AccessTokenResponse convert(Map tokenResponseParame Set scopes = Collections.emptySet(); if (tokenResponseParameters.containsKey(OAuth2ParameterNames.SCOPE)) { String scope = tokenResponseParameters.get(OAuth2ParameterNames.SCOPE); - scopes = Arrays.stream(StringUtils.delimitedListToStringArray(scope, " ")).collect(Collectors.toSet()); + scopes = new HashSet<>(Arrays.asList(StringUtils.delimitedListToStringArray(scope, " "))); } String refreshToken = tokenResponseParameters.get(OAuth2ParameterNames.REFRESH_TOKEN); Map additionalParameters = new LinkedHashMap<>(); - tokenResponseParameters.entrySet().stream() - .filter(e -> !TOKEN_RESPONSE_PARAMETER_NAMES.contains(e.getKey())) - .forEach(e -> additionalParameters.put(e.getKey(), e.getValue())); + for (Map.Entry entry : tokenResponseParameters.entrySet()) { + if (!TOKEN_RESPONSE_PARAMETER_NAMES.contains(entry.getKey())) { + additionalParameters.put(entry.getKey(), entry.getValue()); + } + } return OAuth2AccessTokenResponse.withToken(accessToken) .tokenType(accessTokenType) @@ -205,8 +207,9 @@ public Map convert(OAuth2AccessTokenResponse tokenResponse) { parameters.put(OAuth2ParameterNames.REFRESH_TOKEN, tokenResponse.getRefreshToken().getTokenValue()); } if (!CollectionUtils.isEmpty(tokenResponse.getAdditionalParameters())) { - tokenResponse.getAdditionalParameters().entrySet().stream() - .forEach(e -> parameters.put(e.getKey(), e.getValue().toString())); + for (Map.Entry entry : tokenResponse.getAdditionalParameters().entrySet()) { + parameters.put(entry.getKey(), entry.getValue().toString()); + } } return parameters; diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/DefaultOAuth2User.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/DefaultOAuth2User.java index 68538bf6fd5..28e115b8703 100644 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/DefaultOAuth2User.java +++ b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/DefaultOAuth2User.java @@ -20,16 +20,15 @@ import org.springframework.util.Assert; import java.io.Serializable; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; -import java.util.SortedSet; +import java.util.Collections; +import java.util.Collection; +import java.util.LinkedHashMap; import java.util.TreeSet; -import java.util.stream.Collectors; +import java.util.SortedSet; +import java.util.Comparator; +import java.util.LinkedHashSet; /** * The default implementation of an {@link OAuth2User}. @@ -43,8 +42,8 @@ * and returning it from {@link #getName()}. * * @author Joe Grandja - * @since 5.0 * @see OAuth2User + * @since 5.0 */ public class DefaultOAuth2User implements OAuth2User, Serializable { private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; @@ -55,8 +54,8 @@ public class DefaultOAuth2User implements OAuth2User, Serializable { /** * Constructs a {@code DefaultOAuth2User} using the provided parameters. * - * @param authorities the authorities granted to the user - * @param attributes the attributes about the user + * @param authorities the authorities granted to the user + * @param attributes the attributes about the user * @param nameAttributeKey the key used to access the user's "name" from {@link #getAttributes()} */ public DefaultOAuth2User(Collection authorities, Map attributes, String nameAttributeKey) { @@ -88,7 +87,7 @@ public Map getAttributes() { private Set sortAuthorities(Collection authorities) { SortedSet sortedAuthorities = - new TreeSet<>(Comparator.comparing(GrantedAuthority::getAuthority)); + new TreeSet<>(Comparator.comparing(GrantedAuthority::getAuthority)); sortedAuthorities.addAll(authorities); return sortedAuthorities; } @@ -127,9 +126,9 @@ public String toString() { sb.append("Name: ["); sb.append(this.getName()); sb.append("], Granted Authorities: ["); - sb.append(this.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining(", "))); + sb.append(getAuthorities()); sb.append("], User Attributes: ["); - sb.append(this.getAttributes().entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining(", "))); + sb.append(getAttributes()); sb.append("]"); return sb.toString(); } diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/jws/MacAlgorithm.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/jws/MacAlgorithm.java index cd18ded687f..2f525b9a5c6 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/jws/MacAlgorithm.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/jws/MacAlgorithm.java @@ -15,7 +15,6 @@ */ package org.springframework.security.oauth2.jose.jws; -import java.util.stream.Stream; /** * An enumeration of the cryptographic algorithms defined by the JSON Web Algorithms (JWA) specification @@ -59,10 +58,12 @@ public enum MacAlgorithm implements JwsAlgorithm { * @return the resolved {@code MacAlgorithm}, or {@code null} if not found */ public static MacAlgorithm from(String name) { - return Stream.of(values()) - .filter(algorithm -> algorithm.getName().equals(name)) - .findFirst() - .orElse(null); + for (MacAlgorithm algorithm : values()) { + if (algorithm.getName().equals(name)) { + return algorithm; + } + } + return null; } /** diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/jws/SignatureAlgorithm.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/jws/SignatureAlgorithm.java index 980fb81366a..8ea0d884b78 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/jws/SignatureAlgorithm.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/jws/SignatureAlgorithm.java @@ -15,8 +15,6 @@ */ package org.springframework.security.oauth2.jose.jws; -import java.util.stream.Stream; - /** * An enumeration of the cryptographic algorithms defined by the JSON Web Algorithms (JWA) specification * and used by JSON Web Signature (JWS) to digitally sign the contents of the JWS Protected Header and JWS Payload. @@ -89,10 +87,12 @@ public enum SignatureAlgorithm implements JwsAlgorithm { * @return the resolved {@code SignatureAlgorithm}, or {@code null} if not found */ public static SignatureAlgorithm from(String name) { - return Stream.of(values()) - .filter(algorithm -> algorithm.getName().equals(name)) - .findFirst() - .orElse(null); + for (SignatureAlgorithm value : values()) { + if (value.getName().equals(name)) { + return value; + } + } + return null; } /** diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/MappedJwtClaimSetConverter.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/MappedJwtClaimSetConverter.java index f49494a0a3d..50a6d382dcb 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/MappedJwtClaimSetConverter.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/MappedJwtClaimSetConverter.java @@ -29,7 +29,6 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; -import java.util.stream.Collectors; /** * Converts a JWT claim set, claim by claim. Can be configured with custom converters @@ -161,17 +160,22 @@ public Map convert(Map claims) { } private Map removeClaims(Map claims) { - return claims.entrySet().stream() - .filter(e -> e.getValue() != null) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + Map result = new HashMap<>(); + for (Map.Entry entry : claims.entrySet()) { + if (entry.getValue() != null) { + result.put(entry.getKey(), entry.getValue()); + } + } + return result; } private Map addClaims(Map claims) { Map result = new HashMap<>(claims); - this.claimTypeConverters.entrySet().stream() - .filter(e -> !claims.containsKey(e.getKey())) - .filter(e -> e.getValue().convert(null) != null) - .forEach(e -> result.put(e.getKey(), e.getValue().convert(null))); + for (Map.Entry> entry : claimTypeConverters.entrySet()) { + if (!claims.containsKey(entry.getKey()) && entry.getValue().convert(null) != null) { + result.put(entry.getKey(), entry.getValue().convert(null)); + } + } return result; } } diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/NimbusOpaqueTokenIntrospector.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/NimbusOpaqueTokenIntrospector.java index c870395c2b1..c530d8bb735 100644 --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/NimbusOpaqueTokenIntrospector.java +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/NimbusOpaqueTokenIntrospector.java @@ -22,7 +22,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; +import java.util.ArrayList; import com.nimbusds.oauth2.sdk.TokenIntrospectionResponse; import com.nimbusds.oauth2.sdk.TokenIntrospectionSuccessResponse; @@ -192,9 +192,11 @@ private TokenIntrospectionSuccessResponse castToNimbusSuccess(TokenIntrospection private Map convertClaimsSet(TokenIntrospectionSuccessResponse response) { Map claims = response.toJSONObject(); if (response.getAudience() != null) { - List audience = response.getAudience().stream() - .map(Audience::getValue).collect(Collectors.toList()); - claims.put(AUDIENCE, Collections.unmodifiableList(audience)); + List audiences = new ArrayList<>(); + for (Audience audience : response.getAudience()) { + audiences.add(audience.getValue()); + } + claims.put(AUDIENCE, Collections.unmodifiableList(audiences)); } if (response.getClientID() != null) { claims.put(CLIENT_ID, response.getClientID().getValue()); diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/NimbusReactiveOpaqueTokenIntrospector.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/NimbusReactiveOpaqueTokenIntrospector.java index 5fb1646fc74..7634df66656 100644 --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/NimbusReactiveOpaqueTokenIntrospector.java +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/NimbusReactiveOpaqueTokenIntrospector.java @@ -22,7 +22,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; +import java.util.ArrayList; import com.nimbusds.oauth2.sdk.TokenIntrospectionResponse; import com.nimbusds.oauth2.sdk.TokenIntrospectionSuccessResponse; @@ -153,9 +153,11 @@ private void validate(String token, TokenIntrospectionSuccessResponse response) private Map convertClaimsSet(TokenIntrospectionSuccessResponse response) { Map claims = response.toJSONObject(); if (response.getAudience() != null) { - List audience = response.getAudience().stream() - .map(Audience::getValue).collect(Collectors.toList()); - claims.put(AUDIENCE, Collections.unmodifiableList(audience)); + List audiences = new ArrayList<>(); + for (Audience audience : response.getAudience()) { + audiences.add(audience.getValue()); + } + claims.put(AUDIENCE, Collections.unmodifiableList(audiences)); } if (response.getClientID() != null) { claims.put(CLIENT_ID, response.getClientID().getValue()); diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationEntryPoint.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationEntryPoint.java index c083201a682..526ed228594 100644 --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationEntryPoint.java +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationEntryPoint.java @@ -19,7 +19,6 @@ import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; -import java.util.stream.Collectors; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -41,10 +40,10 @@ * {@code WWW-Authenticate} HTTP header. * * @author Vedran Pavic - * @since 5.1 * @see BearerTokenError * @see RFC 6750 Section 3: The WWW-Authenticate * Response Header Field + * @since 5.1 */ public final class BearerTokenAuthenticationEntryPoint implements AuthenticationEntryPoint { @@ -54,8 +53,8 @@ public final class BearerTokenAuthenticationEntryPoint implements Authentication * Collect error details from the provided parameters and format according to * RFC 6750, specifically {@code error}, {@code error_description}, {@code error_uri}, and {@code scope}. * - * @param request that resulted in an AuthenticationException - * @param response so that the user agent can begin authentication + * @param request that resulted in an AuthenticationException + * @param response so that the user agent can begin authentication * @param authException that caused the invocation */ @Override @@ -112,13 +111,22 @@ public void setRealmName(String realmName) { } private static String computeWWWAuthenticateHeaderValue(Map parameters) { - String wwwAuthenticate = "Bearer"; + StringBuilder wwwAuthenticate = new StringBuilder(); + wwwAuthenticate.append("Bearer"); + if (!parameters.isEmpty()) { - wwwAuthenticate += parameters.entrySet().stream() - .map(attribute -> attribute.getKey() + "=\"" + attribute.getValue() + "\"") - .collect(Collectors.joining(", ", " ", "")); + wwwAuthenticate.append(" "); + int i = 0; + for (Map.Entry entry : parameters.entrySet()) { + wwwAuthenticate.append(entry.getKey()).append("=\"").append(entry.getValue()).append("\""); + + if (i != parameters.size() - 1) { + wwwAuthenticate.append(", "); + } + i++; + } } - return wwwAuthenticate; + return wwwAuthenticate.toString(); } } diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/access/BearerTokenAccessDeniedHandler.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/access/BearerTokenAccessDeniedHandler.java index c2b489218f3..c80190ec9c1 100644 --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/access/BearerTokenAccessDeniedHandler.java +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/access/BearerTokenAccessDeniedHandler.java @@ -30,12 +30,11 @@ import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; -import java.util.stream.Collectors; /** * Translates any {@link AccessDeniedException} into an HTTP response in accordance with * RFC 6750 Section 3: The WWW-Authenticate. - * + *

* So long as the class can prove that the request has a valid OAuth 2.0 {@link Authentication}, then will return an * insufficient scope error; otherwise, * it will simply indicate the scheme (Bearer) and any configured realm. @@ -51,10 +50,9 @@ public final class BearerTokenAccessDeniedHandler implements AccessDeniedHandler * Collect error details from the provided parameters and format according to * RFC 6750, specifically {@code error}, {@code error_description}, {@code error_uri}, and {@code scope}. * - * @param request that resulted in an AccessDeniedException - * @param response so that the user agent can be advised of the failure + * @param request that resulted in an AccessDeniedException + * @param response so that the user agent can be advised of the failure * @param accessDeniedException that caused the invocation - * */ @Override public void handle( @@ -90,13 +88,22 @@ public void setRealmName(String realmName) { } private static String computeWWWAuthenticateHeaderValue(Map parameters) { - String wwwAuthenticate = "Bearer"; + StringBuilder wwwAuthenticate = new StringBuilder(); + wwwAuthenticate.append("Bearer"); + if (!parameters.isEmpty()) { - wwwAuthenticate += parameters.entrySet().stream() - .map(attribute -> attribute.getKey() + "=\"" + attribute.getValue() + "\"") - .collect(Collectors.joining(", ", " ", "")); + wwwAuthenticate.append(" "); + int i = 0; + for (Map.Entry entry : parameters.entrySet()) { + wwwAuthenticate.append(entry.getKey()).append("=\"").append(entry.getValue()).append("\""); + + if (i != parameters.size() - 1) { + wwwAuthenticate.append(", "); + } + i++; + } } - return wwwAuthenticate; + return wwwAuthenticate.toString(); } } diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/access/server/BearerTokenServerAccessDeniedHandler.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/access/server/BearerTokenServerAccessDeniedHandler.java index a96a9a311a0..c4e74650c4b 100644 --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/access/server/BearerTokenServerAccessDeniedHandler.java +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/access/server/BearerTokenServerAccessDeniedHandler.java @@ -30,7 +30,6 @@ import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; -import java.util.stream.Collectors; /** * Translates any {@link AccessDeniedException} into an HTTP response in accordance with @@ -91,13 +90,20 @@ private static Mono respond(ServerWebExchange exchange, Map parameters) { - String wwwAuthenticate = "Bearer"; + StringBuilder wwwAuthenticate = new StringBuilder(); + wwwAuthenticate.append("Bearer"); if (!parameters.isEmpty()) { - wwwAuthenticate += parameters.entrySet().stream() - .map(attribute -> attribute.getKey() + "=\"" + attribute.getValue() + "\"") - .collect(Collectors.joining(", ", " ", "")); + wwwAuthenticate.append(" "); + int i = 0; + for (Map.Entry entry : parameters.entrySet()) { + wwwAuthenticate.append(entry.getKey()).append("=\"").append(entry.getValue()).append("\""); + if (i != parameters.size() - 1) { + wwwAuthenticate.append(", "); + } + i++; + } } - return wwwAuthenticate; + return wwwAuthenticate.toString(); } } diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/BearerTokenServerAuthenticationEntryPoint.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/BearerTokenServerAuthenticationEntryPoint.java index ab768beb75d..3a050f30cb4 100644 --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/BearerTokenServerAuthenticationEntryPoint.java +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/BearerTokenServerAuthenticationEntryPoint.java @@ -32,7 +32,6 @@ import java.util.LinkedHashMap; import java.util.Map; -import java.util.stream.Collectors; /** * An {@link AuthenticationEntryPoint} implementation used to commence authentication of protected resource requests @@ -42,10 +41,10 @@ * {@code WWW-Authenticate} HTTP header. * * @author Rob Winch - * @since 5.1 * @see BearerTokenError * @see RFC 6750 Section 3: The WWW-Authenticate * Response Header Field + * @since 5.1 */ public final class BearerTokenServerAuthenticationEntryPoint implements ServerAuthenticationEntryPoint { @@ -111,13 +110,21 @@ private HttpStatus getStatus(AuthenticationException authException) { } private static String computeWWWAuthenticateHeaderValue(Map parameters) { - String wwwAuthenticate = "Bearer"; + StringBuilder wwwAuthenticate = new StringBuilder(); + wwwAuthenticate.append("Bearer"); + if (!parameters.isEmpty()) { - wwwAuthenticate += parameters.entrySet().stream() - .map(attribute -> attribute.getKey() + "=\"" + attribute.getValue() + "\"") - .collect(Collectors.joining(", ", " ", "")); + wwwAuthenticate.append(" "); + int i = 0; + for (Map.Entry entry : parameters.entrySet()) { + wwwAuthenticate.append(entry.getKey()).append("=\"").append(entry.getValue()).append("\""); + if (i != parameters.size() - 1) { + wwwAuthenticate.append(", "); + } + i++; + } } - return wwwAuthenticate; + return wwwAuthenticate.toString(); } } diff --git a/web/src/main/java/org/springframework/security/web/header/writers/ClearSiteDataHeaderWriter.java b/web/src/main/java/org/springframework/security/web/header/writers/ClearSiteDataHeaderWriter.java index d8683eb2fe0..7669e67aaba 100644 --- a/web/src/main/java/org/springframework/security/web/header/writers/ClearSiteDataHeaderWriter.java +++ b/web/src/main/java/org/springframework/security/web/header/writers/ClearSiteDataHeaderWriter.java @@ -16,9 +16,6 @@ package org.springframework.security.web.header.writers; -import java.util.stream.Collectors; -import java.util.stream.Stream; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -69,7 +66,7 @@ public final class ClearSiteDataHeaderWriter implements HeaderWriter { public ClearSiteDataHeaderWriter(String ...sources) { Assert.notEmpty(sources, "sources cannot be empty or null"); this.requestMatcher = new SecureRequestMatcher(); - this.headerValue = Stream.of(sources).map(this::quote).collect(Collectors.joining(", ")); + this.headerValue = joinQuotes(sources); } @Override @@ -84,6 +81,15 @@ public void writeHeaders(HttpServletRequest request, HttpServletResponse respons } } + private String joinQuotes(String ...sources) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < sources.length-1; i++) { + sb.append(quote(sources[i])).append(", "); + } + sb.append(quote(sources[sources.length-1])); + return sb.toString(); + } + private static final class SecureRequestMatcher implements RequestMatcher { public boolean matches(HttpServletRequest request) { return request.isSecure(); diff --git a/web/src/main/java/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationSuccessHandler.java b/web/src/main/java/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationSuccessHandler.java index 2fafd24becf..6506526e7ca 100644 --- a/web/src/main/java/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationSuccessHandler.java +++ b/web/src/main/java/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationSuccessHandler.java @@ -23,8 +23,7 @@ import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import java.util.ArrayList; /** * Delegates to a collection of {@link ServerAuthenticationSuccessHandler} implementations. @@ -43,7 +42,10 @@ public DelegatingServerAuthenticationSuccessHandler(ServerAuthenticationSuccessH @Override public Mono onAuthenticationSuccess(WebFilterExchange exchange, Authentication authentication) { - Stream> results = this.delegates.stream().map(delegate -> delegate.onAuthenticationSuccess(exchange, authentication)); - return Mono.when(results.collect(Collectors.toList())); + List> results = new ArrayList<>(); + for (ServerAuthenticationSuccessHandler delegate : delegates) { + results.add(delegate.onAuthenticationSuccess(exchange, authentication)); + } + return Mono.when(results); } } diff --git a/web/src/main/java/org/springframework/security/web/server/authentication/logout/DelegatingServerLogoutHandler.java b/web/src/main/java/org/springframework/security/web/server/authentication/logout/DelegatingServerLogoutHandler.java index 197f3565357..3a6a6fc4003 100644 --- a/web/src/main/java/org/springframework/security/web/server/authentication/logout/DelegatingServerLogoutHandler.java +++ b/web/src/main/java/org/springframework/security/web/server/authentication/logout/DelegatingServerLogoutHandler.java @@ -20,8 +20,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; import reactor.core.publisher.Mono; @@ -50,10 +48,12 @@ public DelegatingServerLogoutHandler(Collection delegates) @Override public Mono logout(WebFilterExchange exchange, Authentication authentication) { - return Mono.when(this.delegates.stream() - .filter(Objects::nonNull) - .map(delegate -> delegate.logout(exchange, authentication)) - .collect(Collectors.toList()) - ); + List> results = new ArrayList<>(); + for (ServerLogoutHandler delegate : delegates) { + if (delegate != null) { + results.add(delegate.logout(exchange, authentication)); + } + } + return Mono.when(results); } } diff --git a/web/src/main/java/org/springframework/security/web/server/header/ClearSiteDataServerHttpHeadersWriter.java b/web/src/main/java/org/springframework/security/web/server/header/ClearSiteDataServerHttpHeadersWriter.java index c3aed7df0d6..f0195ac4c69 100644 --- a/web/src/main/java/org/springframework/security/web/server/header/ClearSiteDataServerHttpHeadersWriter.java +++ b/web/src/main/java/org/springframework/security/web/server/header/ClearSiteDataServerHttpHeadersWriter.java @@ -20,9 +20,6 @@ import reactor.core.publisher.Mono; -import java.util.stream.Collectors; -import java.util.stream.Stream; - /** *

Writes the {@code Clear-Site-Data} response header when the request is secure.

* @@ -81,9 +78,12 @@ public String getHeaderValue() { } private String transformToHeaderValue(Directive... directives) { - return Stream.of(directives) - .map(Directive::getHeaderValue) - .collect(Collectors.joining(", ")); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < directives.length - 1; i++) { + sb.append(directives[i].headerValue).append(", "); + } + sb.append(directives[directives.length - 1].headerValue); + return sb.toString(); } private boolean isSecure(ServerWebExchange exchange) { diff --git a/web/src/main/java/org/springframework/security/web/server/header/CompositeServerHttpHeadersWriter.java b/web/src/main/java/org/springframework/security/web/server/header/CompositeServerHttpHeadersWriter.java index bb28c4b2c8c..0674d2225d8 100644 --- a/web/src/main/java/org/springframework/security/web/server/header/CompositeServerHttpHeadersWriter.java +++ b/web/src/main/java/org/springframework/security/web/server/header/CompositeServerHttpHeadersWriter.java @@ -17,8 +17,7 @@ import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import java.util.ArrayList; import org.springframework.web.server.ServerWebExchange; @@ -43,8 +42,10 @@ public CompositeServerHttpHeadersWriter(List writers) { @Override public Mono writeHttpHeaders(ServerWebExchange exchange) { - Stream> results = writers.stream().map( writer -> writer.writeHttpHeaders(exchange)); - return Mono.when(results.collect(Collectors.toList())); + List> results = new ArrayList<>(); + for (ServerHttpHeadersWriter writer : writers) { + results.add(writer.writeHttpHeaders(exchange)); + } + return Mono.when(results); } - }