我对是否应该通过 access_token 还是 id_token 访问我的 Spring Boot 资源服务器有点困惑。
首先,让我快速解释一下我的设置:
我可以轻松配置 Postman 以从 Google 获取令牌。来自 Google 的此令牌响应包括
access_token
、id_token
、scope
、expries_in
和 token_type
的值。
但是,当 Postman 尝试使用检索到的令牌的
access_token
字段中的值作为授权标头中的 Bearer 时,我对资源服务器的请求被拒绝
我能够成功访问受保护的
@Controllers
的唯一方法是使用 id_token
作为授权标头中的承载者。
我是否应该使用
id_token
作为授权标头中的承载者?或者我应该使用access_token
?
一些其他相关信息:
id_token
的值是一个JWT令牌。 access_token
的值不是 JWT 令牌。我知道这一点是因为我可以解码 jwt.io上的
id_token
,但它无法解码 access_token
的值。此外,当我发送 access_token
作为 Authorization 标头中的 Bearer 时,Spring Boot 资源服务器失败,并出现以下错误:尝试解码 Jwt 时发生错误:无效的不安全/JWS/JWE 标头:无效的 JSON:位置 2 处出现意外的令牌 ɭ�。
您不应使用身份令牌来授权对 API 的访问。 要访问 API,您应该使用 OAuth 的访问令牌,该令牌仅适用于受保护的资源 (API),并且内置范围界定。
access_token
(用于测试目的)的值确实是有效的 JWT。与从 Google 返回的 access_token
相反,它不是 JWT。总结:
id_token
的值来访问我的 Spring Boot 资源服务器。 access_token
的值不是JWT,无法被Spring Boot解析。access_token
的工作方式方面是否有不同的行为?如果需要,很乐意澄清或添加更多信息。感谢您的考虑和耐心!
您提到的博客文章在我看来是正确的,并且我相信 OpenID Connect 1.0 规范并不打算将
id_token
用于访问目的。
和您一样,我预计使用 Google 作为授权服务器可以开箱即用,因为 Spring Security 与 Google 一起作为通用 OAuth2 提供者来提供社交登录。然而,事实并非如此,而且我相信这并不是真正的意图,因为 Google 并不是真正的您的授权服务器。例如,我不相信您可以将 Google 配置为使用特定于域的应用程序的范围/权限/权限。这与 Okta 之类的东西不同,Okta 中有很多用于在您自己的租户中配置事物的选项。
我实际上建议查看Spring授权服务器,并将Google配置为联合身份提供商。我目前正在为此制作一个示例,它将在下周左右发布(更新:现在包含在官方示例中)。
话虽如此,如果您仍然对使用 Google 访问令牌对资源服务器进行身份验证的简单用例感兴趣,则需要提供自己的 不透明令牌内省器,它使用 Google 的 tokeninfo 端点。与 Spring Security 期望的不符,所以有点牵扯。
@EnableWebSecurity
public class SecurityConfiguration {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests((authorizeRequests) -> authorizeRequests
.anyRequest().authenticated()
)
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::opaqueToken);
// @formatter:on
return http.build();
}
@Bean
public OpaqueTokenIntrospector introspector() {
return new GoogleTokenIntrospector("https://oauth2.googleapis.com/tokeninfo");
}
}
public final class GoogleTokenIntrospector implements OpaqueTokenIntrospector {
private final RestTemplate restTemplate = new RestTemplate();
private final String introspectionUri;
public GoogleTokenIntrospector(String introspectionUri) {
this.introspectionUri = introspectionUri;
}
@Override
public OAuth2AuthenticatedPrincipal introspect(String token) {
RequestEntity<?> requestEntity = buildRequest(token);
try {
ResponseEntity<Map<String, Object>> responseEntity = this.restTemplate.exchange(requestEntity, new ParameterizedTypeReference<>() {});
// TODO: Create and return OAuth2IntrospectionAuthenticatedPrincipal based on response...
} catch (Exception ex) {
throw new BadOpaqueTokenException(ex.getMessage(), ex);
}
}
private RequestEntity<?> buildRequest(String token) {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("access_token", token);
return new RequestEntity<>(body, headers, HttpMethod.POST, URI.create(introspectionUri));
}
}
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://accounts.google.com
jwk-set-uri: https://www.googleapis.com/oauth2/v3/certs