实现远程oauth服务器并与客户端交换令牌,而无需在服务器上保存令牌

时间:2020-02-10 12:00:09

标签: java google-oauth google-oauth-java-client

//更新-见下文

因此,我目前正在开发一个小型Java应用程序,以访问我的实时流的聊天记录。除了twitch yt以外,没有irc服务器来访问聊天,因此我被迫使用yt api。基本上,我计划仅由我自己使用它,但我可能会为某些朋友提供它,甚至将其公开。 我已经成功获得了对api的访问权限,但前提是我将客户端密码和令牌存储在系统中。当我想公开打开它时,我必须设置一个身份验证服务器,然后将令牌传递给客户端。主要问题是实际需要创建令牌的方法com.google.api.client.auth.oauth2.AuthorizationCodeFlow.createAndStoreCredential(TokenResponse,String)。这将在任何代码运行的地方存储令牌-因此,在服务器而非客户端的计划环境中。 我还发现了这一点:OAuth-server, storing user tokens-因此,如果我正确理解了每当服务使用google oauth时,它将访问令牌存储在其自己的存储中,然后有人与客户端进行交互。 因此,我有两个问题: 1)如果我想向公众开放一个项目以便他人使用,该如何实现这种身份验证服务? 2)如何将令牌发送给客户端?还是必须通过我的服务器完成所有通信? 2a)如果上一个问题的答案是“是”,那么我该如何确保访问权限安全,以便每个客户端只能在仅由简单字符串标识的情况下才能访问其自己的令牌?

当前,这是获取令牌的代码:

public class Main
{
    public final static void main(final String... args)
    {
        (new Main()).start();
    }
    private Main() {}
    private void start()
    {
        try
        {
            File DATA_STORE_DIR=new File(System.getProperty("user.home"), "yta");
            JsonFactory JSON_FACTORY=JacksonFactory.getDefaultInstance();
            List<String> SCOPES=Arrays.asList(YouTubeScopes.YOUTUBE_READONLY, YouTubeScopes.YOUTUBE, YouTubeScopes.YOUTUBE_FORCE_SSL, YouTubeScopes.YOUTUBE_UPLOAD);
            DataStoreFactory DATA_STORE_FACTORY=new FileDataStoreFactory(DATA_STORE_DIR);
            NetHttpTransport transport=GoogleNetHttpTransport.newTrustedTransport();
            GoogleClientSecrets clientSecrets=GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(new FileInputStream(new File(DATA_STORE_DIR, "client_secret.json"))));
            GoogleAuthorizationCodeFlow googleAuthorizationCodeFlow=new GoogleAuthorizationCodeFlow.Builder(transport, JSON_FACTORY, clientSecrets, SCOPES).setDataStoreFactory(DATA_STORE_FACTORY).build();
            String authURL=googleAuthorizationCodeFlow.newAuthorizationUrl().setRedirectUri("urn:ietf:wg:oauth:2.0:oob").build();
            Desktop.getDesktop().browse(new URI(authURL));
            String authToken=System.console().readLine();
            GoogleTokenResponse googleTokenResponse=googleAuthorizationCodeFlow.newTokenRequest(authToken).setRedirectUri("urn:ietf:wg:oauth:2.0:oob").execute();
            Credential credential=googleAuthorizationCodeFlow.createAndStoreCredential(googleTokenResponse, "userId");
        }
        catch(IOException|GeneralSecurityException|URISyntaxException ex)
        {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }

    }
}

最终将在DATA_STORE_DIR目录中创建文件“ StoredCredential”,该目录包含由“ userId”标识的身份验证令牌。要将令牌传输到客户端,我可以调用getRefreshToken和getAccessToken,将它们发送到客户端,将它们存储在客户端上,从这两个字符串构建一个新的Credential实例,并通过刷新来“激活”它。为了在服务器端摆脱令牌,我可以在数据存储区上调用delete-为了提高安全性,我可以为此流使用UUID,因此某些攻击者很难截获令牌-但存在风险还在那儿。因此,我正在寻找一种无需将令牌存储在服务器上(甚至不是很短的时间)来创建令牌的方法,以防止可能的攻击媒介。 你们中有人知道怎么做吗?

//更新 因此,我进行了修补,以查看如何在客户端上创建令牌-仍然需要在身份验证代码流中使用我的client-id和client-secret(否则,您会收到一个异常,告诉您设置这些令牌)。结束于以下几行:

credential=(new Credential.Builder(BearerToken.authorizationHeaderAccessMethod()))
    .setTransport(transport)
    .setJsonFactory(JSON_FACTORY)
    .setTokenServerEncodedUrl("https://oauth2.googleapis.com/token")
    .setClientAuthentication(new ClientParametersAuthentication("client-id", "client-secret"))
    .build()
.setAccessToken("access-token")
.setRefreshToken("refresh-token");

据我所知,即使不暴露必须将api机密保持秘密的情况,也无法将Credential对象传输到远程客户端。这意味着任何呼叫都必须通过服务器,必须通过服务器会话/连接以某种方式唯一地标识客户端,并且每个答复都必须转发回客户端。

所以-这使我想到一个问题:我想做的事*可能吗? * =用Java编写程序,以便访问我的实时流的聊天记录-将该程序的令牌存储在本地,但请求外部服务器来获取它

我也尝试使用API​​密钥-但对于我要执行的操作似乎没有足够的权限-甚至对于给定用户名的简单通道ID查找都失败了,所以我必须使用OAuth < / p>

//更新2 嗯-没关系-我在5分钟内达到了每天10k的正常免费用户帐户的上限-猜猜我必须切换到抽搐然后...

1 个答案:

答案 0 :(得分:0)

因此,我找到了一个解决方案:https://github.com/cryptearth/YouTubeLiveChat/commit/b1ce15400688b6907600b006463ce538132bd807 归结为直到现在我还不了解的两件事:

  1. Credential.Builder可以很好地工作,只需将“ null”作为客户端机密即可。
  2. AuthorizationCodeFlow不需要DataStoreFactory。

因此,当未为AuthorizationCodeFlow设置DataStoreFactory时,将创建凭据,但不会将其存储在任何地方(在源代码中,有一个简单的if(null)用于检查是否设置了DataStoreFactory)。客户端ID也不重要,因为没有其他线程可以访问新创建的凭据的风险。 由于AccessToken的有效性有限(大约一个小时),因此我没有测试客户端运行时间超过此时间会发生什么,但是正如文档提示的那样,存在内部检查,所以我想它会尝试刷新自身-失败。或者,如果未进行刷新,则下一个呼叫将失败,仅显示Google的401-未经授权的回复。因此,通过禁用自动刷新机制,我想我必须自己进行某种方式的检查,并在正确的时间在服务器上进行刷新。要刷新,我只是将刷新令牌发送到服务器,然后用新的访问令牌及其新的生存期进行响应。 因此,服务器不必存储任何内容,而客户端只需存储刷新令牌-完成!在有效性用完后,可以通过滥用401答复引起的异常来重新实现自动刷新的内容,这样做很正常-可以工作,但被认为是错误的代码样式-必须弄清楚如何编写它并不差。 / p>

关于在5分钟内达到10k的限制:我将超时设置为10sec-在测试中给了我大约2h30m-仍然不足以应付我自己的日常视频流-如果要分享,我必须进一步延长超时时间。因此,如果10秒让我得到2h30m,则我需要100秒的轮询超时才能获得24h左右(一次使用(!)-1m40s)乘以用户数量。猜猜我将不得不制作一个适当的项目页面,并以某种方式不得不增加我的最大配额...但这是另一回事了。

问题已解决-项目开发人员已暂停。