在Java中创建JSON Web Token

5

我正在尝试创建一个JSON Web Token,以便在使用Google Analytics API访问时进行刷新令牌调用。我采用了服务账号的方法。

根据这种方法,我需要:

  1. 创建服务账号
  2. 将为Analytics应用程序创建的电子邮件地址添加到Google Analytics帐户中。
  3. 下载私钥文件(.p12)
  4. 使用此私钥和电子邮件地址构造JWT,随后用于向Google授权服务器发出HTTP POST调用,以获取刷新令牌。

我不确定我创建JWT的方法是否正确。 Google Code网站上可用的JWT_Handler.java示例仅讲述如何创建具有claim部分和request payload的JWT,其中缺少header和signature部分。这与Google为刷新令牌创建JWT的指南相矛盾,其中JWT包括三个部分:

  1. JWT Header
  2. JWT Claim
  3. Signature

所有三个部分都是Base64Url编码的。 我尝试了以下代码:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.Calendar;
import java.util.Enumeration;

import com.google.api.client.util.Base64;
import com.google.gson.JsonObject;
public class TestJWT {

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());
        String alias = "";       
        Enumeration objEnumeration = myStore.aliases();
        while (objEnumeration.hasMoreElements() == true) {
            alias = (String) objEnumeration.nextElement();              
            privateKey = (PrivateKey) myStore.getKey(alias,
                    "notasecret".toCharArray());
        }
    } catch (Exception e1) {
        e1.printStackTrace();
    }

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

    Calendar cal = Calendar.getInstance();      
    cal.set(1970, 01, 01);      
    String iat = Long.toString((System.currentTimeMillis() - cal.getTimeInMillis())/1000);
    String exp = Long.toString((System.currentTimeMillis() - cal.getTimeInMillis())/1000 + 60000L);

    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("access_type", "offline");
    claim.addProperty("exp", exp);
    claim.addProperty("iat", iat);


    System.out.println("Header : " + header);
    String headerStr = header.toString();
    System.out.println("claim : " + claim);
    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);

        System.out.println("Input String : " + inputStr);
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(inputStr.getBytes(UTF8_CHARSET));
        System.out.println("Sign : " + signature.sign());

        System.out.println("Base64url encoded sign : " + Base64.encodeBase64String(signature.sign()));

        System.out.println("Final JWT : " + Base64.encodeBase64String(headerArr) + "." + Base64.encodeBase64String(claimArr) + "." + Base64.encodeBase64String(signature.sign()));

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

}
1个回答

2

Prathamesh,这个问题和您的其他帖子是同一个问题吗? (使用JWT在Java中通过独立应用程序进行刷新令牌请求 - 不是Web应用程序)

澄清一下,使用P12文件签署JWT将让您获得访问令牌(而不是刷新令牌)。 这没关系,因为访问令牌是您需要进行后续API调用所需的。

我强烈建议使用Google的Java客户端库构建JWT并进行签名,其中您已经在其他帖子中贴出了一个很好的示例:

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();

有没有特定的原因你不想使用客户端库?它会负责创建JWT,签名,发送,构建服务请求,添加授权头,刷新访问令牌等等。


说实话,我没有任何样本代码可以查找,也没有任何API使用文档来使用Google客户端库生成JWT。我只有一个在使用Google钱包时生成JWT的示例。该示例不显示如何为Google Analytics特定API生成带有标头的JWT。这就是我不得不创建此示例代码的原因。我有一个更大的问题需要解决,那就是获取刷新令牌,因此我认为创建签名的JWT是第一步。其他线程讨论了我的更大问题。如果造成任何困惑,请谅解。 - prathamesh_mb
但仍然希望提供有关为Google Analytics创建签名JWT的指导。 - prathamesh_mb
好的,我想我可能知道混淆的地方在哪里。无论您尝试访问哪个Google API,您应该能够使用相同的代码生成一个已签名的JWT。关键区别在于更改您请求的范围,看起来您已经做到了。您能确认上面的代码对您是否有效吗?(如果不行,您收到了什么错误信息?) - aeijdenberg
一个快速的观察:使用'encodeBase64URLSafeString'代替'encodeBase64String'。 - Pratap Singh

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