使用JWT在Java中创建Refresh Token请求的独立应用程序 - 不是Web应用程序

3
我有一个关于使用JWT获取Google Analytics的Refresh Token的问题。
请参考下面的示例代码,它正常工作,我能够获得Analytics对象,通过该对象我可以访问Analytics数据。
        GoogleCredential credentialGA = new GoogleCredential.Builder().setTransport(httpTransport)
                .setJsonFactory(JSON_FACTORY)
                .setServiceAccountId("$#$@#$#$#$@developer.gserviceaccount.com")
                .setServiceAccountScopes(Collections.singleton(AnalyticsScopes.ANALYTICS_READONLY))
                .setServiceAccountPrivateKeyFromP12File(new File("$#$#$%$%$%$-privatekey.p12"))
                .build();
        this.analytics = new Analytics.Builder(httpTransport, JSON_FACTORY, credentialGA).setApplicationName("Demo App").build();

如果我调用credentialGA.getAccessToken(),它会返回一个字符串。但是如果我调用credentialGA.getRefreshToken(),它总是返回null。 根据所说的话,如果我要继续使用此Analytics对象进行进一步的调用,则需要使用刷新令牌,以便我可以再次创建GoogleCredential对象来获取Analytics对象。
因此,我按照https://developers.google.com/accounts/docs/OAuth2ServiceAccount中提到的步骤创建了JWT。然后必须使用此JWT来进行刷新令牌调用。 在此链接中共享的示例说明了如何通过servlet/jsp/http实现: https://code.google.com/p/gwdg-java/source/browse/src/java/com/google/iapsample/?r=a52c4e72381b1fc959053e3a1fc8c585ae2d7112 是否有任何示例代码可以解释如何使用Java Standalone应用程序使用JWT进行刷新令牌调用。
提前致谢。
问候, Prathamesh
生成JWT的代码:
    private final static Charset UTF8_CHARSET = Charset.forName("UTF-8");
