适用于桌面应用程序的带有Java SDK的AWS Cognito登录

时间:2018-07-03 20:43:27

标签: java amazon-web-services authentication aws-java-sdk

我进行了很多搜索,但似乎无法从头到尾找到该主题的解决方案。作为前提,我已经在iOS和Android的两个本机应用程序中实现了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. 在应用程序界面中,输入用户名和密码,然后按“登录”按钮;
  4. 使用先前实例化的UserPool中的用户名检索CognitoUser;
  5. 使用带有各种回调的AuthenticationHandler为该CognitoUser获取CognitoUserSession,以在需要时传递密码;
  6. 以TokenKey +从会话中提取的JWT令牌的形式,将CognitoUserSession添加到先前实例化的凭据提供程序中。
  7. 这时,每当我需要访问S3,Lambda或DynamoDB时,我只需将此凭据提供程序作为其客户端构造函数的参数传递。

在我看来,要实现与Java SDK相同的功能要困难得多。

我设法轻松实现了用户注册。但是,对于用户登录,我根本不知道从哪里开始。

每个示例都以不同的方式执行此操作。最重要的是,每个示例都使用特定的用例,例如开发人员验证的登录或自定义URL,以连接到某些拥有的后端。为什么很难找到像我所需的基本用例的示例?我开始认为我的基本用例根本不是基本的,而是非典型的。为什么使用用户名和密码针对AWS的默认用户/凭证服务登录是非典型的,但是,我真的不知道。

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

Credentials getCredentials(String accessCode)

我想它应该接受使用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和Android SDK可以以更简单的方式自行处理所有操作。这是由于他们期望桌面应用程序的开发人员更加有能力/更专业,而不是某天起床后决定制作iOS / Android应用程序的普通人。对自己] ?相比之下,这可以解释他们为使移动SDK如此易于开发人员所做的努力。

我真的很难相信我必须这样做,在文档页面上阅读谁知道谁知道谁,然后登录用户,这使我认为我确实错过了一些东西。我从字面上阅读了我能找到的每个堆栈交换问题和文档。事实是,几乎总是有一个我需要的AWS文档页面,但实际上,有时候至少对于Cognito文档而言,并不是那么简单。

我读到我可以在PC文件系统中放入具有所需凭据的文件,并且Java SDK将使用这些凭据来访问所有资源,但是据我了解,此方法保留给服务器上运行的Java应用程序。后端(servlet),最终用户无法通过浏览器访问它们。我的应用程序是面向最终用户的桌面应用程序,因此,我什至不考虑将AWS凭证保留在用户PC上(如果我输入错了,请更正我,我真的很想做得这么简单)。


真正令我恐惧的是,根本不可能使用用户池和身份池登录。我知道,与Cognito相关的东西很晚才被添加到Java SDK中,可用于iOS,Android和JavaScript。但是,如果他们添加了它,我想它应该至少支持与移动对等设备相似的身份验证流程。

使问题更严重的是,我最初使应用程序的所有功能脱机工作。我以为最终可以将AWS集成到应用程序中。通过这种方式,应用程序更加模块化,并且与AWS相关的内容集中在一个包中,与其余应用程序逻辑和UI分离。在我的无知之海中,这对我来说似乎是一个好习惯,但是现在,如果我无法解决这个问题,我已经浪费了几个月的时间,只是意识到我必须构建一个Web应用程序,因为JavaScript是支持更多。

即使在MobileHub上,在Cognito上创建ClientApp的唯一选项也适用于iOS,Android,JavaScript和React-something。 当提供其他语言/ SDK的文档或示例时,通常会省略Java,而我看到的最常见的选项是.NET。 更让我感到沮丧的是,每次我在搜索引擎上搜索内容时,“ JavaScript”一词中都包含了“ Java”一词,这使可能有用的少数结果变得难以理解,因为所有与JavaScript SDK相关的东西通常都是在搜索引擎中的排名高于Java(这在一定程度上解释了为什么至少在StackOverflow或其他Q&A网站上,.NET相关的东西看起来更容易找到的原因。


总而言之,所有这些在我脑海中引发了一些疑问:

  1. 为什么这么少的人似乎需要这种身份验证方法(带有用户名和密码)?在我看来,这是桌面应用程序的一个非常普遍且合理的用例。我知道Web应用程序的发展是蓬勃发展的,但是鉴于Java是当今使用最广泛的语言之一,那么怎么可能没有人需要从桌面应用程序进行简单登录呢?这就引出了下一个问题:

  2. 在桌面应用程序中使用Java SDK是否存在本质上的坏处/错误/危险/愚蠢的事情?它是否仅打算作为后端服务器或Web应用程序在服务器上使用? ?制作连接到AWS服务的桌面应用程序应该是什么解决方案?根本不做AWS连接的桌面应用程序是错的吗?网络应用程序应该是唯一考虑的选择吗?为什么?我选择使用Java来实现可在Widows,macOS和Linux上运行的应用程序。我还选择了Java,因为我认为它的用法与Android SDK最相似,因为它的代码应该与平台UI无关,从而使代码的重用变得简单。我错了。

  3. 如果使用这样的Java SDK没什么问题,可以 好灵魂,请帮我找到一个例子, 用户名和密码在两个字段中,并实例化客户端以 访问Java桌面中的其他AWS服务(例如S3客户端) 申请吗?

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

请有人帮助我,我的头脑迷茫了。

2 个答案:

答案 0 :(得分:0)

对于OP来说可能为时已晚,但这是我用来获得CWT身份证明之后从Cognito获得凭据的过程。通过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 :(得分:0)

好的,我找到了答案,是的,Java 非常复杂:

https://github.com/aws-samples/aws-cognito-java-desktop-app/blob/master/src/main/java/com/amazonaws/sample/cognitoui/AuthenticationHelper.java

因此 Cognito 可以使用多种身份验证流程,但建议使用 SRP。 我不是密码学或安全专家,但本质上 SRP 可以帮助您避免中间人攻击,并在将密码发送到服务器之前对其进行加密。

有两个 API 调用:

  1. cognitoIdentityProvider.initiateAuth 你必须发送 USERNAME 和一个叫做 SRP_A 的盐键,它基本上是一个整数
  2. 如果成功,您应该会收到挑战类型 PASSWORD_VERIFIER
  3. cognitoIdentityProvider.respondToAuthChallenge
相关问题