AWS Cognito使用Java SDK登录桌面应用程序

AWS Cognito使用Java SDK登录桌面应用程序,java,amazon-web-services,authentication,aws-java-sdk,Java,Amazon Web Services,Authentication,Aws Java Sdk,我搜索了很多,但似乎不可能从头到尾找到解决这个问题的方法。作为前提,我已经在iOS和Android的两个本机应用程序中实现了Cognito注册、登录和凭据刷新,因此我已经(至少)基本了解了身份验证流程 这些移动应用使用尽可能最简单的cognito设置:用户池、具有IAM角色的身份池,用于已验证的用户,不允许未经验证的使用。我没有使用(至少现在)Facebook、Google或Amazon登录,也没有使用其他身份验证方法 现在我需要用Java制作这些应用程序的桌面版本,在我看来,这是一个完全不同的

我搜索了很多,但似乎不可能从头到尾找到解决这个问题的方法。作为前提,我已经在iOS和Android的两个本机应用程序中实现了Cognito注册、登录和凭据刷新,因此我已经(至少)基本了解了身份验证流程

这些移动应用使用尽可能最简单的cognito设置:用户池、具有IAM角色的身份池,用于已验证的用户,不允许未经验证的使用。我没有使用(至少现在)Facebook、Google或Amazon登录,也没有使用其他身份验证方法

现在我需要用Java制作这些应用程序的桌面版本,在我看来,这是一个完全不同的野兽。我想做的是:

String username; //retrieved from UI
String password; //retrieved from UI

//I copied AuthenticationHelper as is from the project
AuthenticationHelper helper = new AuthenticationHelper(POOL_ID, CLIENT_APP_ID, CLIENT_SECRET);

//I then retrieve the tokens with SRP authentication
AuthenticationResultType result = helper.performSRPAuthentication(username, password);

//Now I can successfully print the tokens, for example:
System.out.println(result.getAccessToken());
  • 在我的Java桌面应用程序中打开登录窗口
  • 在其字段中插入用户名和密码,然后按登录按钮
  • 获取一些凭据并开始使用连接到其他AWS服务的应用程序,特别是我需要使用S3、Lambda和DynamoDB
  • 实现这一目标的方法在纸面上相当简单:

  • 从Cognito用户池中获取令牌
  • 将此令牌提供给Cognito标识池,以交换一些凭据
  • 使用此凭据访问其他AWS服务

  • 在阅读了大量文档、下载了大量不同的项目示例和大量绝望之后,我终于找到了在移动应用程序中实现这一点的方法。例如,在Android中,身份验证流的工作方式如下:

  • 使用UserPoolID、AppClientID、PoolRegion和(可选)ClientSecret实例化CognitoUserPool
  • 使用IdentityPoolID和PoolRegion实例化凭证提供者
  • 在应用程序界面中,插入用户名和密码,然后按登录按钮
  • 从前面实例化的UserPool中使用该用户名检索CognitoUser
  • 获取该CognitoUser的CognitoUserSession,使用带有各种回调的AuthenticationHandler在需要时传递密码
  • 将该CognitoUserSession添加到前面实例化的凭据提供程序中,形式为TokenKey+从会话中提取的JWT令牌
  • 此时,每当我需要访问S3、Lambda或DynamoDB时,我只需将此凭据提供程序作为其客户机构造函数的参数传递
  • 在我看来,用JavaSDK实现相同的功能要困难得多

    我设法实现了用户注册相当容易。然而,随着用户登录,我根本不知道从哪里开始

    每个示例都以不同的方式执行此操作。除此之外,每个示例都使用特定的用例,例如开发人员身份验证的登录,或自定义URL来连接到某些自有后端。为什么很难找到一个基本用例的例子,比如我需要的那个?我开始认为我的基本用例根本不是基本的,而是非典型的。为什么使用用户名和密码登录AWS的默认用户/凭据服务会是非典型的,然而,我真的不知道

    到目前为止,我所做的最好的工作就是复制相关的类(我也从中学习了注册部分,效果很好),并在控制台中打印IdToken、AccessToken和RefreshToken。它们打印正确且不为空。 我不能真正理解的是如何获取凭据并将其添加到凭据提供程序中,以便实例化客户端以访问其他AWS服务。我在项目中看到的唯一方法就是调用该方法

    凭证获取凭证(字符串访问代码)

    我认为它应该接受使用InitAuth方法检索的访问代码(启动OAuth2.0身份验证流,如果我错了,请纠正我)。问题是我找不到检索代码的方法。我找不到访问代码的在线示例来查看它的外观。我尝试放置一个令牌,web请求响应

    {"error":"invalid_grant"}
    
    这表明它不是一个有效的代码,但至少web请求是有效的

    更清楚地说,我能做的是:

    String username; //retrieved from UI
    String password; //retrieved from UI
    
    //I copied AuthenticationHelper as is from the project
    AuthenticationHelper helper = new AuthenticationHelper(POOL_ID, CLIENT_APP_ID, CLIENT_SECRET);
    
    //I then retrieve the tokens with SRP authentication
    AuthenticationResultType result = helper.performSRPAuthentication(username, password);
    
    //Now I can successfully print the tokens, for example:
    System.out.println(result.getAccessToken());
    
    如何从这里检索凭据?我应该把身份池id放在哪里?在Android中,我只需将JWT令牌添加到
    HashMap
    中,然后像这样使用它

    credentialsProvider.setLogins(loginsMap)

    此外,该项目包含数百行代码的类、BigInteger变量、多行随机字符的硬编码字符串(我想是某种密钥或令牌)以及其他类似的黑魔法(尤其是在AuthenticationHelper类中)。我不喜欢这个解决方案的另一件事是,它通过手动编写的web请求检索凭据(另外一个单独的类是临时创建的,用于发出请求)。Java SDK中真的没有一些方便的方法将所有这些东西包装在一堆优雅的代码行中吗?为什么称它为SDK而不是?iOS和安卓SDK以如此简单的方式自行处理所有这些问题。这是因为他们期望桌面应用程序的开发人员更能干/更专业,而不是有一天从床上起来,决定制作iOS/Android应用程序的普通人[暗指他自己]?这就解释了为什么相比之下,他们努力使移动SDK对开发者如此友好

    有一件事我很难相信我必须这么做,在一个知道在哪里的文档页面上阅读谁知道什么,才能登录一个用户,这让我觉得我真的错过了一些东西。我逐字逐句地阅读了我能找到的每个堆栈交换问题和文档。事实上,对于我所需要的东西,几乎总是有一个AWS文档页面,但要真正找到它有时并不那么简单,至少对于Cognito文档来说是这样

    我读到我可以把一个具有所需凭据的文件放在
        //create a Cognito provider with anonymous creds
        AnonymousAWSCredentials awsCreds = new AnonymousAWSCredentials();
        AmazonCognitoIdentity provider = AmazonCognitoIdentityClientBuilder
                .standard()
                .withCredentials(new AWSStaticCredentialsProvider(awsCreds))
                .withRegion(REGION)
                .build();
    
        //request authenticated credentials using the identity id and login map for authentication
        String idpUrl = String.format("cognito-idp.%s.amazonaws.com/%s", REGION, cognitoUserPoolId);
        GetCredentialsForIdentityRequest request = new GetCredentialsForIdentityRequest();
        request.setIdentityId(identityId);
        request.addLoginsEntry(idpUrl, jwt);
    
        //use Cognito provider to perform credentials request
        GetCredentialsForIdentityResult result = provider.getCredentialsForIdentity(request);
        return result.getCredentials();