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

15

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

这些移动应用程序使用最简单的Cognito设置:一个用户池,一个身份池,具有针对已验证用户的IAM角色,不允许未经身份验证的使用。 我目前没有使用Facebook、Google或Amazon登录,也没有使用其他身份验证方法。

现在,我需要在Java中创建桌面版应用程序,而且对我来说它似乎完全是另一种方法。 我想做的是:

  1. 在我的Java桌面应用程序中打开登录窗口;
  2. 在它们的字段中插入用户名和密码,然后按下登录按钮;
  3. 获取一些凭据并开始使用连接到其他AWS服务的应用程序,特别是需要使用S3、Lambda和DynamoDB。

在纸上实现这一点相当简单:

  1. 从Cognito用户池获取令牌;
  2. 将此令牌与Cognito身份池交换以获得一些凭据;
  3. 使用此凭据访问其他AWS服务。

在阅读了大量文档、下载了大量不同的项目示例和经历了很多绝望之后,我最终找到了在移动应用程序中实现此功能的方法。例如,在Android中,身份验证流程如下:

  1. 使用UserPoolID、AppClientID、PoolRegion(可选)和ClientSecret(可选)来实例化CognitoUserPool;
  2. 使用IdentityPoolID和PoolRegion来实例化凭证提供程序;
  3. 在应用程序UI中,插入用户名和密码,然后按“登录”按钮;
  4. 从先前实例化的UserPool中检索使用该用户名的CognitoUser;
  5. 为该CognitoUser获取CognitoUserSession,使用AuthenticationHandler和各种回调函数在需要时传递密码;
  6. 将该CognitoUserSession添加到先前实例化的凭证提供程序中,以TokenKey +从会话中提取的JWT令牌的形式。
  7. 此时,每当我需要访问S3、Lambda或DynamoDB时,我只需将此凭证提供程序作为其客户端构造函数的参数传递即可。

对于Java SDK来说,实现相同的功能似乎更加困难。

我成功地实现了用户注册,但是对于用户注册,我根本不知道从哪里开始。每个示例都以不同的方式执行此操作。另外,每个示例都使用特定的用例,例如开发人员身份验证登录或自定义URL以连接到某些拥有的后端。为什么很难找到像我需要的基本用例的示例?我开始认为我的基本用例根本不是基本用例,而是非典型用例。然而,我真的不知道使用用户名和密码与AWS的默认用户/凭据服务进行登录为什么会非典型。

到目前为止,我做得最好的事情就是从此示例项目中复制相关类(我也从中获取了Sign-Up部分,它的功能非常好),

{"error":"invalid_grant"}

这表明这不是有效的代码,但至少网络请求是有效的。

为了更清楚,我可以这样做:

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和Android SDK以更简单的方式自行处理所有这些问题。这是因为他们期望桌面应用程序的开发人员比起某天醒来决定制作iOS/Android应用程序的普通人要能够/专业得多吗?这可以解释他们为何要将移动SDK打造成如此开发者友好。老实说,我真的很难相信我必须做到这一点,阅读不知道放在哪里的文档页面上的内容才能登录用户,这让我想到我真的缺少了什么。我几乎阅读了我能找到的每一篇Stack Exchange问题和文档。事实上,几乎总是有一个所需的AWS文档页面,但是有时要真正找到它并不那么简单,至少对于Cognito文档而言如此。我读到可以在PC文件系统中放置所需的凭据文件,Java SDK将使用这些凭据访问所有资源,但从我的理解来看,这种方法仅适用于作为后端(Servlets)运行在服务器上的Java应用程序,最终用户无法通过浏览器访问它们。我的应用程序是面向最终用户的桌面应用程序,因此我甚至无法考虑将AWS凭据留在用户PC上(如果我错了,请纠正我,我真的很想做点简单的事情)。让我真正担心的是,可能根本无法使用用户池和身份池进行登录。我知道与Cognito相关的内容比iOS、Android和JavaScript可用得晚得多被添加到Java SDK中。但是,如果他们添加了它,我认为它应该支持至少类似于移动端的身份验证流程。更加恶化问题的是,我最初使我的应用程序的所有功能离线工作。我认为我最终会集成AWS到应用程序中。这样,应用程序会更加模块化,并且AWS相关的内容会集中在一个包中,与其余应用程序逻辑和UI分离。在我的无知海洋中,这对我来说似乎是一个好的实践,但现在,如果我无法解决这个问题,我将把几个月的工作扔进垃圾桶,只为意识到我必须构建Web应用程序,因为JavaScript得到了更多支持。

