Spring Boot Sping Boot Authorization Server + Google OAuth2/OpenId Connect应该使用access_token还是id_token?

kcugc4gi  于 2023-04-11  发布在  Spring
关注(0)|答案(1)|浏览(173)

我对是否应该通过access_token或id_token访问Sping Boot 资源服务器有点困惑。
首先,让我快速解释一下我的设置:

  • Sping Boot 应用程序作为OAuth 2.0 Resource Server。这是按照Spring文档中的描述配置的:JWT的最小配置此应用程序提供安全的@Controllers,将为JavaScript SPA(例如React)提供数据
  • Google的OAuth 2.0 AP / OpenID Connect已配置(凭据、客户端ID、客户端密码)
  • 一个JavaScript SPA应用程序(例如React),它将用户登录到Google并向Sping Boot 资源服务器请求安全数据。这些请求包括登录用户的Authorization头(从Google获得的Bearer令牌)。
  • 出于开发目的,我还使用Postman向Sping Boot 资源服务器发出请求

我可以轻松地配置Postman从Google获取令牌。Google的令牌响应包括access_tokenid_tokenscopeexpries_intoken_type的值。
但是,当Postman尝试使用检索到的令牌的access_token字段中的值作为Authorization头中的Bearer时,我对资源服务器的请求被拒绝
我能够成功访问受保护的@Controllers的唯一方法是使用id_token作为Authorization头中的Bearer。
我应该使用id_token作为授权头中的承载吗?还是应该使用access_token
一些额外的相关信息:

  • id_token的值是一个JWT令牌。access_token的值不是一个JWT令牌。我知道这一点,因为我可以在jwt.io上解码id_token,但它无法解码access_token的值。此外,当我在Authorization头中发送access_token作为Bearer时,Sping Boot 资源服务器失败,如下所示:

尝试解码Jwt时出错:无效的不安全/JWS/JWE标头:无效的JSON:位置% 2处的意外标记?。

您不应该使用身份令牌来授权对API的访问。要访问API,您应该使用OAuth的访问令牌,该令牌仅用于受保护的资源(API),并且内置了范围。

  • 查看使用OAuth2资源服务器的spring-security-samples,我看到硬编码的access_token(用于测试目的)的值确实是一个有效的JWT。与从Google返回的不是JWT的access_token相反。

总结如下:

  • 我可以使用从Google获得的id_token的值访问我的Sping Boot 资源服务器。access_token的值不是JWT,无法通过Spring Boot解析。
  • 我的理解有问题吗?我的配置有什么问题吗?Google的OpenId Connect在access_token的工作方式上是否有不同的表现?

如果需要的话,很高兴澄清或添加更多的信息。感谢您的考虑和耐心!

zxlwwiss

zxlwwiss1#

在我看来,你提到的博客文章是正确的,我相信OpenID Connect 1.0规范并不打算将id_token用于访问目的。
像你一样,我希望使用Google作为授权服务器可以开箱即用,因为Spring Security与Google一起作为一个常见的OAuth2提供商提供社交登录。然而,事实并非如此,我相信这不是真正的意图,因为Google不是真正的授权服务器。例如,我不相信你可以配置谷歌与您的域特定的应用程序的范围/权限/权限工作.这是不同于像Okta的东西,那里有很多选项来配置你自己的租户的东西.
实际上,我建议检查Spring Authorization Server,并将Google配置为联合身份提供商。我目前正在为此编写一个示例,它将在下周左右发布(请参阅this branch)。
话虽如此,如果您仍然对使用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

相关问题