From f6c650db47233cadf8a72f6bc3ac667c1b50e92f Mon Sep 17 00:00:00 2001 From: kostya05983 Date: Sun, 4 Aug 2019 17:11:24 +0700 Subject: [PATCH] Replace Streams with Loops 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 Fixes gh-7154 --- .../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 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.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 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); } - }