即使在MobileHub上,创建基于Cognito的ClientApp的选项也仅限于iOS、Android、JavaScript和React-something。当针对其他语言/SDK提供文档或示例时,经常会省略Java,并且我看到的选项中最频繁的是.NET。为了让我的挫败感更大,每次我在搜索引擎上搜索东西时,“Java”一词包含在“JavaScript”一词中,这掩盖了少数可能有用的结果,因为与JavaScript SDK相关的所有内容通常在搜索引擎中排名更高(这可能部分解释了为什么在StackOverflow或其他Q&A网站上查找.NET相关内容似乎更容易)。


总之,所有这些都在我脑海中产生了一些问题:

  1. 为什么很少有人似乎需要这种身份验证方法(使用用户名和密码)? 对我来说,这似乎是一个相当普遍和合理的桌面应用程序用例。我知道Web应用程序的增长率飙升,但考虑到Java是今天使用最多的语言之一,为什么没有人需要从桌面应用程序进行简单登录呢?这导致了下一个问题:
  2. 在桌面应用程序中使用Java SDK是否有什么本质上的不好/错误/风险/愚蠢之处? 它是否仅用于服务器作为后端或Web应用程序的使用?那么,连接到AWS服务的桌面应用程序应该是什么解决方案?完全不应该做AWS连接的桌面应用程序吗?Web应用程序是唯一要考虑的选项吗?为什么?我选择使用Java来实现在Widows、macOS和Linux上运行的应用程序。我也选择了Java,因为我认为它在使用上与Android SDK大致相似,因为其代码应该独立于平台UI,使得重用代码变得简单。 我错了。
  3. 如果像这样使用Java SDK没有问题,那么有没有好心人可以帮助我找到一个示例,从中将用户名和密码放入两个字段,并实例化客户端以访问其他AWS服务(如S3客户端)在Java桌面应用程序中?

告诉我您需要知道的所有内容,我会编辑问题。

请有人帮帮我,我快疯了。


1
你找到解决问题的方法了吗? - Giulio Pulina
你好,有解决方案吗? - Matteo
1个回答

2

也许对于原帖已经太晚了,但这是我用来从Cognito获取凭证的过程,该过程是在获得JWT身份令牌后进行的。通过SRP身份验证获得JWT后,我使用联合池ID和通过Cognito Idp Url和JWT传递的登录映射获取身份ID。 URL已完成,其中包括您的AWS区域和Cognito用户池ID。

    //create a Cognito provider with anonymous creds
    AnonymousAWSCredentials awsCreds = new AnonymousAWSCredentials();
    AmazonCognitoIdentity provider = AmazonCognitoIdentityClientBuilder
            .standard()
            .withCredentials(new AWSStaticCredentialsProvider(awsCreds))
            .withRegion(REGION)
            .build();

    //get the identity id using the login map
    String idpUrl = String.format("cognito-idp.%s.amazonaws.com/%s", REGION, cognitoUserPoolId);
    GetIdRequest idrequest = new GetIdRequest();
    idrequest.setIdentityPoolId(FED_POOL_ID);
    idrequest.addLoginsEntry(idpUrl, jwt);

    //use the provider to make the id request
    GetIdResult idResult = provider.getId(idrequest);
    return idResult.getIdentityId();

如果您使用不同的登录提供程序,则该URL需要更改,但这应该获取身份ID。接下来是一个类似的请求,通过传递身份ID和相同的登录映射来获取IAM凭据。

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

我花了整整一周的时间才弄明白这个问题。在我看来,AWS Java文档真的很糟糕。希望这篇文章能对某些人有所帮助。


1
感谢您抽出时间回答我的问题,但是是的,我已经采用了不同的方法来进行项目,所以我不能测试您的代码。我希望这对某些人甚至是“未来的我”有所帮助!谢谢! - mars
1
完全同意AWS文档的确很烂,难以置信。你在使用哪个AWS SDK?是2.0版本吗?还是旧的1.1.x版本?我甚至都难以编译它。他们不仅没有文档,还没有示例。有些日子我只想尖叫。 - Bill Evans
@BillEvans 我是在旧的1.1.x SDK中完成的。 - nos9
据我所知,OP表示使用AuthenticationHelper类可以通过SRP身份验证获得JWT令牌。正如我在答案中写到的那样,我也通过SRP身份验证获得了JWT。 OP正在询问如何获得身份验证凭据以进行对AWS服务的请求,这就是我的答案涉及到的内容。 - nos9

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