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 60c4ec3873..dc5ec528b3 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.factory.PasswordEncoderFactories; 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 @@ public class AuthenticationConfiguration { } 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 @@ public class AuthenticationConfiguration { 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 6189ae08e7..5ab05c960c 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 reactor.core.publisher.Mono; 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 class AuthorityReactiveAuthorizationManager implements ReactiveAuthori 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 d499a8d19e..c0aeb2a53d 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.RSAPrivateKey; 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 class RsaKeyConverters { 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 class RsaKeyConverters { 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 c05f22a827..f0d8c12e56 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 @@ package org.springframework.security.core.userdetails; 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 class MapReactiveUserDetailsService implements ReactiveUserDetailsService */ 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 e8b33ef3bc..97154ac416 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 class MapReactiveUserDetailsServiceTests { 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 0343b96071..d57bcf8153 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.ArrayList; 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 final class DelegatingOAuth2AuthorizedClientProvider implements OAuth2Aut @Nullable public OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) { Assert.notNull(context, "context cannot be null"); - return this.authorizedClientProviders.stream() - .map(authorizedClientProvider -> 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 fd67555e94..7c5345ad5f 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 org.springframework.util.Assert; 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 final class OAuth2AuthorizedClientProviderBuilder { * @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 4cd9e3f90d..806c837112 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.time.Instant; 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 final class OidcIdTokenValidator implements OAuth2TokenValidator { } 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 456c68be7c..7378d5fe4f 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.Collections; 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 final class InMemoryClientRegistrationRepository implements ClientRegistr 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 c3b50a8d23..1cddacc26c 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.Iterator; 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 final class InMemoryReactiveClientRegistrationRepository */ 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 final class InMemoryReactiveClientRegistrationRepository 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 5ef5f7d25f..bad2f80969 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 class OidcIdTokenValidatorTests { .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 f48b9210b5..0778afafec 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 class InMemoryClientRegistrationRepositoryTests { 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 class InMemoryClientRegistrationRepositoryTests { 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 54564b0c60..daba913f25 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.Collection; 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 @@ final class ObjectToListStringConverter implements ConditionalGenericConverter { } } 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 a8eb477f6b..5c10438d1b 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 org.springframework.web.util.UriComponentsBuilder; 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 final class OAuth2AuthorizationRequest implements Serializable { */ 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 @@ public final class OAuth2AuthorizationRequest implements Serializable { .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 eb731d2ca0..1180464d6c 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.Charset; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.time.temporal.ChronoUnit; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; +import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.HashMap; /** * A {@link HttpMessageConverter} for an {@link OAuth2AccessTokenResponse OAuth 2.0 Access Token Response}. @@ -132,12 +131,13 @@ public class OAuth2AccessTokenResponseHttpMessageConverter extends AbstractHttpM * OAuth 2.0 Access Token Response parameters to an {@link OAuth2AccessTokenResponse}. */ private static class OAuth2AccessTokenResponseConverter implements 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 class OAuth2AccessTokenResponseHttpMessageConverter extends AbstractHttpM 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 class OAuth2AccessTokenResponseHttpMessageConverter extends AbstractHttpM 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 68538bf6fd..28e115b870 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.security.core.SpringSecurityCoreVersion; 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 @@ import java.util.stream.Collectors; * 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 class DefaultOAuth2User implements OAuth2User, Serializable { 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 class DefaultOAuth2User implements OAuth2User, Serializable { 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 cd18ded687..2f525b9a5c 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 980fb81366..8ea0d884b7 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 f49494a0a3..50a6d382dc 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.time.Instant; 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 final class MappedJwtClaimSetConverter implements Converter 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 c870395c2b..c530d8bb73 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.time.Instant; 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 @@ public class NimbusOpaqueTokenIntrospector implements OpaqueTokenIntrospector { 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 5fb1646fc7..7634df6665 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.time.Instant; 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 @@ public class NimbusReactiveOpaqueTokenIntrospector implements ReactiveOpaqueToke 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 c083201a68..526ed22859 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 @@ package org.springframework.security.oauth2.server.resource.web; 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 @@ import org.springframework.util.StringUtils; * {@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 final class BearerTokenAuthenticationEntryPoint implements Authentication } 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 c2b489218f..c80190ec9c 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 javax.servlet.http.HttpServletResponse; 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 final class BearerTokenAccessDeniedHandler implements AccessDeniedHandler } 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 a96a9a311a..c4e74650c4 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.Arrays; 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 @@ public class BearerTokenServerAccessDeniedHandler implements ServerAccessDeniedH } 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/server/BearerTokenServerAuthenticationEntryPoint.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/BearerTokenServerAuthenticationEntryPoint.java index ab768beb75..3a050f30cb 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 reactor.core.publisher.Mono; 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 @@ import java.util.stream.Collectors; * {@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 @@ public final class BearerTokenServerAuthenticationEntryPoint implements } 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 d8683eb2fe..7669e67aab 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 final class ClearSiteDataHeaderWriter implements HeaderWriter { } } + 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 2fafd24bec..6506526e7c 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 reactor.core.publisher.Mono; 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 class DelegatingServerAuthenticationSuccessHandler implements ServerAuthe @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 197f356535..3a6a6fc400 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.ArrayList; 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 class DelegatingServerLogoutHandler implements ServerLogoutHandler { @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 c3aed7df0d..f0195ac4c6 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 org.springframework.web.server.ServerWebExchange; 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 final class ClearSiteDataServerHttpHeadersWriter implements ServerHttpHea } 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 bb28c4b2c8..0674d2225d 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 @@ package org.springframework.security.web.server.header; 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 class CompositeServerHttpHeadersWriter implements ServerHttpHeadersWriter @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); } - }