Skip to content

Commit c1988b8

Browse files
authored
Authorize with anonymous principal if principal name is null (#1050)
The authorizedClientManager.authorize method requires a non-null principal name or it will usually throw an exception in practice like "principalName cannot be empty". Using the anonymous principal in this case like for a null principal handles the situation more gracefully. Fixes #1049
1 parent e9777e5 commit c1988b8

File tree

2 files changed

+65
-9
lines changed

2 files changed

+65
-9
lines changed

spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptor.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2015-2022 the original author or authors.
2+
* Copyright 2015-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -48,6 +48,7 @@
4848
*
4949
* @author Dangzhicairang(小水牛)
5050
* @author Olga Maciaszek-Sharma
51+
* @author Philipp Meier
5152
* @since 4.0.0
5253
*/
5354
public class OAuth2AccessTokenInterceptor implements RequestInterceptor {
@@ -122,7 +123,7 @@ protected OAuth2AccessToken getToken(String clientRegistrationId) {
122123
}
123124

124125
Authentication principal = SecurityContextHolder.getContext().getAuthentication();
125-
if (principal == null) {
126+
if (principal == null || principal.getName() == null) {
126127
principal = ANONYMOUS_AUTHENTICATION;
127128
}
128129

spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptorTests.java

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2015-2022 the original author or authors.
2+
* Copyright 2015-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,7 +23,10 @@
2323
import feign.Target;
2424
import org.junit.jupiter.api.BeforeEach;
2525
import org.junit.jupiter.api.Test;
26+
import org.mockito.ArgumentMatcher;
2627

28+
import org.springframework.security.authentication.TestingAuthenticationToken;
29+
import org.springframework.security.core.context.SecurityContextHolder;
2730
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
2831
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
2932
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
@@ -43,6 +46,7 @@
4346
*
4447
* @author Dangzhicairang(小水牛)
4548
* @author Olga Maciaszek-Sharma
49+
* @author Philipp Meier
4650
*
4751
*/
4852
class OAuth2AccessTokenInterceptorTests {
@@ -78,7 +82,7 @@ void shouldThrowExceptionWhenNoTokenAcquired() {
7882
void shouldAcquireValidToken() {
7983
oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientManager);
8084
when(mockOAuth2AuthorizedClientManager
81-
.authorize(argThat((OAuth2AuthorizeRequest request) -> ("test").equals(request.getClientRegistrationId()))))
85+
.authorize(argThat(matchAuthorizeRequest("test"))))
8286
.thenReturn(validTokenOAuth2AuthorizedClient());
8387

8488
oAuth2AccessTokenInterceptor.apply(requestTemplate);
@@ -89,7 +93,7 @@ void shouldAcquireValidToken() {
8993
@Test
9094
void shouldAcquireValidTokenFromServiceId() {
9195
when(mockOAuth2AuthorizedClientManager
92-
.authorize(argThat((OAuth2AuthorizeRequest request) -> ("test").equals(request.getClientRegistrationId()))))
96+
.authorize(argThat(matchAuthorizeRequest("test"))))
9397
.thenReturn(validTokenOAuth2AuthorizedClient());
9498
oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientManager);
9599

@@ -99,26 +103,68 @@ void shouldAcquireValidTokenFromServiceId() {
99103
}
100104

101105
@Test
102-
void shouldAcquireValidTokenFromSpecifiedClientRegistrationId() {
106+
void shouldAcquireValidTokenFromSpecifiedClientRegistrationIdPrincipalIsNull() {
107+
SecurityContextHolder.getContext().setAuthentication(null);
103108
oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(DEFAULT_CLIENT_REGISTRATION_ID,
104109
mockOAuth2AuthorizedClientManager);
105110
when(mockOAuth2AuthorizedClientManager
106-
.authorize(argThat((OAuth2AuthorizeRequest request) -> (DEFAULT_CLIENT_REGISTRATION_ID)
107-
.equals(request.getClientRegistrationId()))))
111+
.authorize(argThat(matchAuthorizeRequest(DEFAULT_CLIENT_REGISTRATION_ID))))
108112
.thenReturn(validTokenOAuth2AuthorizedClient());
109113

110114
oAuth2AccessTokenInterceptor.apply(requestTemplate);
111115

112116
assertThat(requestTemplate.headers().get("Authorization")).contains("Bearer Valid Token");
113117
}
114118

119+
@Test
120+
void shouldAcquireValidTokenFromSpecifiedClientRegistrationIdPrincipalNameIsNull() {
121+
SecurityContextHolder.getContext().setAuthentication(principalWithName(null));
122+
oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(DEFAULT_CLIENT_REGISTRATION_ID,
123+
mockOAuth2AuthorizedClientManager);
124+
when(mockOAuth2AuthorizedClientManager
125+
.authorize(argThat(matchAuthorizeRequest(DEFAULT_CLIENT_REGISTRATION_ID))))
126+
.thenReturn(validTokenOAuth2AuthorizedClient());
127+
128+
oAuth2AccessTokenInterceptor.apply(requestTemplate);
129+
130+
assertThat(requestTemplate.headers().get("Authorization")).contains("Bearer Valid Token");
131+
}
132+
133+
@Test
134+
void shouldAcquireValidTokenFromSpecifiedClientRegistrationIdPrincipalNameIsNotNull() {
135+
String principalName = "principalName";
136+
SecurityContextHolder.getContext().setAuthentication(principalWithName(principalName));
137+
oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(DEFAULT_CLIENT_REGISTRATION_ID,
138+
mockOAuth2AuthorizedClientManager);
139+
when(mockOAuth2AuthorizedClientManager
140+
.authorize(argThat(matchAuthorizeRequestWithPrincipalName(DEFAULT_CLIENT_REGISTRATION_ID, principalName))))
141+
.thenReturn(validTokenOAuth2AuthorizedClienWithPrincipalName(principalName));
142+
143+
oAuth2AccessTokenInterceptor.apply(requestTemplate);
144+
145+
assertThat(requestTemplate.headers().get("Authorization")).contains("Bearer Valid Token");
146+
}
147+
148+
private ArgumentMatcher<OAuth2AuthorizeRequest> matchAuthorizeRequest(String clientRegistrationId) {
149+
return matchAuthorizeRequestWithPrincipalName(clientRegistrationId, "anonymousUser");
150+
}
151+
152+
private ArgumentMatcher<OAuth2AuthorizeRequest> matchAuthorizeRequestWithPrincipalName(String clientRegistrationId, String principalName) {
153+
return (OAuth2AuthorizeRequest request) -> clientRegistrationId.equals(request.getClientRegistrationId())
154+
&& principalName.equals(request.getPrincipal().getName());
155+
}
156+
115157
private OAuth2AccessToken validToken() {
116158
return new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "Valid Token", Instant.now(),
117159
Instant.now().plusSeconds(60L));
118160
}
119161

120162
private OAuth2AuthorizedClient validTokenOAuth2AuthorizedClient() {
121-
return new OAuth2AuthorizedClient(defaultClientRegistration(), "anonymousUser", validToken());
163+
return validTokenOAuth2AuthorizedClienWithPrincipalName("anonymousUser");
164+
}
165+
166+
private OAuth2AuthorizedClient validTokenOAuth2AuthorizedClienWithPrincipalName(String principalName) {
167+
return new OAuth2AuthorizedClient(defaultClientRegistration(), principalName, validToken());
122168
}
123169

124170
private ClientRegistration defaultClientRegistration() {
@@ -129,4 +175,13 @@ private ClientRegistration defaultClientRegistration() {
129175
.build();
130176
}
131177

178+
private TestingAuthenticationToken principalWithName(String principalName) {
179+
return new TestingAuthenticationToken(new java.security.Principal() {
180+
@Override
181+
public String getName() {
182+
return principalName;
183+
}
184+
}, null);
185+
}
186+
132187
}

0 commit comments

Comments
 (0)