Skip to content

Commit b6afe66

Browse files
edeandrearwinch
authored andcommitted
Add ServerAuthenticationConverter interface
- Adding an ServerAuthenticationConverter interface - Retro-fitting ServerOAuth2LoginAuthenticationTokenConverter, ServerBearerTokenAuthentivationConverter, ServerFormLoginAuthenticationConverter, and ServerHttpBasicAuthenticationConverter to implement ServerAuthenticationConverter - Deprecate existing AuthenticationWebFilter.setAuthenticationConverter and add overloaded one which takes ServerAuthenticationConverter Fixes gh-5338
1 parent 34c8d66 commit b6afe66

File tree

11 files changed

+88
-44
lines changed

11 files changed

+88
-44
lines changed

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/ServerOAuth2LoginAuthenticationTokenConverter.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
package org.springframework.security.oauth2.client.web;
1818

19-
import java.util.function.Function;
20-
2119
import org.springframework.security.core.Authentication;
2220
import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;
2321
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
@@ -27,23 +25,22 @@
2725
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
2826
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;
2927
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
28+
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
3029
import org.springframework.util.Assert;
3130
import org.springframework.util.MultiValueMap;
3231
import org.springframework.web.server.ServerWebExchange;
3332
import org.springframework.web.util.UriComponentsBuilder;
3433

3534
import reactor.core.publisher.Mono;
3635

