关于我应该通过access_token还是id_token访问我的Spring Boot资源服务器,我有点困惑。
首先,让我快速解释一下我的设置:
我可以轻松地配置Postman以从Google获取令牌。来自 Google 的此令牌响应包括access_token
、id_token
、范围
、expries_in
和token_type
的值。
但是,当 Postman 尝试将检索到的令牌的 access_token
字段中的值用作授权标头中的持有者时,我对资源服务器的请求被拒绝
我能够成功访问受保护@Controllers
的唯一方法是使用 id_token
作为授权标头中的持有者。
我是否应该使用id_token
作为授权标头中的承载?还是我应该使用access_token
?
一些其他相关信息:
id_token
的值是JWT令牌。access_token
的值不是JWT令牌。我知道这一点,因为我可以解码jwt.io上的id_token
,但它无法解码access_token
的值。此外,当我将access_token
作为Authorization标头中的承载发送时,Spring Boot Resource Server出现以下故障:尝试解码 JWT 时出错:无效的不安全/JWS/JWE 标头:无效的 JSON:位置 2 处的意外令牌 ɭ。
您不应该使用身份令牌来授权对API的访问。要访问API,您应该使用OAuth的访问令牌,这些令牌仅适用于受保护的资源(API),并内置了范围。
access_token
(用于测试目的)的值确实是一个有效的 JWT。与从谷歌返回的access_token
相反,它不是 JWT。总而言之:
id_token
值访问我的Spring Boot资源服务器。access_token
的值不是JWT,Spring Boot无法解析access_token
的工作方式上是否有所不同如果需要,很乐意澄清或添加更多信息。感谢您的考虑和耐心等待!
在我看来,你提到的博客文章是正确的,我相信OpenIDConnect1.0规范并不打算将id_token
用于访问目的。
和您一样,我也希望将Google用作授权服务器,因为Spring Security与Google一起作为提供社交登录的常用OAuth2提供者。然而,事实并非如此,我相信这并不是真的有意为之,因为谷歌并不是你真正的授权服务器。例如,我不相信您可以将Google配置为使用域特定应用程序的范围/权限/权限。这与Okta不同,Okta有许多选项可用于在自己的租户中配置内容。
我实际上建议检查Spring Authorization Server,并将Google配置为联合身份提供者。我目前正在为此制作一个示例,它将在下周左右发布(请参阅此分支)。
话虽如此,如果您仍然对使用Google访问令牌对资源服务器进行身份验证的简单用例感兴趣,您需要提供自己的不透明令牌内省器,该内省器使用Google的令牌信息endpoint。它与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