在 Java 中创建 JSON Web 令牌

Posted

技术标签:

【中文标题】在 Java 中创建 JSON Web 令牌【英文标题】:Creating JSON Web Token in Java 【发布时间】:2013-11-28 22:08:25 【问题描述】:

我正在尝试创建一个 JSON Web 令牌,以便使用它通过 Google Analytics API 访问进行刷新令牌调用。我采取了服务帐户的方法。

按照这种方法,我需要:

    创建服务帐号 使用 Google Analytics 帐户添加为 Analytics 应用创建的电子邮件地址。 下载私钥文件(.p12) 使用此私钥和电子邮件地址构建 JWT,随后将用于对 google auth 服务器进行 HTTP POST 调用以获取刷新令牌。

我不确定我创建 JWT 的方法是否正确。 Google Code 站点上以JWT_Handler.java 提供的示例讨论了创建带有声明部分和请求有效负载的 JWT,仅缺少标头和签名部分。这与 Google 为刷新令牌创建 JWT 的指南混淆,其中 JWT 涉及三个部分:

    JWT 标头 JWT 声明 签名

这三个部分都是 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】:

Prathamesh,这个问题和你的其他帖子一样吗? (Making Refresh Token Request In Java With JWT through a stand alone application - Not a Web App)

为了澄清,使用 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。我有一个在谷歌钱包的情况下生成 JWT 的例子。该示例未显示为 Google Analytics 特定 API 生成带有标头的 JWT。这就是我不得不求助于创建此示例代码的原因。我遇到了获取刷新令牌的更大问题,为此我认为创建签名的 JWT 是第一步。另一个线程讨论了我的更大问题。很抱歉,如果造成任何混乱。 但如果提供有关为 Google Analytics 创建签名 JWT 的指导,我们将不胜感激。 好的,我想我可能会看到混乱在哪里。无论您尝试访问哪个 Google API,您都应该能够使用相同的代码生成签名的 JWT。关键区别在于更改您请求的范围,看起来您已经完成了。你能确认上面的代码是否适合你吗? (如果没有,您收到什么错误?) 一个快速观察:使用 'encodeBase64URLSafeString' 代替 'encodeBase64String'

以上是关于在 Java 中创建 JSON Web 令牌的主要内容,如果未能解决你的问题,请参考以下文章

如何在纯 php 中创建刷新令牌?

在c#asp.net core web api中创建jwt令牌[重复]

如何在java中创建jwt [关闭]

java中创建解析json

使用JSON在java Servlet中创建json数组[重复]

即使令牌是在邮递员中创建的,JWT 令牌也会返回空