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
相关产品推荐
相关产品推荐

