当前位置: 首页 > 知识库问答 >
问题:

AWSiOS(Swift)应用中的Cognito用户池

诸葛亮
2023-03-14

我正在尝试在iOS(Swift)应用程序中实现新的AWS Cognito用户池,但我很难让登录过程正常工作。我基本上是在尝试遵循这里提供的示例。

这是我到目前为止所拥有的:

AppDelegate:

class AppDelegate: UIResponder, UIApplicationDelegate, AWSCognitoIdentityInteractiveAuthenticationDelegate {
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        let serviceConfiguration = AWSServiceConfiguration(region: AWSRegionType.USEast1, credentialsProvider: nil)
        AWSServiceManager.defaultServiceManager().defaultServiceConfiguration = serviceConfiguration
        let configurationUserPool = AWSCognitoIdentityUserPoolConfiguration(
            clientId: "###",
            clientSecret: "#########",
            poolId: "###")
        AWSCognitoIdentityUserPool.registerCognitoIdentityUserPoolWithConfiguration(serviceConfiguration, userPoolConfiguration: configurationUserPool, forKey: "UserPool")
        self.userPool = AWSCognitoIdentityUserPool(forKey: "UserPool")

        self.userPool!.delegate = self

        return true
    }

    func startPasswordAuthentication() -> AWSCognitoIdentityPasswordAuthentication {
        let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
        let logInNavigationController = mainStoryboard.instantiateViewControllerWithIdentifier("LogInNavigationController") as! UINavigationController

        dispatch_async(dispatch_get_main_queue(), {
            self.window?.rootViewController = logInNavigationController
        })

        let logInViewController = mainStoryboard.instantiateViewControllerWithIdentifier("LogInViewController") as! LogInViewController
        return logInViewController
    }
}

LoginView控制器:

class LogInViewController: UIViewController, AWSCognitoIdentityPasswordAuthentication {
    var usernameText : String?
    var passwordAuthenticationCompletion = AWSTaskCompletionSource()

    func getPasswordAuthenticationDetails(authenticationInput: AWSCognitoIdentityPasswordAuthenticationInput, passwordAuthenticationCompletionSource: AWSTaskCompletionSource) {
        self.passwordAuthenticationCompletion = passwordAuthenticationCompletionSource

        dispatch_async(dispatch_get_main_queue(), {
            if self.usernameText == nil {
                self.usernameText = authenticationInput.lastKnownUsername
            }
        })
    }

    func didCompletePasswordAuthenticationStepWithError(error: NSError) {
        dispatch_async(dispatch_get_main_queue(), {
            let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
            let mainNavigationController = mainStoryboard.instantiateViewControllerWithIdentifier("MainNavigationController") as! UINavigationController
            (UIApplication.sharedApplication().delegate as! AppDelegate).window?.rootViewController = mainNavigationController
        })
    }

    func logInButtonPressed() {
        self.passwordAuthenticationCompletion.setResult(AWSCognitoIdentityPasswordAuthenticationDetails(username: emailTextField.text, password: passwordTextField.text))
    }
}

当我点击“登录”按钮时,似乎什么也没有发生,尽管如果我再次点击它,我会得到一个NSInternalInconsistencyException(我相信这是因为AWSTask结果已经设置好了)。

任何对此的帮助都将不胜感激。我使用AWS SDKiOS版本2.4.1。

更新:

这不是我最初问题的解决方案,但我已经能够通过使用显式登录方法而不是委托方法来让用户池工作(有关详细信息,请参阅本页)。以下是我的SignenViewController中的代码:

class SignInViewController: UIViewController {
    @IBAction func signInButtonTouched(sender: UIButton) {
        if (emailTextField.text != nil) && (passwordTextField.text != nil) {
            let user = (UIApplication.sharedApplication().delegate as! AppDelegate).userPool!.getUser(emailTextField.text!)
            user.getSession(emailTextField.text!, password: passwordTextField.text!, validationData: nil, scopes: nil).continueWithExecutor(AWSExecutor.mainThreadExecutor(), withBlock: {
                (task:AWSTask!) -> AnyObject! in

                if task.error == nil {
                    // user is logged in - show logged in UI
                } else {
                    // error
                }

                return nil
            })
        } else {
            // email or password not set
        }
    }
}

然后,为了使用AWS服务(在我的例子中,AWS服务位于与Cognito不同的区域),我使用用户池创建了一个新的凭证提供者:

