Amazon Cognito
开发人员指南
AWS 文档中描述的 AWS 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 Amazon AWS 入门

教程:为 Android 应用程序集成用户池

本教程概述了将 Amazon Cognito 您的用户池与 Android 应用程序集成的关键步骤。有关说明如何在应用程序中使用用户池的完整示例应用程序,请参阅 GitHub 网站上的 Amazon Cognito 您的用户池示例

步骤 1:使用控制台为应用程序创建用户池

以下过程介绍如何创建用户池以及如何在应用程序中使用用户池。此过程将使用默认设置创建一个池 ID、一个应用程序客户端 ID 和一个应用程序客户端密钥。有关自定义这些设置的信息,请参阅逐步介绍 AWS 管理控制台中的 Amazon Cognito 用户池设置

为应用程序创建用户池的步骤

  1. 登录 Amazon Cognito 控制台

  2. 选择 Manage your User Pools

  3. 选择 Create a User Pool

  4. Pool name 中,键入池的名称,然后选择 Review defaults。这样将使用默认设置创建池。

  5. 在左侧导航窗格中,选择 Attributes 以指定哪些属性为必需属性以及哪些属性要用作别名。设置以下属性之后,池中的用户需要先验证其电子邮件地址,然后才可以使用用户名或电子邮件地址登录。

    1. 对于 email,选择 RequiredAlias

    2. 对于 phone number,选择 RequiredAlias

    3. 对于 given name,选择 Required

    4. 选择 Save changes

  6. 在左侧导航窗格中,选择 Policies 以指定密码策略。在本教程中,请使用默认设置。

  7. 在左侧导航窗格中选择 Verifications。在此页面上,您可以自定义发送给池中的用户,用于提供验证代码的消息。在本教程中,请使用默认设置。

  8. 在左侧导航窗格中,选择 Apps,然后选择 Add an app。您可以为一个用户池创建多个应用程序客户端,还可以为每个平台创建一个应用程序。

  9. 对于 App name,键入应用程序的名称。使 Generate app client secret 保持选定状态,然后选择 Set attribute read and write permissions。选择需要写入权限的属性。必需属性始终具有写入权限。

  10. 选择 Create app,然后选择 Save changes

  11. 在左侧导航栏中,选择 Review,然后选择 Create pool

  12. 记下 Pool IDPool ARNApp client IDApp client secret。您可以在左侧导航栏上的 Apps 下找到应用程序客户端 ID 和应用程序客户端密钥。要查看客户端密钥,请选择 Show details

步骤 2:创建用户池对象

要创建用户池对象,您需要池 ID、客户端 ID 和客户端密钥。以下示例说明了如何创建 ClientConfiguration 对象和 CognitoUserPool 对象。CognitoUserPool 对象是与应用程序的用户池进行的所有交互的入口点。在示例应用程序中,userPool 是在 AppHelper.java 中创建的。

ClientConfiguration clientConfiguration = new ClientConfiguration(); // Create a CognitoUserPool object to refer to your user pool CognitoUserPool userPool = new CognitoUserPool(context, poolId, clientId, clientSecret, clientConfiguration);

步骤 3:为应用程序注册用户

以下步骤介绍了如何为应用程序注册用户。

