Spring Boot3 WebFlux多OAuth2 IdP认证:IdP2令牌请求自定义配置
问题描述
我们有一个基于Spring Boot 3、Spring Security和Spring WebFlux的应用,已完成IdP1的用户认证授权功能,现在需要扩展支持部分用户通过IdP2完成认证授权。已配置基础OAuth2客户端信息如下:
spring: security: oauth2: client: registration: idp1: client-id: idp1client client-secret: idp1secret scope: openid, profile, email authorization-grant-type: authorization_code redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}' idp2: client-id: idp2client client-secret: idp2secret scope: openid, profile, email authorization-grant-type: authorization_code redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}' provider: idp1: authorization-uri: http://idp1/auth token-uri: http://idp1/token idp2: authorization-uri: http://idp2/auth token-uri: http://idp2/token
目前IdP1的认证流程正常,但IdP2需要在授权码生成后,通过自定义令牌请求(比如发起额外API调用获取字段)来生成访问令牌,需要明确需要自定义哪些Spring Bean。
解决方案
针对WebFlux环境,需要自定义以下Spring Bean来实现IdP2的自定义令牌请求逻辑:
1. 自定义ReactiveOAuth2AccessTokenResponseClient
这个Bean是核心,负责处理授权码模式下的令牌请求流程。我们可以继承默认的DefaultReactiveOAuth2AccessTokenResponseClient,重写getTokenResponse方法,在默认令牌请求前后插入自定义逻辑(比如额外API调用)。
示例代码:
import org.springframework.security.oauth2.client.endpoint.DefaultReactiveOAuth2AccessTokenResponseClient; import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest; import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; import reactor.core.publisher.Mono; public class CustomIdP2AccessTokenResponseClient extends DefaultReactiveOAuth2AccessTokenResponseClient { @Override public Mono<OAuth2AccessTokenResponse> getTokenResponse(OAuth2AuthorizationCodeGrantRequest grantRequest) { // 先执行默认的令牌请求逻辑,获取基础令牌响应 return super.getTokenResponse(grantRequest) .flatMap(tokenResponse -> { // 此处添加自定义逻辑:调用IdP2的额外API获取字段 String accessToken = tokenResponse.getAccessToken().getTokenValue(); return callIdP2ExtraApi(accessToken) .map(extraFields -> { // 可将额外字段存入tokenResponse的额外参数,或后续在用户信息处理中使用 // 示例:直接返回原令牌响应,额外字段后续处理 return tokenResponse; }); }); } // 模拟调用IdP2额外API的方法,实际需用WebClient实现HTTP请求 private Mono<String> callIdP2ExtraApi(String accessToken) { return Mono.just("extra_field_value"); } }
然后在配置类中注册该Bean:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient; import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest; @Configuration public class OAuth2ClientConfig { @Bean public ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> idp2AccessTokenResponseClient() { return new CustomIdP2AccessTokenResponseClient(); } }
2. 自定义ReactiveOAuth2UserService(可选)
如果需要将额外API获取的字段整合到用户认证信息中(比如存入OAuth2User),可以自定义这个Bean,扩展用户信息加载逻辑。
示例代码:
import org.springframework.security.oauth2.client.userinfo.DefaultReactiveOAuth2UserService; import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; import org.springframework.security.oauth2.core.user.OAuth2User; import reactor.core.publisher.Mono; public class CustomIdP2OAuth2UserService extends DefaultReactiveOAuth2UserService { @Override public Mono<OAuth2User> loadUser(OAuth2UserRequest userRequest) { return super.loadUser(userRequest) .flatMap(oAuth2User -> { // 调用额外API获取用户扩展信息 String accessToken = userRequest.getAccessToken().getTokenValue(); return callIdP2ExtraApi(accessToken) .map(extraInfo -> new CustomOAuth2User(oAuth2User.getAttributes(), extraInfo)); }); } private Mono<String> callIdP2ExtraApi(String accessToken) { return Mono.just("extra_user_info"); } } // 自定义OAuth2User,包含原用户信息和额外字段 class CustomOAuth2User implements OAuth2User { private final java.util.Map<String, Object> attributes; private final String extraInfo; public CustomOAuth2User(java.util.Map<String, Object> attributes, String extraInfo) { this.attributes = attributes; this.extraInfo = extraInfo; } @Override public java.util.Map<String, Object> getAttributes() { return attributes; } @Override public java.util.Collection<? extends org.springframework.security.core.GrantedAuthority> getAuthorities() { return java.util.Collections.emptyList(); } @Override public String getName() { return attributes.get("name").toString(); } public String getExtraInfo() { return extraInfo; } }
在配置类中注册该Bean:
@Bean public ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> idp2OAuth2UserService() { return new CustomIdP2OAuth2UserService(); }
3. 自定义ReactiveOAuth2AuthorizationRequestResolver(可选)
如果IdP2的授权请求需要添加自定义参数(比如额外的请求参数或scope),可以自定义这个解析器,针对IdP2的授权请求做定制化处理。
示例代码:
import org.springframework.context.annotation.Bean; import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository; import org.springframework.security.oauth2.client.web.DefaultReactiveOAuth2AuthorizationRequestResolver; import org.springframework.security.oauth2.client.web.ReactiveOAuth2AuthorizationRequestResolver; @Configuration public class OAuth2ClientConfig { @Bean public ReactiveOAuth2AuthorizationRequestResolver authorizationRequestResolver(ReactiveClientRegistrationRepository clientRegistrationRepository) { DefaultReactiveOAuth2AuthorizationRequestResolver resolver = new DefaultReactiveOAuth2AuthorizationRequestResolver(clientRegistrationRepository); resolver.setAuthorizationRequestCustomizer(customizer -> { if ("idp2".equals(customizer.getClientRegistration().getRegistrationId())) { // 为IdP2的授权请求添加自定义参数 customizer.additionalParameters(params -> params.put("custom_param", "custom_value")); } }); return resolver; } }
内容的提问来源于stack exchange,提问作者ducky8592
相关产品推荐
相关产品推荐