let credentialsProvider = AWSCognitoCredentialsProvider(regionType: .USEast1, identityPoolId: "###", identityProviderManager: (UIApplication.sharedApplication().delegate as! AppDelegate).userPool!)
let serviceConfiguration = AWSServiceConfiguration(region: .APNortheast1, credentialsProvider: credentialsProvider)
AWSLambdaInvoker.registerLambdaInvokerWithConfiguration(serviceConfiguration, forKey: "Lambda")
let lambdaInvoker = AWSLambdaInvoker(forKey: "Lambda")

另外一个问题是,每次启动应用程序时,我都会看到这个错误:“在info.plist中找不到有效的'AWSDefaultRegionType'、'AWSCOgnitorRegionType'和'AWSCOgnitorIdentityPolid'值。”。这似乎与Fabric有关,我正在使用Fabric跟踪碰撞。我已通过更改AppDelegate中的这一行解决了此问题:

Fabric.with([AWSCognito.self, Crashlytics.self])

为此:

Fabric.with([Crashlytics.self])

我希望这对其他人有帮助。

共有3个答案

虞华彩
2023-03-14

谢谢你,艾略特。几天来,我一直在努力编写这段代码的swift版本。

我尝试使用下面的代码使用显式登录。

@IBAction func signInButtonPressed(sender: AnyObject) {

var emailTextField = "username"
var passwordTextField = "password"

let serviceConfiguration = AWSServiceConfiguration(region: AWSRegionType.USEast1, credentialsProvider: nil)
AWSServiceManager.defaultServiceManager().defaultServiceConfiguration = serviceConfiguration
let configurationUserPool = AWSCognitoIdentityUserPoolConfiguration.init(clientId: "####", clientSecret: "#####", poolId: "#####")
AWSCognitoIdentityUserPool.registerCognitoIdentityUserPoolWithConfiguration(serviceConfiguration, userPoolConfiguration: configurationUserPool, forKey: "TestUserPool")

let userPool = AWSCognitoIdentityUserPool(forKey: "TestUserPool")
let user = userPool.getUser(emailTextField)

user.getSession(emailTextField, password: passwordTextField, validationData: nil, scopes: nil).continueWithExecutor(AWSExecutor.mainThreadExecutor(), withBlock: {
    (task:AWSTask!) -> AnyObject! in

    if task.error == nil {
            print("No Error")
            print(task.result)
    } else {
            print("Some Error")
            print(task.error)
    }
    return nil
})
}

当我提供正确的凭据时,它将转到错误块。虽然我已经在注册过程中验证了我的用户,但每次运行代码时都会向我的手机发送验证代码。回应机构是

Response body:
{"AuthState":"H4sIAAAAAAAAAAXB3ZJzMAAA0EeqBDvTnfkulhVCpRXyI3dNmI8KzU7bLZ5+z+m3HBjINz2jp6rxB174rmT+agWweHyPLVydEqFXi2o8j9gjTT6XcH1qeA+vWWQVbAMDW6gXvhEYgHOMH3gmg06pNTP61pBaNvO1E3zvEPFaSS2+3ccuQ6qUVvXcYjqBQKFoKvfoJHgLDKJx3VhlkKsIUGs7qbhH6qXZ3a9kl+v0uPEEOWqR0/7gk4T8iiYPm0XBXt59LivPwAGUmSr1RAfDqSz8COhkZcQLFdsev3oGVw3oWTRRXIHuRkTuqYS6/juHBIYRgzTsZ1crqHB5I5OZ2JvaMmB2aKqpS2qYizMqg5KjgqI24DtNGLfXenGu8/+/zU5ZnZlVCXTRNwtKxgXP2k0LJK9T58TCnxxRJtLnQ7AAFD4lZpnWk+dY4fGBCFqZlP4YyUGfqVQ3rW/i/PgJPnd8WN8fw/Hr5D0OChfhfCleb290yaV/AXf4itllINJONfv3B7RgGQzfAQAA","CodeDeliveryDetails":    
{"DeliveryMedium":"SMS","Destination":"+*******8869"}}
Some Error
Optional(Error Domain=com.amazonaws.AWSCognitoIdentityProviderErrorDomain Code=-1000 "startMultiFactorAuthentication not implemented by authentication delegate" UserInfo={NSLocalizedDescription=startMultiFactorAuthentication not implemented by authentication delegate})

当我提供了错误的密码时,响应主体是

Response body:
{"__type":"NotAuthorizedException","message":"Incorrect username or password."}
Some Error
Optional(Error Domain=com.amazonaws.AWSCognitoIdentityProviderErrorDomain Code=12 "(null)" UserInfo={__type=NotAuthorizedException, message=Incorrect username or password.})

你能告诉我我做错了什么吗?

齐锐进
2023-03-14

