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

Spring Webflux应用JWT验证后获取Userinfo端点信息的最优方案

问题:Spring Webflux应用验证JWT后调用第三方Userinfo端点做校验的最优方式

我有一个暴露Webflux端点的Spring应用,使用JWT令牌对POST请求进行授权,但还需要获取Userinfo端点提供的信息。当前已配置SecurityWebFilterChain Bean并采用oauth2ResourceServer配置,需调用Userinfo端点做进一步校验。授权服务器为第三方服务,请问验证JWT令牌后获取Userinfo端点信息做后续校验的最优方式是什么?

附未调用Userinfo的安全配置代码:

@Bean
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {

  http
    .cors()
    .and()
          .httpBasic().disable()
          .formLogin().disable()
          .csrf().disable()
          .logout().disable()
          .oauth2Client()
          .and()
    .authorizeExchange()
          .pathMatchers(HttpMethod.POST).authenticated()
          .anyExchange().permitAll()
          .and().oauth2ResourceServer().jwt()
          ;

  return http.build();
}
解决方案

在Spring Webflux的OAuth2资源服务器场景下,验证JWT后调用第三方Userinfo端点做校验的最优方案是自定义ReactiveAuthenticationManager,在原有JWT验证逻辑之后,追加Userinfo端点的调用与校验逻辑,同时将获取到的用户信息整合到认证对象中供后续使用。

步骤1:配置Userinfo端点信息

在application.yml中配置第三方授权服务器的Userinfo端点地址:

spring:
  security:
    oauth2:
      resourceserver:
        userinfo-uri: https://第三方授权服务器地址/userinfo

步骤2:自定义ReactiveAuthenticationManager

实现组合式认证管理器,先验证JWT有效性,再调用Userinfo端点执行校验:

@Component
public class CustomReactiveAuthenticationManager implements ReactiveAuthenticationManager {

    private final JwtReactiveAuthenticationManager jwtAuthManager;
    private final WebClient webClient;
    private final String userinfoUri;

    public CustomReactiveAuthenticationManager(JwtDecoder jwtDecoder,
                                               WebClient.Builder webClientBuilder,
                                               @Value("${spring.security.oauth2.resourceserver.userinfo-uri}") String userinfoUri) {
        this.jwtAuthManager = new JwtReactiveAuthenticationManager(jwtDecoder);
        this.webClient = webClientBuilder.build();
        this.userinfoUri = userinfoUri;
    }

    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        // 第一步:验证JWT合法性
        return jwtAuthManager.authenticate(authentication)
                .flatMap(jwtAuth -> {
                    // 从JWT中提取令牌
                    String token = ((Jwt) jwtAuth.getPrincipal()).getTokenValue();
                    // 第二步:调用Userinfo端点获取用户信息
                    return webClient.get()
                            .uri(userinfoUri)
                            .header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
                            .retrieve()
                            .bodyToMono(UserInfoDTO.class)
                            .map(userInfo -> {
                                // 第三步:执行自定义校验逻辑
                                validateUserStatus(userInfo);
                                // 将Userinfo信息整合到认证对象,供后续业务使用
                                List<GrantedAuthority> authorities = new ArrayList<>(jwtAuth.getAuthorities());
                                // 可添加Userinfo返回的角色/权限到authorities
                                return new UsernamePasswordAuthenticationToken(
                                        userInfo,
                                        jwtAuth.getCredentials(),
                                        authorities
                                );
                            })
                            .onErrorMap(e -> new AuthenticationServiceException("Userinfo校验失败", e));
                });
    }

    // 自定义校验规则,比如检查用户是否启用
    private void validateUserStatus(UserInfoDTO userInfo) {
        if (!userInfo.isEnabled()) {
            throw new AuthenticationServiceException("用户已被禁用");
        }
    }

    // 自定义Userinfo响应DTO
    public static class UserInfoDTO {
        private String username;
        private boolean enabled;
        private List<String> roles;
        // 省略getter、setter
    }
}

步骤3:修改Security配置,替换认证管理器

将自定义的认证管理器配置到资源服务器中:

@Bean
public SecurityWebFilterChain filterChain(ServerHttpSecurity http,
                                          CustomReactiveAuthenticationManager customAuthManager) {

    http
        .cors()
        .and()
            .httpBasic().disable()
            .formLogin().disable()
            .csrf().disable()
            .logout().disable()
            .oauth2Client()
            .and()
        .authorizeExchange()
            .pathMatchers(HttpMethod.POST).authenticated()
            .anyExchange().permitAll()
            .and()
            .oauth2ResourceServer()
            .authenticationManager(customAuthManager); // 使用自定义认证逻辑

    return http.build();
}

补充优化建议

  • 缓存优化:如果Userinfo调用频率高,可添加Redis缓存,缓存令牌对应的用户信息,设置与令牌过期时间匹配的缓存时长,减少第三方接口调用次数。
  • 异常兜底:针对Userinfo端点的网络超时、返回错误码等情况,添加兜底处理逻辑,避免因第三方服务不可用导致认证失败。

内容的提问来源于stack exchange,提问作者Enthus

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 00:30:37