Spring 用谷歌访问令牌交换用户信息

问题描述 投票:0回答:2

我正在尝试为我的网站实现google身份验证,它是React前端和Spring Boot REST后端。

对于我的 React 应用程序,我找到了一个提供 Google 登录按钮的库,在其中我可以指定我的应用程序的“客户端 ID”,在 Google 开发者控制台中注册以及其余部分(将用户重定向到 google 身份验证页面, 访问令牌/ID令牌)的检索是由该库完成的。

现在我想将此访问令牌发送到我的后端,用它向谷歌发出请求,以验证该前端用户是否是真正的谷歌用户,谁正确地进行了身份验证并通过显示此访问来获取该用户的数据谷歌令牌(我只想要电子邮件/姓名/子,这是用户唯一的谷歌ID)。我是否正在寻找一些库来将此谷歌访问令牌交换为用户数据?

我也对以下事实感到困惑:所有 oauth 教程都说,当用户在 google 页面上进行身份验证时,我的应用程序将收到一个 授权代码 ,但正如我所说,我的前端收到访问令牌和 id 令牌。是因为我正在使用的库吗? https://www.npmjs.com/package/react-google-login这是库

这是当用户在谷歌页面上进行身份验证时到达我的客户端应用程序的数据 console.log(response) in browser

谢谢

java spring spring-boot spring-security-oauth2 google-oauth
2个回答
3
投票

我也遇到了和你一样的问题。我通过使用谷歌的GoogleIdTokenVerifier解决了这个问题。设置非常简单。

这是我的代码:

import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.gson.GsonFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Collections;

@RestController
@RequestMapping(value = "/api/authenticate")
public final class AuthenticationController {

    @GetMapping
    public String exchange(@Autowired NetHttpTransport transport, @Autowired GsonFactory factory, HttpServletRequest request) throws GeneralSecurityException, IOException, IllegalAccessException {
        // get id_token from Authorization Bearer
        String token = this.getTokenFromRequest(request);

        // Create verifier
        GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, factory)
                .setAudience(Collections.singletonList(<CLIENT_ID_HERE>))
                .build();

        // Verify it
        GoogleIdToken idToken = verifier.verify(token);
        if (idToken == null) {
            throw new IllegalAccessException("Invalid id_token");
        }
        // Access payload 
        System.out.println("Email: " + idToken.getPayload().getEmail());
    }

    public String getTokenFromRequest(HttpServletRequest request) throws IllegalAccessException {
        String token = request.getHeader("Authorization");
        String[] parts = token.split(" ");
        if (parts.length != 2 || !parts[0].contains("Bearer")) {
            throw new IllegalAccessException("Authorization Bearer format invalid. <Bearer {token}>");
        }
        return parts[1];
    }
}

Maven 依赖项:

<dependency>
    <groupId>com.google.api-client</groupId>
    <artifactId>google-api-client</artifactId>
    <version>1.30.4</version>
</dependency>

<dependency>
    <groupId>com.google.http-client</groupId>
    <artifactId>google-http-client-gson</artifactId>
    <version>1.21.0</version>
</dependency>

0
投票

也许我迟到了,但让我把它留在这里:

  1. 您使用的反应库有意检索访问令牌,因为它假设您将其用于前端,因此使用此令牌,您可以向资源服务器发出任何进一步的请求。在这种情况下,资源服务器和身份验证服务器是相同的。 例如,您想使用 Google 作为 OAuth 提供商,并在前端应用程序中显示 Google 的联系人。所以 Google 这里是身份验证服务器(react 的 lib 使 Google 的 API 调用访问令牌)和资源服务器(使用访问令牌,您的 React 应用程序会向 Google 联系人发出请求)。
  2. 如果后端进入游戏,您需要获取授权代码 - 客户端(不是前端;就 OAuth 规范而言是客户端)将交换访问令牌的临时代码。 React 向身份验证服务器(Google/Facebook/Okta)请求身份验证代码,以将此代码发送到后端。 后端将此代码与 client_id、secret、grant_type...以及 OAuth 规范中定义的其他参数一起发送到同一 OAuth 服务器。 Auth 服务器会比较所有参数,如果所有参数都正确,则返回令牌。 后端收到令牌,因此我们可以声明我们用令牌交换了代码。 下一步是将令牌发送回前端,将其存储在前端,并将其附加到后端的每个进一步请求。 当然,我们还需要处理刷新令牌。

这可能是一种可能的控制器方法,用于将代码交换为令牌。

@PostMapping("/check/code/google")
    public ResponseEntity<?> handleGoogleAuthCode(@RequestBody Map<String, String> codeMap) {
        String code = codeMap.get("code");

        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        Map<String, String> params = new HashMap<>();
        params.put("code", code);
        params.put("client_id", clientId);
        params.put("client_secret", clientSecret);
        String redirectUri = "https://bpnckmnjnpoohfnodnhjpehocneckmmc.chromiumapp.org/";
        params.put("redirect_uri", redirectUri);
        params.put("grant_type", "authorization_code");

        HttpEntity<Map<String, String>> request = new HttpEntity<>(params, headers);

        ResponseEntity<Map> response = restTemplate.postForEntity("https://oauth2.googleapis.com/token", request, Map.class);
        Map<String, Object> responseBody = response.getBody();

        String accessToken = (String) responseBody.get("access_token");

        // Use the access token to authenticate the user
        // Your logic here to associate the user with the access token

        // Set the authentication in the SecurityContext
        //       Authentication auth = Create your own authentication object based on the user's details
        //      SecurityContextHolder.getContext().setAuthentication(auth);

        return ResponseEntity.ok().build();
    }
© www.soinside.com 2019 - 2024. All rights reserved.