只是添加我的2美分的人谁与Object-c和示例应用程序CognitoYourUserPoolsSample由亚马逊提供的工作。@Bruce0已经用他的快速解决方案覆盖了一切。但是,如果您遇到这样的问题,即在单击登录时,getPassword身份验证详细信息不会被调用,这是因为您根本没有调用[self.userget详细信息]。事实上-getMore触发器getPassword身份验证详细信息。如果你仔细观察AWS示例应用程序,当他们在UserDetailTableViewController的viewdiLoad中启动应用程序时,他们会调用它,这是第一个加载的控制器。如果用户未登录,则get细节响应会以某种方式触发SignInViewController。这就是我下面要解释的。这就像是一种“myHomeViewController”,您想在其中显示用户相关信息。否则,您希望默认显示登录/注册屏幕。

>

  • 一般的经验法则是,在AppDelegate(didFinishLaunchingWithOptions)中连接并初始化Cognito用户池,就像在示例应用中一样。确保添加AWSCOgnitoIdentity InteractiveeAuthenticationDelegate并实施startPasswordAuthentication,您将在其中调出登录ViewController。让AppDelegate处理用户未登录时要做的事情(例如,将SignenViewController置于顶部),然后关注用户何时需要登录应用程序中的某个位置。

    当您需要特定于用户的数据时,请告诉应用程序是时候检查用户是否已登录(self.user getDetails)。同样,如果用户未登录,则AppDelegate知道该做什么。它控制着应用程序,并在所有内容的顶部显示登录视图。因此,它可能在一开始(如Facebook、Twitter等)或其他地方(如Ebay等)。只需在viewDidLoad结束时调用[self.user getDetails]。这将阻止当前ViewController在身份验证步骤(登录/注册)之前显示,或者如果用户已登录,则仅加载当前ViewController。

    1. 找出在任何ViewController中需要用户特定数据的位置
    2. 在相关ViewDidLoad中,调用[self.user getDetails]
    3. 如果用户已登录,则使用self.User getDetails的completionHandler显示用户特定的数据。
    4. 如果没有,则会在AppDelegate中自动调用startPasswordAuthentication,这会在显示ViewController之前显示登录ViewController,因为您需要特定于用户的数据
    5. 用户登录或注册
    6. 取消登录/注册ViewController,您将返回到ViewController,现在可以加载一些特定于用户的数据

    AWS示例应用程序并不简单,但它非常简单。

  • 裴永年
    2023-03-14

    更新6:(这次真的是最后一次)

    值得一提的是,(最后)AWS已经让AWS移动中心构建了一个非常好的演示应用程序,其中包括用户池作为SignInProvider(谷歌和脸书也是如此)。架构(在我看来)非常好(他们已经将身份管理和身份验证分开)

    更新5:(和最终版本)

    有一个相当完整的示例实现,以及一些关于它在另一个答案中如何工作的文档。

    iOS-AWS MobileHub使用开发人员验证的提供程序登录

    更新4:

    如果您想访问AWS服务,还需要更多步骤

    事实证明,这并不能让您通过Cognito联合身份验证(身份浏览器上的“登录”计数保持为0)。要解决此问题,您需要建立credentialsProvider并执行“credentialsProvider.getIdentityId”。之后,登录将显示肯定,您可以根据您的身份验证角色从AWS获得服务。

    如果您试图对移动应用同时进行身份验证和未身份验证访问,则需要创建AWSAnnamousCre的提供者(在单独的服务配置中)。然后,您在注销时self.credentials提供商?。(注意:我发现如果你在凭证提供商上清除keychain,每次用户注销时,它都会从一个新的id开始,这可能会很快烧掉你的50,000个免费id。)

    更新3:

    上传了一个github示例应用程序,用于Swift中IOS的AWS用户池。

    https://github.com/BruceBuckland/signin

    更新2:

    我终于让AWS用户池在Swift中正常工作了

    我的问题是,每次启动身份验证时,都是由不同viewcontroller中的身份验证失败引起的(我的错误)。我的结果是,他们中的一大群人在等待完成返回,而这些返回从未出现,而且API是“静默的”(没有显示任何错误)。API没有注意到它被多次启动(每次都是由不同的viewController启动),因此它允许反复以静默方式登录。原始帖子中没有足够的代码来查看是否存在相同的问题。

    您必须小心,AWS示例代码(在Objecte-C中)有两个导航控制器,代码会重新使用它们。我不喜欢示例应用程序在身份验证委托开始之前闪烁登录视图控制器的方式,我试图在swft版本中改进这种方式,这导致了我的问题。

    AWS用户池API被设置为使用这样的故事板或应用结构:

    1) 您的应用程序假定它已登录,然后触发代理,代理将触发身份验证,如果未登录,则会触发登录屏幕。

    2) 在最初登录的视图控制器池中,currentUser()不足以启动身份验证,API只会在您执行更多操作时触发委托(在我的示例中是user.getDetails()。

    3)身份验证是通过didCompletePassword身份验证StepBackError完成的。如果获得身份验证(或其他)错误,并且如果成功进行身份验证,则调用此委托方法。在成功认证的情况下,NSError为零,因此应该声明为NSError?在委托中(这会导致警告)。API是测试版,他们可能会解决这个问题。

    4)另一个小的“明白”,它可能对你来说很明显,它抓住了我,当你在你指定允许的应用程序的控制台中定义你的用户池时,这些应用程序中的每一个都有不同的客户端ID字符串。(我只是把同样的事情插入到示例中),效果很差(但不会报告错误)。API需要在报告部门进行一些工作。当它工作时,它是非常详细的,但是如果你传递了错误的客户端字符串,它什么也没说。如果你(像我一样)从不同的视图控制器调用API,它似乎什么也没说。它只是从不同的视图控制器接收每个新的身份验证请求,什么也不说。

    不管怎样,它现在起作用了。我希望这有助于解决您的问题。

    更新:

    我终于得到了要执行的getPasswordAuthenticationDetails。

    事实证明,只有当前用户的user.getDetails(即使没有当前用户)才能执行它。

    所以

    let user=appDelegate.pool!。currentUser()让详细信息=用户!。getDetails()

    将导致在第二行执行getPasswordAuthenticationDetails回调。

    AWS用户池的概念似乎是,我们编写一个应用程序,假设我们有一个登录用户。我们从该用户(例如在初始视图控制器中)获取详细信息,如果没有用户,代理将被踢出。

    IOS上用户池的AWS留档缺少一些重要的概念页。这些页面包括在(否则并行)Android留档。我承认,我仍在努力(天现在)与让用户池工作在迅速,但阅读的主要类和关键概念部分的Android留档澄清了很多我。我不明白为什么IOS文档中省略了它。

     类似资料:
    • 我试图弄清楚这是否是使用AWS为iOS开发用户/开发人员定义的登录凭据的“正确的”/“当前的/正确的流程”。 (我正在从Parse迁移到AWS,所以只阅读了一周的AWS)。 下载、安装并构建用于注册用户的iOS应用程序(比如电子邮件和密码(完成后,应用程序将显示电子邮件和密码的UIExtField,可在UIViewController中访问))。此外,还安装并提供了通过Cocoapods的iOS

    • 我正在尝试将AWS Cognito(用户池)和AWS DynamoDB用于我的移动应用程序。 我做了以下工作: 在AWS Cognito上创建用户池。 在AWS Cognito上创建身份池,并将用户池ID、应用客户端ID设置为身份验证提供商上的Cognito。 在AWS DynamoDB上创建SampleTable. 设置权限验证角色以访问AWS IAM上的SampleTable。 我创建了以下代

    • 我正在使用AWS Congito用户池进行帐户管理,其中Cognoto标识池将此用户池作为标识提供者。我用它来控制通过API网关对API的访问,API网关向Lambda发送请求。我的Lambda是使用Micronaut用Java8实现的。所有这些都很好。 在Lambda中,我从中的获得名称: 在Cognito标识符的字符串名称中返回的是什么。像这样的东西: us-east-1:xxxxe650-5

    • 我刚刚开始和AWS一起工作,学习AWS。我正在使用AWS移动中心的服务,到目前为止已经设置了登录 一切正常,现在我正在处理忘记密码和更新密码。 问题是,AWS设置忘记密码的方式是: 首先输入用户名,然后在用户输入用户名后,AWS通过SMS向与该用户名关联的电话号码发送验证码。 这是一个问题,因为这意味着任何人都可以输入任何用户名,并会发送一条短信,导致我的短信付款增加,老实说,这看起来很草率。 我

    • 我试图使用Cognito用户池控制台创建一个用户(我正在设置用户名和临时密码的值),但我一直收到这个错误。 属性不符合架构:生日:数字不能超过10个字符(服务:AWSCognitoIdentityProviderService;状态代码:400;错误代码:InvalidParameterException;请求ID:98f3de9e-5ce3-11e7-98e8-9d0c69d31df9) 使用无

    • 我们的设置是: 创建电子邮件/密码帐户或登录Facebook 我能够创建两个成功独立于彼此与AWS Cognito。 我想解决的问题是,如果用户先有一个电子邮件/密码帐户,然后单击Facebook登录,我希望他们链接并提示用户确认他们有权访问该电子邮件/密码帐户。 一旦用户确认以后,他们应该能够登录Facebook或通过电子邮件/密码登录来访问和调整相同的帐户。 Facebook用户在Cognit