You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 10:14:56