37-
3836
/**
3937
* Converts from a {@link ServerWebExchange} to an {@link OAuth2LoginAuthenticationToken} that can be authenticated. The
4038
* converter does not validate any errors it only performs a conversion.
4139
* @author Rob Winch
4240
* @since 5.1
43-
* @see org.springframework.security.web.server.authentication.AuthenticationWebFilter#setAuthenticationConverter(Function)
41+
* @see org.springframework.security.web.server.authentication.AuthenticationWebFilter#setAuthenticationConverter(ServerAuthenticationConverter)
4442
*/
45-
public class ServerOAuth2LoginAuthenticationTokenConverter implements
46-
Function<ServerWebExchange, Mono<Authentication>> {
43+
public class ServerOAuth2LoginAuthenticationTokenConverter implements ServerAuthenticationConverter {
4744

4845
static final String AUTHORIZATION_REQUEST_NOT_FOUND_ERROR_CODE = "authorization_request_not_found";
4946

@@ -72,7 +69,7 @@ public void setAuthorizationRequestRepository(
7269
}
7370

7471
@Override
75-
public Mono<Authentication> apply(ServerWebExchange serverWebExchange) {
72+
public Mono<Authentication> convert(ServerWebExchange serverWebExchange) {
7673
return this.authorizationRequestRepository.removeAuthorizationRequest(serverWebExchange)
7774
.switchIfEmpty(oauth2AuthenticationException(AUTHORIZATION_REQUEST_NOT_FOUND_ERROR_CODE))
7875
.flatMap(authorizationRequest -> authenticationRequest(serverWebExchange, authorizationRequest));
@@ -97,14 +94,14 @@ private Mono<OAuth2LoginAuthenticationToken> authenticationRequest(ServerWebExch
9794
})
9895
.switchIfEmpty(oauth2AuthenticationException(CLIENT_REGISTRATION_NOT_FOUND_ERROR_CODE))
9996
.map(clientRegistration -> {
100-
OAuth2AuthorizationResponse authorizationResponse = convert(exchange);
97+
OAuth2AuthorizationResponse authorizationResponse = convertResponse(exchange);
10198
OAuth2LoginAuthenticationToken authenticationRequest = new OAuth2LoginAuthenticationToken(
10299
clientRegistration, new OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse));
103100
return authenticationRequest;
104101
});
105102
}
106103

107-
private static OAuth2AuthorizationResponse convert(ServerWebExchange exchange) {
104+
private static OAuth2AuthorizationResponse convertResponse(ServerWebExchange exchange) {
108105
MultiValueMap<String, String> queryParams = exchange.getRequest()
109106
.getQueryParams();
110107
String redirectUri = UriComponentsBuilder.fromUri(exchange.getRequest().getURI())

oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/ServerOAuth2LoginAuthenticationTokenConverterTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,6 @@ public void applyWhenCodeParameterFoundThenCode() {
141141

142142
private OAuth2LoginAuthenticationToken applyConverter() {
143143
MockServerWebExchange exchange = MockServerWebExchange.from(this.request);
144-
return (OAuth2LoginAuthenticationToken) this.converter.apply(exchange).block();
144+
return (OAuth2LoginAuthenticationToken) this.converter.convert(exchange).block();
145145
}
146146
}

oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/ServerBearerTokenAuthenticationConverter.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@
2525
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
2626
import org.springframework.security.oauth2.server.resource.BearerTokenError;
2727
import org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes;
28+
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
2829
import org.springframework.util.StringUtils;
2930
import org.springframework.web.server.ServerWebExchange;
3031
import reactor.core.publisher.Mono;
3132

32-
import java.util.function.Function;
3333
import java.util.regex.Matcher;
3434
import java.util.regex.Pattern;
3535

@@ -41,13 +41,12 @@
4141
* @since 5.1
4242
* @see <a href="https://tools.ietf.org/html/rfc6750#section-2" target="_blank">RFC 6750 Section 2: Authenticated Requests</a>
4343
*/
44-
public class ServerBearerTokenAuthenticationConverter implements
45-
Function<ServerWebExchange, Mono<Authentication>> {
44+
public class ServerBearerTokenAuthenticationConverter implements ServerAuthenticationConverter {
4645
private static final Pattern authorizationPattern = Pattern.compile("^Bearer (?<token>[a-zA-Z0-9-._~+/]+)=*$");
4746

4847
private boolean allowUriQueryParameter = false;
4948

50-
public Mono<Authentication> apply(ServerWebExchange exchange) {
49+
public Mono<Authentication> convert(ServerWebExchange exchange) {
5150
return Mono.justOrEmpty(this.token(exchange.getRequest()))
5251
.map(BearerTokenAuthenticationToken::new);
5352
}

oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/server/ServerBearerTokenAuthenticationConverterTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,6 @@ private BearerTokenAuthenticationToken convertToToken(MockServerHttpRequest.Base
129129

130130
private BearerTokenAuthenticationToken convertToToken(MockServerHttpRequest request) {
131131
MockServerWebExchange exchange = MockServerWebExchange.from(request);
132-
return this.converter.apply(exchange).cast(BearerTokenAuthenticationToken.class).block();
132+
return this.converter.convert(exchange).cast(BearerTokenAuthenticationToken.class).block();
133133
}
134134
}

web/src/main/java/org/springframework/security/web/server/ServerFormLoginAuthenticationConverter.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@
1515
*/
1616
package org.springframework.security.web.server;
1717

18-
import java.util.function.Function;
19-
18+
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
2019
import org.springframework.util.Assert;
2120
import reactor.core.publisher.Mono;
2221

@@ -32,14 +31,14 @@
3231
* @author Rob Winch
3332
* @since 5.0
3433
*/
35-
public class ServerFormLoginAuthenticationConverter implements Function<ServerWebExchange, Mono<Authentication>> {
34+
public class ServerFormLoginAuthenticationConverter implements ServerAuthenticationConverter {
3635

3736
private String usernameParameter = "username";
3837

3938
private String passwordParameter = "password";
4039

4140
@Override
42-
public Mono<Authentication> apply(ServerWebExchange exchange) {
41+
public Mono<Authentication> convert(ServerWebExchange exchange) {
4342
return exchange.getFormData()
4443
.map( data -> createAuthentication(data));
4544
}

web/src/main/java/org/springframework/security/web/server/ServerHttpBasicAuthenticationConverter.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@
1616
package org.springframework.security.web.server;
1717

1818
import java.util.Base64;
19-
import java.util.function.Function;
2019

2120
import org.springframework.http.HttpHeaders;
2221
import org.springframework.http.server.reactive.ServerHttpRequest;
2322
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
2423
import org.springframework.security.core.Authentication;
24+
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
2525
import org.springframework.web.server.ServerWebExchange;
2626

2727
import reactor.core.publisher.Mono;
@@ -32,12 +32,12 @@
3232
* @author Rob Winch
3333
* @since 5.0
3434
*/
35-
public class ServerHttpBasicAuthenticationConverter implements Function<ServerWebExchange, Mono<Authentication>> {
35+
public class ServerHttpBasicAuthenticationConverter implements ServerAuthenticationConverter {
3636

3737
public static final String BASIC = "Basic ";
3838

3939
@Override
40-
public Mono<Authentication> apply(ServerWebExchange exchange) {
40+
public Mono<Authentication> convert(ServerWebExchange exchange) {
4141
ServerHttpRequest request = exchange.getRequest();
4242

4343
String authorization = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);

web/src/main/java/org/springframework/security/web/server/authentication/AuthenticationWebFilter.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public class AuthenticationWebFilter implements WebFilter {
6767

6868
private ServerAuthenticationSuccessHandler authenticationSuccessHandler = new WebFilterChainServerAuthenticationSuccessHandler();
6969

70-
private Function<ServerWebExchange, Mono<Authentication>> authenticationConverter = new ServerHttpBasicAuthenticationConverter();
70+
private ServerAuthenticationConverter authenticationConverter = new ServerHttpBasicAuthenticationConverter();
7171

7272
private ServerAuthenticationFailureHandler authenticationFailureHandler = new ServerAuthenticationEntryPointFailureHandler(new HttpBasicServerAuthenticationEntryPoint());
7373

@@ -88,7 +88,7 @@ public AuthenticationWebFilter(ReactiveAuthenticationManager authenticationManag
8888
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
8989
return this.requiresAuthenticationMatcher.matches(exchange)
9090
.filter( matchResult -> matchResult.isMatch())
91-
.flatMap( matchResult -> this.authenticationConverter.apply(exchange))
91+
.flatMap( matchResult -> this.authenticationConverter.convert(exchange))
9292
.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))
9393
.flatMap( token -> authenticate(exchange, chain, token));
9494
}
@@ -138,8 +138,24 @@ public void setAuthenticationSuccessHandler(ServerAuthenticationSuccessHandler a
138138
* that no authentication attempt should be made. The default converter is
139139
* {@link ServerHttpBasicAuthenticationConverter}
140140
* @param authenticationConverter the converter to use
141+
* @deprecated As of 5.1 in favor of {@link #setAuthenticationConverter(ServerAuthenticationConverter)}
142+
* @see #setAuthenticationConverter(ServerAuthenticationConverter)
141143
*/
144+
@Deprecated
142145
public void setAuthenticationConverter(Function<ServerWebExchange, Mono<Authentication>> authenticationConverter) {
146+
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
147+
setAuthenticationConverter((ServerAuthenticationConverter) authenticationConverter);
148+
}
149+
150+
/**
151+
* Sets the strategy used for converting from a {@link ServerWebExchange} to an {@link Authentication} used for
152+
* authenticating with the provided {@link ReactiveAuthenticationManager}. If the result is empty, then it signals
153+
* that no authentication attempt should be made. The default converter is
154+
* {@link ServerHttpBasicAuthenticationConverter}
155+
* @param authenticationConverter the converter to use
156+
* @since 5.1
157+
*/
158+
public void setAuthenticationConverter(ServerAuthenticationConverter authenticationConverter) {
143159
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
144160
this.authenticationConverter = authenticationConverter;
145161
}
@@ -156,7 +172,7 @@ public void setAuthenticationFailureHandler(
156172

157173
/**
158174
* Sets the matcher used to determine when creating an {@link Authentication} from
159-
* {@link #setAuthenticationConverter(Function)} to be authentication. If the converter returns an empty
175+
* {@link #setAuthenticationConverter(ServerAuthenticationConverter)} to be authentication. If the converter returns an empty
160176
* result, then no authentication is attempted. The default is any request
161177
* @param requiresAuthenticationMatcher the matcher to use. Cannot be null.
162178
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2002-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.web.server.authentication;
18+
19+
import org.springframework.security.core.Authentication;
20+
import org.springframework.web.server.ServerWebExchange;
21+
22+
import reactor.core.publisher.Mono;
23+
24+
/**
25+
* A strategy used for converting from a {@link ServerWebExchange} to an {@link Authentication} used for
26+
* authenticating with a provided {@link org.springframework.security.authentication.ReactiveAuthenticationManager}.
27+
* If the result is {@link Mono#empty()}, then it signals that no authentication attempt should be made.
28+
*
29+
* @author Eric Deandrea
30+
* @since 5.1
31+
*/
32+
@FunctionalInterface
33+
public interface ServerAuthenticationConverter {
34+
/**
35+
* Converts a {@link ServerWebExchange} to an {@link Authentication}
36+
* @param exchange The {@link ServerWebExchange}
37+
* @return A {@link Mono} representing an {@link Authentication}
38+
*/
39+
Mono<Authentication> convert(ServerWebExchange exchange);
40+
}

web/src/test/java/org/springframework/security/web/server/ServerFormLoginAuthenticationConverterTests.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public void applyWhenUsernameAndPasswordThenCreatesTokenSuccess() {
5555
this.data.add("username", username);
5656
this.data.add("password", password);
5757

58-
Authentication authentication = this.converter.apply(this.exchange).block();
58+
Authentication authentication = this.converter.convert(this.exchange).block();
5959

6060
assertThat(authentication.getName()).isEqualTo(username);
6161
assertThat(authentication.getCredentials()).isEqualTo(password);
@@ -73,7 +73,7 @@ public void applyWhenCustomParametersAndUsernameAndPasswordThenCreatesTokenSucce
7373
this.data.add(usernameParameter, username);
7474
this.data.add(passwordParameter, password);
7575

76-
Authentication authentication = this.converter.apply(this.exchange).block();
76+
Authentication authentication = this.converter.convert(this.exchange).block();
7777

7878
assertThat(authentication.getName()).isEqualTo(username);
7979
assertThat(authentication.getCredentials()).isEqualTo(password);
@@ -82,7 +82,7 @@ public void applyWhenCustomParametersAndUsernameAndPasswordThenCreatesTokenSucce
8282

8383
@Test
8484
public void applyWhenNoDataThenCreatesTokenSuccess() {
85-
Authentication authentication = this.converter.apply(this.exchange).block();
85+
Authentication authentication = this.converter.convert(this.exchange).block();
8686

8787
assertThat(authentication.getName()).isNullOrEmpty();
8888
assertThat(authentication.getCredentials()).isNull();

web/src/test/java/org/springframework/security/web/server/ServerHttpBasicAuthenticationConverterTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,6 @@ public void applyWhenWrongSchemeThenEmpty() {
9696
}
9797

9898
private Mono<Authentication> apply(MockServerHttpRequest.BaseBuilder<?> request) {
99-
return this.converter.apply(MockServerWebExchange.from(this.request.build()));
99+
return this.converter.convert(MockServerWebExchange.from(this.request.build()));
100100
}
101101
}

web/src/test/java/org/springframework/security/web/server/authentication/AuthenticationWebFilterTests.java

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
package org.springframework.security.web.server.authentication;
1818

19-
import java.util.function.Function;
20-
2119
import org.junit.Before;
2220
import org.junit.Test;
2321
import org.junit.runner.RunWith;
@@ -34,16 +32,11 @@
3432
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
3533
import org.springframework.test.web.reactive.server.EntityExchangeResult;
3634
import org.springframework.test.web.reactive.server.WebTestClient;
37-
import org.springframework.web.server.ServerWebExchange;
3835

3936
import static org.assertj.core.api.Assertions.assertThat;
4037
import static org.mockito.Matchers.any;
4138
import static org.mockito.Matchers.eq;
42-
import static org.mockito.Mockito.never;
43-
import static org.mockito.Mockito.verify;
44-
import static org.mockito.Mockito.verifyZeroInteractions;
45-
import static org.mockito.Mockito.when;
46-
39+
import static org.mockito.Mockito.*;
4740

4841
/**
4942
* @author Rob Winch
@@ -54,7 +47,7 @@ public class AuthenticationWebFilterTests {
5447
@Mock
5548
private ServerAuthenticationSuccessHandler successHandler;
5649
@Mock
57-
private Function<ServerWebExchange, Mono<Authentication>> authenticationConverter;
50+
private ServerAuthenticationConverter authenticationConverter;
5851
@Mock
5952
private ReactiveAuthenticationManager authenticationManager;
6053
@Mock
@@ -136,7 +129,7 @@ public void filterWhenDefaultsAndAuthenticationFailThenUnauthorized() {
136129

137130
@Test
138131
public void filterWhenConvertEmptyThenOk() {
139-
when(this.authenticationConverter.apply(any())).thenReturn(Mono.empty());
132+
when(this.authenticationConverter.convert(any())).thenReturn(Mono.empty());
140133

141134
WebTestClient client = WebTestClientBuilder
142135
.bindToWebFilters(this.filter)
@@ -157,7 +150,7 @@ public void filterWhenConvertEmptyThenOk() {
157150

158151
@Test
159152
public void filterWhenConvertErrorThenServerError() {
160-
when(this.authenticationConverter.apply(any())).thenReturn(Mono.error(new RuntimeException("Unexpected")));
153+
when(this.authenticationConverter.convert(any())).thenReturn(Mono.error(new RuntimeException("Unexpected")));
161154

162155
WebTestClient client = WebTestClientBuilder
163156
.bindToWebFilters(this.filter)
@@ -178,7 +171,7 @@ public void filterWhenConvertErrorThenServerError() {
178171
@Test
179172
public void filterWhenConvertAndAuthenticationSuccessThenSuccess() {
180173
Mono<Authentication> authentication = Mono.just(new TestingAuthenticationToken("test", "this", "ROLE_USER"));
181-
when(this.authenticationConverter.apply(any())).thenReturn(authentication);
174+
when(this.authenticationConverter.convert(any())).thenReturn(authentication);
182175
when(this.authenticationManager.authenticate(any())).thenReturn(authentication);
183176
when(this.successHandler.onAuthenticationSuccess(any(), any())).thenReturn(Mono.empty());
184177
when(this.securityContextRepository.save(any(), any())).thenAnswer( a -> Mono.just(a.getArguments()[0]));
@@ -203,7 +196,7 @@ public void filterWhenConvertAndAuthenticationSuccessThenSuccess() {
203196
@Test
204197
public void filterWhenConvertAndAuthenticationEmptyThenServerError() {
205198
Mono<Authentication> authentication = Mono.just(new TestingAuthenticationToken("test", "this", "ROLE_USER"));
206-
when(this.authenticationConverter.apply(any())).thenReturn(authentication);
199+
when(this.authenticationConverter.convert(any())).thenReturn(authentication);
207200
when(this.authenticationManager.authenticate(any())).thenReturn(Mono.empty());
208201

209202
WebTestClient client = WebTestClientBuilder
@@ -245,7 +238,7 @@ public void filterWhenNotMatchAndConvertAndAuthenticationSuccessThenContinues()
245238
@Test
246239
public void filterWhenConvertAndAuthenticationFailThenEntryPoint() {
247240
Mono<Authentication> authentication = Mono.just(new TestingAuthenticationToken("test", "this", "ROLE_USER"));
248-
when(this.authenticationConverter.apply(any())).thenReturn(authentication);
241+
when(this.authenticationConverter.convert(any())).thenReturn(authentication);
249242
when(this.authenticationManager.authenticate(any())).thenReturn(Mono.error(new BadCredentialsException("Failed")));
250243
when(this.failureHandler.onAuthenticationFailure(any(), any())).thenReturn(Mono.empty());
251244

@@ -268,7 +261,7 @@ public void filterWhenConvertAndAuthenticationFailThenEntryPoint() {
268261
@Test
269262
public void filterWhenConvertAndAuthenticationExceptionThenServerError() {
270263
Mono<Authentication> authentication = Mono.just(new TestingAuthenticationToken("test", "this", "ROLE_USER"));
271-
when(this.authenticationConverter.apply(any())).thenReturn(authentication);
264+
when(this.authenticationConverter.convert(any())).thenReturn(authentication);
272265
when(this.authenticationManager.authenticate(any())).thenReturn(Mono.error(new RuntimeException("Failed")));
273266

274267
WebTestClient client = WebTestClientBuilder

0 commit comments

Comments
 (0)