Procházet zdrojové kódy

Use Oauth2UserService bean in OidcReactiveOAuth2UserService

Closes gh-15846
DingHao před 11 měsíci
rodič
revize
ef1226ddf8

+ 3 - 1
config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java

@@ -4586,7 +4586,9 @@ public class ServerHttpSecurity {
 			if (bean != null) {
 				return bean;
 			}
-			return new OidcReactiveOAuth2UserService();
+			OidcReactiveOAuth2UserService reactiveOAuth2UserService = new OidcReactiveOAuth2UserService();
+			reactiveOAuth2UserService.setOauth2UserService(getOauth2UserService());
+			return reactiveOAuth2UserService;
 		}
 
 		private ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> getOauth2UserService() {

+ 83 - 0
config/src/test/java/org/springframework/security/config/web/server/OAuth2LoginTests.java

@@ -64,6 +64,8 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio
 import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;
 import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
 import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
+import org.springframework.security.oauth2.client.userinfo.DefaultReactiveOAuth2UserService;
+import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
 import org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;
 import org.springframework.security.oauth2.client.web.server.DefaultServerOAuth2AuthorizationRequestResolver;
 import org.springframework.security.oauth2.client.web.server.ServerAuthorizationRequestRepository;
@@ -84,6 +86,7 @@ import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;
 import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
 import org.springframework.security.oauth2.core.oidc.user.OidcUser;
 import org.springframework.security.oauth2.core.oidc.user.TestOidcUsers;
+import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
 import org.springframework.security.oauth2.core.user.OAuth2User;
 import org.springframework.security.oauth2.core.user.TestOAuth2Users;
 import org.springframework.security.oauth2.jwt.Jwt;
@@ -664,6 +667,41 @@ public class OAuth2LoginTests {
 			.block()).isEmpty();
 	}
 
+	@Test
+	public void oauth2LoginWhenOauth2UserServiceBeanPresent() {
+		this.spring.register(OAuth2LoginWithMultipleClientRegistrations.class, OAuth2LoginWithOauth2UserService.class)
+			.autowire();
+		WebTestClient webTestClient = WebTestClientBuilder.bindToWebFilters(this.springSecurity).build();
+		OAuth2LoginWithOauth2UserService config = this.spring.getContext()
+			.getBean(OAuth2LoginWithOauth2UserService.class);
+		OAuth2AuthorizationRequest request = TestOAuth2AuthorizationRequests.request().scope("openid").build();
+		OAuth2AuthorizationResponse response = TestOAuth2AuthorizationResponses.success().build();
+		OAuth2AuthorizationExchange exchange = new OAuth2AuthorizationExchange(request, response);
+		OAuth2AccessToken accessToken = TestOAuth2AccessTokens.scopes("openid");
+		OAuth2AuthorizationCodeAuthenticationToken token = new OAuth2AuthorizationCodeAuthenticationToken(google,
+				exchange, accessToken);
+		ServerAuthenticationConverter converter = config.authenticationConverter;
+		given(converter.convert(any())).willReturn(Mono.just(token));
+		ServerSecurityContextRepository securityContextRepository = config.securityContextRepository;
+		given(securityContextRepository.save(any(), any())).willReturn(Mono.empty());
+		given(securityContextRepository.load(any())).willReturn(authentication(token));
+		Map<String, Object> additionalParameters = new HashMap<>();
+		additionalParameters.put(OidcParameterNames.ID_TOKEN, "id-token");
+		OAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue())
+			.tokenType(accessToken.getTokenType())
+			.scopes(accessToken.getScopes())
+			.additionalParameters(additionalParameters)
+			.build();
+		ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> tokenResponseClient = config.tokenResponseClient;
+		given(tokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));
+		ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> userService = config.reactiveOAuth2UserService;
+		given(userService.loadUser(any())).willReturn(Mono
+			.just(new DefaultOAuth2User(AuthorityUtils.createAuthorityList("USER"), Map.of("sub", "subject"), "sub")));
+		webTestClient.get().uri("/login/oauth2/code/google").exchange().expectStatus().is3xxRedirection();
+		verify(userService).loadUser(any());
+
+	}
+
 	Mono<SecurityContext> authentication(Authentication authentication) {
 		SecurityContext context = new SecurityContextImpl();
 		context.setAuthentication(authentication);
@@ -674,6 +712,51 @@ public class OAuth2LoginTests {
 		return this.spring.getContext().getBean(beanClass);
 	}
 
+	@Configuration
+	static class OAuth2LoginWithOauth2UserService {
+
+		ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> tokenResponseClient = mock(
+				ReactiveOAuth2AccessTokenResponseClient.class);
+
+		ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> reactiveOAuth2UserService = mock(
+				DefaultReactiveOAuth2UserService.class);
+
+		ServerAuthenticationConverter authenticationConverter = mock(ServerAuthenticationConverter.class);
+
+		ServerSecurityContextRepository securityContextRepository = mock(ServerSecurityContextRepository.class);
+
+		@Bean
+		SecurityWebFilterChain springSecurity(ServerHttpSecurity http) {
+			http.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())
+				.oauth2Login((c) -> c.authenticationConverter(this.authenticationConverter)
+					.securityContextRepository(this.securityContextRepository));
+			return http.build();
+		}
+
+		@Bean
+		ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> customOAuth2UserService() {
+			return this.reactiveOAuth2UserService;
+		}
+
+		@Bean
+		ReactiveJwtDecoderFactory<ClientRegistration> jwtDecoderFactory() {
+			return (clientRegistration) -> (token) -> {
+				Map<String, Object> claims = new HashMap<>();
+				claims.put(IdTokenClaimNames.SUB, "subject");
+				claims.put(IdTokenClaimNames.ISS, "http://localhost/issuer");
+				claims.put(IdTokenClaimNames.AUD, Collections.singletonList("client"));
+				claims.put(IdTokenClaimNames.AZP, "client");
+				return Mono.just(TestJwts.jwt().claims((c) -> c.putAll(claims)).build());
+			};
+		}
+
+		@Bean
+		ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> requestReactiveOAuth2AccessTokenResponseClient() {
+			return this.tokenResponseClient;
+		}
+
+	}
+
 	@Configuration
 	@EnableWebFluxSecurity
 	static class OAuth2LoginWithMultipleClientRegistrations {