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

Spring OAuth资源服务器中如何通过accessToken获取IdToken

如何在Spring Boot资源服务器中从Access Token获取对应的Id Token?

我的Spring Boot应用作为单页应用(SPA)的后端服务,认证流程在SPA端通过Okta的OAuth PKCE协议完成,之后SPA将Access Token发送到后端。后端已经能通过请求头接收Access Token并完成认证,当前的Spring Security配置如下:

return http.httpBasic((AbstractHttpConfigurer::disable))
           .formLogin((AbstractHttpConfigurer::disable))
           .csrf(AbstractHttpConfigurer::disable)
           .logout((AbstractHttpConfigurer::disable))
           .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
           .authorizeHttpRequests(it -> it.anyRequest().authenticated())
           .cors(Customizer.withDefaults())
           .oauth2ResourceServer(it -> it.jwt(Customizer.withDefaults()))
           .build();

现在需要在Spring应用中获取该Access Token对应的Id Token,当执行以下代码时,该如何实现?

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// 此处authentication是JwtAuthenticationToken实例

我知道可以通过Access Token调用Userinfo端点实现,但想了解Spring Boot中是否有其他可行方法。


解决方案

首先明确:Id Token不会自动随Access Token传递到后端。在OAuth2 PKCE流程中,Id Token是直接返回给SPA客户端的,后端作为资源服务器仅负责验证Access Token的有效性,本身无法直接从Access Token中提取Id Token(两者是独立生成的令牌,无直接关联的存储或映射关系)。

以下是几种可行的处理方式:

1. 让SPA主动传递Id Token

这是最直接的方案:

  • 修改SPA代码,在请求后端接口时,除了在Authorization头中携带Access Token,额外添加一个自定义请求头(比如X-Id-Token)来传递Id Token。
  • 后端通过请求头获取Id Token,并使用Spring Security的JwtDecoder验证解析:
    @Autowired
    private JwtDecoder jwtDecoder;
    
    public Jwt getIdTokenFromRequest(HttpServletRequest request) {
        String idTokenStr = request.getHeader("X-Id-Token");
        if (idTokenStr != null) {
            // 自动验证签名、issuer、过期时间等
            return jwtDecoder.decode(idTokenStr);
        }
        return null;
    }
    
  • 注意:必须验证Id Token的有效性,避免恶意伪造的令牌被后端接受。

2. 通过Userinfo端点获取用户信息(替代Id Token内容)

如果你的核心需求是获取Id Token中携带的用户信息(比如用户ID、邮箱、姓名等),而非Id Token本身,那么调用Userinfo端点是标准的OAuth2资源服务器做法,Spring Security可以自动处理:

  • 调整Spring Security配置,开启Userinfo端点自动调用:
    .oauth2ResourceServer(oauth2 -> oauth2
        .jwt(jwt -> jwt.jwtAuthenticationConverter(jwt -> {
            // 自定义权限转换(可选)
            JwtAuthenticationToken token = new JwtAuthenticationToken(jwt, Collections.emptyList());
            return token;
        }))
        .userInfoEndpoint(userInfo -> userInfo
            .userAuthoritiesMapper(authorities -> authorities)
        )
    )
    
  • 之后在代码中可以直接从JwtAuthenticationToken的claims中获取用户信息,这些信息和Id Token中的内容一致:
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    if (authentication instanceof JwtAuthenticationToken jwtToken) {
        String userId = jwtToken.getToken().getClaim("sub");
        String email = jwtToken.getToken().getClaim("email");
        // 其他用户信息
    }
    

3. 使用Okta令牌自省端点验证(无法直接获取Id Token)

Okta提供令牌自省端点,可以用Access Token查询其有效性和元数据,但这个端点不会返回Id Token,仅能确认令牌状态和关联的用户标识:

  • 手动调用Okta的自省端点(需要配置Okta的客户端ID和密钥):
    @Autowired
    private RestTemplate restTemplate;
    
    public Map<String, Object> introspectAccessToken(String accessToken) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("token", accessToken);
        body.add("client_id", "你的Okta客户端ID");
        body.add("client_secret", "你的Okta客户端密钥");
        body.add("token_type_hint", "access_token");
    
        ResponseEntity<Map> response = restTemplate.postForEntity(
            "https://你的Okta域名/oauth2/v1/introspect",
            new HttpEntity<>(body, headers),
            Map.class
        );
        return response.getBody();
    }
    
  • 该方法主要用于验证令牌有效性,无法获取Id Token本身。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 09:06:04