private static KeyStore myStore = null;
private static FileInputStream in_cert = null;
public static void main(String[] args) {
    PrivateKey privateKey = null;       
    try {
        in_cert = new FileInputStream(
                "D://Google Analytics//ClientLogin//Analytics//%#$%#$%$^%^%$&^%&$-privatekey.p12");

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }

    try {
        myStore = KeyStore.getInstance("PKCS12");
        myStore.load(in_cert, "notasecret".toCharArray());

        Enumeration<String> aliases = myStore.aliases();
        String alias = "";       
        Enumeration objEnumeration = myStore.aliases();
        while (objEnumeration.hasMoreElements() == true) {
            alias = (String) objEnumeration.nextElement();

            privateKey = (PrivateKey) myStore.getKey(alias,
                    "notasecret".toCharArray());
            if(privateKey != null){
            System.out.println("PRIVATE KEY:" + privateKey.toString());
            }
        }

    } catch (Exception e1) {
        e1.printStackTrace();
    }

    JsonObject header = new JsonObject();
    header.addProperty("alg", "RS256");
    header.addProperty("typ", "JWT");
    String headerStr = header.toString();

    JsonObject claim = new JsonObject();
    claim.addProperty("iss", "%^@%@^%$%@developer.gserviceaccount.com");
    claim.addProperty("scope", "https://www.googleapis.com/auth/devstorage.readonly");
    claim.addProperty("aud", "https://accounts.google.com/o/oauth2/token");
    claim.addProperty("exp", "1384497516");
    claim.addProperty("iat", "1384597516");     
    String claimStr = claim.toString();

    try {

        byte[] headerArr = headerStr.getBytes(UTF8_CHARSET);
        System.out.println(Base64.encodeBase64String(headerArr));
        byte[] claimArr = claimStr.getBytes(UTF8_CHARSET);
        System.out.println(Base64.encodeBase64String(claimArr));
        String inputStr = Base64.encodeBase64String(headerArr) + "." + Base64.encodeBase64String(claimArr);         
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(inputStr.getBytes(UTF8_CHARSET));      

    } catch (Exception e) {
        e.printStackTrace();
    }

这个JWT字符串被复制并在另一个程序中使用,请求刷新令牌,如下所示:
    HttpClient client = new DefaultHttpClient();
    HttpPost post = new HttpPost("<link i have posted in the comment>");
    try {
        List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
        nameValuePairs.add(new BasicNameValuePair("grant_type",
                "urn:ietf:params:oauth:grant-type:jwt-bearer"));
        nameValuePairs.add(new BasicNameValuePair("assertion",
                "<JWT Calculated earlier>"));
        post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
        HttpResponse response = client.execute(post);
        BufferedReader rd = new BufferedReader(new InputStreamReader(
                response.getEntity().getContent()));

        String line = "";
        while ((line = rd.readLine()) != null) {
            System.out.println(line);
            if (line.startsWith("Auth=")) {
                String key = line.substring(5);
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

代码中提到的链接:https://accounts.google.com/o/oauth2/token - prathamesh_mb
3个回答

1
您成功调用API所需的是访问令牌。对于服务帐户,您可以使用私钥签署JWT并将其发送到accounts.google.com来获取访问令牌。
看起来,您上面的代码应该可以很好地完成这项工作。
刷新令牌是一种不同的东西。这是Google在最终用户授权您的应用程序访问其数据后向您的应用程序提供的内容。与上面的私钥类似,它可用于获取访问令牌,然后用于后续的API调用。
简而言之,对于服务帐户用例(这是代码片段显示的内容),您根本不需要查看刷新令牌。您具体要做什么?

我正在寻找示例代码,可以让我了解如何创建JWT并使用该JWT发送请求以获取刷新令牌。我尝试过同样的事情,但是无法获得任何样本来验证我的努力。不幸的是,Google也没有足够的文档或示例。如果我尝试运行我的代码,我会收到{ "error" : "invalid_request" }的错误提示。 - prathamesh_mb
我想说的是,使用JWT无法获取刷新令牌。私钥+JWT和刷新令牌都是获取访问令牌的方式,而访问令牌最终是您调用API所需的。是什么让您认为您需要为您的服务帐户获取刷新令牌?(我强烈建议使用Google提供的Java客户端库来处理授权,而不是自己生成底层结构) - aeijdenberg
如果我错了,请原谅我...我的基本要求是按照OAuth 2.0开发人员指南中所述获取刷新令牌。如果上述过程不需要获取刷新令牌,即使使用它来获取访问令牌也不会产生任何结果,而是返回错误。如果相同的请求用于获取访问令牌,则仍然失败。请帮助我理解我的错误。提前致谢。 - prathamesh_mb
在顶部的问题中,您说“下面的示例代码运行良好,我能够获取Analytics对象,使用它可以访问Analytics数据。” 我不明白为什么您要获取底层的刷新或访问令牌,如果您已经有了使用推荐库的工作代码。您能否详细说明您要实现的确切目标?或者,如果该示例代码实际上出现错误,请粘贴您看到的确切错误消息? - aeijdenberg

0

我认为您可以通过调用以下方法来检查accessToken的有效性:

https://www.googleapis.com/oauth2/v1/tokeninfo?access_token={accessToken} 

终端点并重新生成JWT,获取访问令牌。


0
我知道有点晚了,但是我需要实现自己的JWT生成来访问Firebase云消息,而且我注意到你的代码中缺少了这个部分。
byte[] sig = signature.sign();

正确签署JWT。
整个部分:
jwt = Base64.getUrlEncoder().encodeToString(headerStr.getBytes(Charset.forName("UTF-8"))) + "."
                    + Base64.getUrlEncoder().encodeToString(claimStr.getBytes(Charset.forName("UTF-8")));
            String inpSign = new String(jwt);
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initSign(privateKey);
            signature.update(inpSign.getBytes(Charset.forName("UTF-8")));
            byte[] sig = signature.sign();
            jwt = jwt + "."+ Base64.getUrlEncoder().encodeToString(sig);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接