为应用程序注册用户的步骤

  1. 从用户处收集以下信息:

    • 用户 ID:供用户登录时使用,且在池中必须是唯一的。

    • 密码:用户的密码。

    • 用户属性:您必须为池指定必需属性 (电子邮件、名字和电话号码)。

  2. 使用池实例注册用户。

    // Create a CognitoUserAttributes object and add user attributes CognitoUserAttributes userAttributes = new CognitoUserAttributes(); // Add the user attributes. Attributes are added as key-value pairs // Adding user's given name. // Note that the key is "given_name" which is the OIDC claim for given name userAttributes.addAttribute("given_name", userGivenName); // Adding user's phone number userAttributes.addAttribute("phone_number", phoneNumber); // Adding user's email address userAttributes.addAttribute("email", emailAddress);
  3. 创建一个用于注册的回调处理程序。注册成功后,将调用 onSuccess 方法。

    SignUpHandler signupCallback = new SignUpHandler() { @Override public void onSuccess(CognitoUser cognitoUser, boolean userConfirmed, CognitoUserCodeDeliveryDetails cognitoUserCodeDeliveryDetails) { // Sign-up was successful // Check if this user (cognitoUser) needs to be confirmed if(!userConfirmed) { // This user must be confirmed and a confirmation code was sent to the user // cognitoUserCodeDeliveryDetails will indicate where the confirmation code was sent // Get the confirmation code from user } else { // The user has already been confirmed } } @Override public void onFailure(Exception exception) { // Sign-up failed, check exception for the cause } };
  4. 调用注册 API。

    userPool.signUpInBackground(userId, password, userAttributes, null, signupCallback);

步骤 4:为应用程序确认用户

用户注册后可能需要确认,然后才能登录。用户可以通过电子邮件或电话确认。注册成功后,如果需要确认用户,则会将确认代码发送到用户的电子邮件地址或电话号码。此外,您还可以使用 Lambda 触发器在注册后自动确认用户。

如果用户在注册时提供了电子邮件地址或电话号码,并且您针对用户池选择了自动验证,则确认代码将以文本消息的形式发送到用户的电话号码,或者发送到用户的电子邮件地址。注册成功后发送到回调处理程序的 cognitoUserCodeDeliveryDetails 对象会指示确认代码发送到了哪个位置。您可以使用此对象让用户知道如何获取确认代码。

以下步骤介绍了如何确认用户信息,才能让用户登录应用程序。

为应用程序确认用户的步骤

  1. 创建一个用于确认用户的回调处理程序。此回调处理程序供开发工具包用于传达确认 API 调用的结果。

    // Callback handler for confirmSignUp API GenericHandler confirmationCallback = new GenericHandler() { @Override public void onSuccess() { // User was successfully confirmed } @Override public void onFailure(Exception exception) { // User confirmation failed. Check exception for the cause. } };
  2. 新用户确认后,发送确认代码时所用的用户属性 (电子邮件地址或电话号码) 将标记为已验证。如果此属性也设置为用作别名,则用户可以使用该属性 (电子邮件地址或电话号码) 登录,而非使用用户名登录。

步骤 5:解决别名值冲突

在池中,别名值必须是唯一的。当您确认新用户时,如果用户的电子邮件地址或电话号码用作别名,并且该电子邮件或电话号码已被池中的现有用户使用,则您必须解决这一冲突。要确保唯一性,您可以执行以下任一操作:

  • forcedAliasCreation 参数设置为 false。这将导致用户确认失败,从而解决冲突。该属性仍会对现有用户保持已验证状态,并继续用作现有用户的别名。新用户仍保持未确认状态,如以下示例所示。

    // This will cause confirmation to fail if the user attribute has been verified for another user in the same pool boolean forcedAliasCreation = false; // Call API to confirm this user cognitoUser.confirmSignUpInBackground(confirmationCode, forcedAliasCreation, confirmationCallback);
  • forcedAliasCreation 参数设置为 true 可解决冲突,具体方法是:将属性 (电子邮件或电话号码) 针对新用户标记为已验证,相应地针对现有用户将其标记为未验证。此属性不再是现有用户的别名。

所有已确认的用户均可以登录。成功登录后,将返回访问令牌和 ID 令牌。这些令牌位于 CognitoUserSession 对象中。

步骤 6:让用户登录应用程序

要让用户登录应用程序,您必须先创建一个用于身份验证的回调处理程序。以下示例介绍了开发工具包如何通过此回调处理程序与应用程序交互。

// Callback handler for the sign-in process AuthenticationHandler authenticationHandler = new AuthenticationHandler() { @Override public void onSuccess(CognitoUserSession cognitoUserSession) { // Sign-in was successful, cognitoUserSession will contain tokens for the user } @Override public void getAuthenticationDetails(AuthenticationContinuation authenticationContinuation, String userId) { // The API needs user sign-in credentials to continue AuthenticationDetails authenticationDetails = new AuthenticationDetails(userId, password, null); // Pass the user sign-in credentials to the continuation authenticationContinuation.setAuthenticationDetails(authenticationDetails); // Allow the sign-in to continue authenticationContinuation.continueTask(); } @Override public void getMFACode(MultiFactorAuthenticationContinuation multiFactorAuthenticationContinuation) { // Multi-factor authentication is required; get the verification code from user multiFactorAuthenticationContinuation.setMfaCode(mfaVerificationCode); // Allow the sign-in process to continue multiFactorAuthenticationContinuation.continueTask(); } @Override public void onFailure(Exception exception) { // Sign-in failed, check exception for the cause } }; // Sign in the user cognitoUser.getSessionInBackground(authenticationHandler);

步骤 7:获取用户详细信息

对用户进行身份验证后,您可以在用户池中检索有关用户的其他信息,如以下示例所示。

// Implement callback handler for getting details GetDetailsHandler getDetailsHandler = new GetDetailsHandler() { @Override public void onSuccess(CognitoUserDetails cognitoUserDetails) { // The user detail are in cognitoUserDetails } @Override public void onFailure(Exception exception) { // Fetch user details failed, check exception for the cause } }; // Fetch the user details cognitoUser.getDetailsInBackground(getDetailsHandler);

步骤 8:为应用程序用户获取访问 AWS 资源的凭证

要为用户获取访问 AWS 资源的凭证,请先创建一个身份池,并将用户池与该身份池关联。

获取用于访问 AWS 资源的 AWS 凭证的步骤

  1. 登录 Amazon Cognito 控制台

  2. 选择 Manage Federated Identities

  3. 选择 Create new identity pool。在 Identity pool name 中,为身份池键入一个名称。

  4. 展开 Authentication providers 部分。在 Cognito 选项卡上,为您刚创建的用户池键入 User Pool IDApp Client ID

  5. 选择 Create Pool

  6. 在应用程序代码中,将身份验证成功后收到的 ID 令牌添加到凭证提供程序,如下所示。

    // Get id token from CognitoUserSession. String idToken = cognitoUserSession.getIdToken().getJWTToken(); // Create a credentials provider, or use the existing provider. CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(context, IDENTITY_POOL_ID, REGION); // Set up as a credentials provider. Map<String, String> logins = new HashMap<String, String>(); logins.put("cognito-idp.us-east-1.amazonaws.com/us-east-1_123456678", cognitoUserSession.getIdToken().getJWTToken()); credentialsProvider.setLogins(logins);
  7. 使用凭证提供程序访问 AWS 资源,如 Amazon DynamoDB 表,如下所示。

    AmazonDynamoDBClient ddbClient = new AmazonDynamoDBClient(credentialsProvider);

步骤 9:设置 IAM 权限以允许访问 AWS 资源

当您创建一个身份池时,Amazon Cognito 会创建两个 IAM 角色:Cognito<identity pool name>Auth_RoleCognito<identity pool name>Unauth_Role。默认情况下,这些角色仅允许访问 Amazon Cognito 身份 和 Amazon Cognito Sync。要允许应用程序访问 AWS 服务 (如 Amazon DynamoDB),您必须为角色附加相应的托管策略。例如,如果应用程序需要读取和写入 DynamoDB 数据库,您必须为角色附加 AmazonDynamoDBFullAccess 托管策略,如以下过程中所述。

设置 IAM 权限以允许访问 AWS 资源的步骤

  1. 登录 AWS 管理控制台 并通过以下网址打开 IAM 控制台 https://console.amazonaws.cn/iam/

  2. 从角色列表中为策略选择经过身份验证的角色,然后选择 Attach Policy

  3. 为托管策略列表选择所需策略 (如 AmazonDynamoDBFullAccess),然后选择 Attach Policy

现在,您的应用程序可以在 DynamoDB 中执行创建、读取、更新和删除操作。

步骤 10:为用户设置新的多重验证机制

从 Amazon Cognito 开发工具包版本 2.6.8 开始,您可以为用户设置新的 MFA 机制。您可以在 Amazon Cognito 用户池控制台的 MFA and verifications 选项卡中管理用户池级别 MFA 设置。

为了能够注册新的 MFA 方法,应首先为您的用户池启用该方法。

配置软件令牌 (TOTP) MFA

Amazon Cognito 支持基于时间的一次性密码算法 (TOTP) 多重验证 (MFA) 机制。

TOTP 配置过程包括以下两个步骤:

  1. 关联软件令牌:通过请求 Amazon Cognito 服务为您的用户添加 TOTP MFA 开始该过程。这将重写已经为您的用户设置的任何 TOTP MFA。为响应此请求,服务会生成一个私有密钥,应当安全地存储该密钥并将其用于生成 TOTP 代码,以进行 MFA 验证。

  2. 验证软件令牌:使用根据私有密钥生成的 TOTP 代码完成 MFA 设置。使用 Amazon Cognito 开发工具包返回的 VerifyMfaContinuation 对象完成此步骤。

// Create a callback handler. RegisterMfaHandler registerMFAHandler = new RegisterMfaHandler() { @Override public void onSuccess(final String sessionToken) { // Success, new MFA setup is complete. } @Override public void onVerify(VerifyMfaContinuation continuation) { // Get the secret key from Continuation. String secretKey = continuation.getParameters().get("secretKey"); // Store the secret key in a TOTP code generator and verify using // the generated TOTP code. String verificationCode = storeAndGetTotpVerificationCode(secretKey); // Set a user friendly name to remember the TOTP generator. String friendlyName = "the best TOTP generator"; // Complete the registration by verifying the TOTP code. continuation.setVerificationResponse(verificationCode, friendlyName); continuation.continueTask(); } @Override public void onFailure(Exception exception) { closeWaitDialog(); showDialogMessage("TOTP MFA registration failed", AppHelper.formatException(exception), false); } }; // Use the CognitoUser to register a new Software Token MFA. associateSoftwareTokenInBackground(sessionToken, registerMFAHandler);

注意

每个用户只能具有一个与您的应用程序关联的 TOTP 软件令牌 MFA。关联新的 MFA 软件令牌将重写当前令牌。

要关联 TOTP MFA 软件令牌,您的用户必须使用有效的访问令牌经过身份验证。当未经身份验证的用户需要关联新的 TOTP MFA 软件令牌时,您可以使用 Amazon Cognito 生成的 sessionToken 来关联新的 TOTP MFA 软件令牌。

使用会话令牌配置 MFA

Amazon Cognito 用户池 现在支持多个 MFA 类型。您可以在 Amazon Cognito 控制台上启用在您的用户池中允许的 MFA 类型。您还可以选择将启用的 MFA 类型设置为 OptionalRequired

如果 MFA 设置为 Optional,则池中的用户可以根据其喜好选择注册和启用一个或多个 MFA 方法。

如果对于用户池将 MFA 设置为 Required,则用户必须注册和启用至少一个 MFA 类型。如果 MFA 是必需的,则一个 MFA 方法都未设置的用户将以 MFA_SETUP 质询状态列出。这些用户必须设置至少一个 MFA 类型才能继续进行身份验证。

为允许用户在此阶段注册新的 MFA 方法,Amazon Cognito 将返回一个会话令牌以及质询。可以在开发工具包中使用该会话令牌来注册一个 MFA 类型,然后继续进行身份验证。会话令牌是一种一次性令牌,只能用于注册新的 MFA 类型。

MFA_SETUP 质询

当引发 MFA_SETUP 质询时,用户需要设置 MFA 方法。Amazon Cognito 开发工具包将返回 RegisterMFAContinuation。您可以使用此延续性来获取用户可以设置的 MFA 类型列表,然后继续进行身份验证。

AuthenticationHandler authHandler = new AuthenticationHandler() { @Override public void onSuccess(CognitoUserSession cognitoUserSession) { // ... } @Override public void getAuthenticationDetails(AuthenticationContinuation authenticationContinuation, String userId) { // ... } @Override public void getMFACode(MultiFactorAuthenticationContinuation multiFactorAuthenticationContinuation) { // ... } @Override public void authenticationChallenge(ChallengeContinuation continuation) { // This challenge is invoked for MFA_SETUP Challenge RegisterMFAContinuation regMFAContinuation = (RegisterMFAContinuation) continuation; // Register the new MFA. registerMfa(regMFAContinuation); } @Override public void onFailure(Exception exception) { // Sign-in failed, check exception for the cause } }; // Register a new MFA. public void registerMfa(RegisterMFAContinuation regMFAContinuation) { // Get the list of MFA's to setup. List<String> mfaOptions = continuation.getMfaOptions(); // Get the session token to register an MFA. final String sessionToken = continuation.getSessionToken(); // ... // Use the sessionToken to register MFA. associateSoftwareTokenInBackground(sessionToken, registerMFAHandler); } RegisterMfaHandler registerMFAHandler = new RegisterMfaHandler() { @Override public void onSuccess(final String sessionToken) { // Success, new MFA setup is complete. // Use the sessionToken to continue to authenticate. regMFAContinuation.setSessionToken(sessionToken); } @Override public void onVerify(VerifyMfaContinuation continuation) { // ... } @Override public void onFailure(Exception exception) { // ... } };

启用和禁用 MFA 机制

使用 API setUserMfaSettings 启用或禁用 MFA 方法并将 MFA 类型设置为用户的首选类型。

GenericHandler updatesMFASettingsHandler = new GenericHandler() { @Override public void onSuccess() { // Update complete. } @Override public void onFailure(Exception exception) { // Failed update, check exception for details. } }; // Enable SMS MFA and set preferred state void enableSmsMfa(boolean preferred) { CognitoMfaSettings smsMfaSettings = new CognitoMfaSettings(CognitoMfaSettings.SMS_MFA); smsMfaSettings.setEnabled(true); smsMfaSettings.setPreferred(preferred); List<CognitoMfaSettings> settings = new ArrayList<CognitoMfaSettings>(); settings.add(smsMfaSettings); cognitoUser.setUserMfaSettingsInBackground(settings, updateSettingHandler); }

选择 MFA 质询

当用户启用了多个 MFA 类型但没有一个类型设置为首选时,Amazon Cognito 会显示一个 SELECT_MFA_TYPE 质询来允许用户选择一个 MFA 方法进行身份验证。

AuthenticationHandler authHandler = new AuthenticationHandler() { @Override public void onSuccess(CognitoUserSession cognitoUserSession) { // ... } @Override public void getAuthenticationDetails(AuthenticationContinuation authenticationContinuation, String userId) { // ... } @Override public void getMFACode(MultiFactorAuthenticationContinuation multiFactorAuthenticationContinuation) { // ... } @Override public void authenticationChallenge(ChallengeContinuation continuation) { ChooseMfaContinuation mfaOptionsContinuation = (ChooseMfaContinuation) continuation; // Get the list of MFA's to choose from List<String> mfaOptions = mfaOptionsContinuation.getMfaOptions(); // ... // Set the MFA option and continue to authenticate. mfaOptionsContinuation.setMfaOption(option); mfaOptionsContinuation.continueTask(); } @Override public void onFailure(Exception exception) { // Sign-in failed, check exception for the cause } };