使用 AWS Cognito 對 Web 和 iOS 應用程序進行用戶身份驗證(第 1 部分)

已發表: 2022-03-10
快速總結↬開發人員和組織都在尋找一種方法來提高移動解決方案的靈活性。 人們希望減少從構思到測試的時間。 作為一名開發人員,我經常遇到一個可以減緩移動假設初始構建的障礙:用戶管理。

多年來,我從零開始構建了至少三個用戶管理系統。 大部分方法可以基於樣板,但總是有一些關鍵項目需要為特定客戶定制。 這足以引起人們的關注,一整類用戶管理、身份驗證和授權服務都應運而生,以滿足這一需求。 像 Auth0 這樣的服務擁有基於用戶和身份管理的完整解決方案,開發人員可以與之集成。

提供此功能的一項服務是 Amazon Web Services (AWS') Cognito。 Cognito 是一種工具,可讓用戶註冊和登錄您創建的 Web 和移動應用程序。 除此功能外,它還允許離線存儲用戶數據,並提供此數據的同步。 正如亞馬遜所說,“借助 Amazon Cognito,您可以專注於創建出色的應用體驗,而不必擔心構建、保護和擴展解決方案來處理用戶管理、身份驗證和跨設備同步。”

低估輪播

旋轉木馬真的不值得他們多年來獲得的壞名聲。 它們可以證明是非常有效的,並且有多種形狀和大小。 閱讀相關文章 →

去年,亞馬遜在其 Cognito 服務中引入了自定義用戶池。 現在,此功能提供了我和其他開發人員所需的東西,以便擁有一個完整的、可定制的、跨平台的用戶管理系統,並具有適應大多數用例所需的靈活性。 要了解原因,我們需要快速了解用戶管理是什麼以及它解決了哪些問題。

跳躍後更多! 繼續往下看↓
AWS 認知
Cognito 自定義用戶池圖(查看大圖)

在本文中,我們將花費大部分時間來完成為我們的需求配置用戶池的過程。 然後,我們將此用戶池與 iOS 應用程序集成,並允許用戶登錄並獲取與其用戶帳戶關聯的屬性。 到最後,我們將有一個有限的演示應用程序,但它處理用戶管理的核心。 此外,在這到位之後,會有一篇後續文章對此進行更深入的探討。

我們需要從用戶管理中得到什麼?

如果您有一個移動或網絡應用程序,那麼您在用戶管理方面究竟需要什麼? 雖然用戶登錄可能是您首先想到的,但我們不能止步於此。 如果我們想要一個適用於大多數 Web 和移動應用程序用例的靈活用戶管理系統,它需要具有以下功能:

  • 用戶名和密碼登錄;
  • 安全密碼散列和存儲;
  • 密碼更改;
  • 密碼策略和驗證;
  • 用戶生命週期觸發器(歡迎電子郵件、再見電子郵件等);
  • 用戶屬性(名字、姓氏等);
  • 每個用戶所需的配置和可選屬性;
  • 處理忘記的密碼;
  • 通過短信驗證電話號碼;
  • 電子郵件驗證;
  • 基於權限的 API 訪問端點;
  • 在移動設備上安全存儲訪問令牌;
  • 移動設備用戶屬性的離線存儲;
  • 在線和離線狀態的用戶屬性同步;
  • 多因素身份驗證。

雖然用戶管理起初可能看起來像一個登錄系統,但功能必須遠遠超出此範圍,才能使系統真正靈活地處理大多數用例。 這顯然遠遠超出了用戶名和密碼。

此處需要指出另外一項:安全性。 任何用戶管理系統的要求之一是需要對整個系統的安全性進行持續評估。 許多自定義用戶管理系統存在根本沒有得到糾正的漏洞。 去年,Dropbox、Dailymotion、Twitter 和 Yahoo 等公司的用戶管理系統出現了安全漏洞。 如果您選擇構建自定義解決方案,那麼您就需要保護您的系統。

進入亞馬遜認知

Amazon Cognito 是一項託管服務,可讓您將靈活且可擴展的用戶管理系統集成到您的 Web 和移動應用程序中。 Cognito 提供了兩種不同的方式來使用該服務:聯合身份,允許通過 Facebook 等社交網絡登錄,以及用戶池,為特定應用程序或應用程序套件提供完全自定義的用戶管理功能。

如果您希望用戶能夠使用 Facebook(或 Google、Amazon 等)登錄,聯合身份非常有用,但這意味著用戶管理流程的一部分將外包給 Facebook。 雖然在某些情況下這可能是可以接受的,但用戶可能不想將他們的 Facebook 帳戶連接到您的應用程序。 此外,您可能希望直接管理更多用戶的生命週期,為此,聯合身份沒有那麼靈活。 就今天的文章而言,我們將重點關注用戶池,因為它們提供了適用於大多數用例的強大用戶管理平台所需的靈活性。 通過這種方式,您將擁有一種可用於大多數項目的方法。

因為這是一項 AWS 服務,所以使用 Cognito 還有其他好處。 Cognito 可以與 API Gateway 集成,以提供一種基於從 Cognito 登錄返回的令牌授權 API 訪問的輕鬆方式。 此外,如果您已經將其他 AWS 服務用於您的移動應用程序,您可以使用您的用戶池作為您的 AWS 憑證的身份提供商。

與任何其他 AWS 服務一樣,都會產生成本。 Cognito 的定價基於每月活躍用戶 (MAU)。 對於大多數開發人員來說,好消息是,使用自定義用戶池時,有一個無限期的免費層級,其上限為 50,000 個 MAU。 如果您有一個大型應用程序,這將為您提供大量用戶來試用一種新的用戶管理方法。 但是,我懷疑你們中的許多人的體驗永遠不會超過 50,000 個用戶。 在這種情況下,核心用戶管理幾乎是免費的。 唯一的例外是您將在用戶管理流程中使用的其他 AWS 服務,例如 Lambda、SNS 和 S3。

創建用戶池

將用戶池集成到您的移動應用程序的第一步是創建一個 Cognito 用戶池。 這將為我們提供插入示例應用程序所需的配置值。 要創建新用戶池,請完成 Amazon 的 Cognito 控制台中提供的嚮導。

讓我們來看看創建用戶池的過程。 我必須警告你,這是一個漫長的過程。 在許多方面,這是一件好事,因為它顯示了靈活性。 但是,您會想喝杯咖啡並為此做好準備。

1.名稱

創建用戶池的第一步包括為您的用戶池設置名稱並選擇您將採用的創建用戶池的方法。 您可以查看默認設置或“逐步執行”設置。 因為我們希望對如何配置用戶池有一個很好的工作知識,所以選擇“逐步執行設置”選項。

用戶池創建步驟 1
創建用戶池的第一步(查看大圖)

2. 屬性

配置屬性需要一些思考。 對於每個用戶池,您需要確定哪些屬性將存儲在系統中,哪些是必需的。 因為系統將強制執行所需的值,所以您無法在以後更改它。 此處最好的方法是根據需要僅在此處標記真正重要的值。 此外,如果您希望用戶能夠使用他們的電子郵件地址登錄,請務必將該電子郵件地址標記為別名。

如果您想包含自定義值,您也需要在此處執行此操作。 每個自定義值都有一個類型、可選的驗證規則和一個可變(可更改)或不可變(不可更改)的選項。 有 25 個自定義屬性的硬性限制。

最後,這裡需要說明一下用戶名。 每個用戶的用戶名值是不可變的(不可更改)。 這意味著,在大多數情況下,自動生成此值是有意義的。 這就是存在“首選用戶名”值的原因。 如果您希望用戶擁有可以編輯的用戶名值,只需將“首選用戶名”屬性標記為別名。 如果您希望用戶只需使用他們的電子郵件地址登錄,請務必將“電子郵件”屬性標記為必填項和別名。

對於我們的演示應用程序,我選擇將“電子郵件”、“名字”和“姓氏”設為必需。

用戶池創建步驟 2
為用戶池配置用戶屬性(查看大圖)

3. 政策

配置屬性後,您將能夠為帳戶配置策略。 要配置的第一個策略是密碼策略。 該策略允許您配置長度以及是否需要數字、特殊字符、大寫字母或小寫字母。 此策略將在用戶輸入的密碼以及管理員分配給用戶的密碼上強制執行。

下一個政策與用戶註冊有關。 對於公共應用程序,您可能希望允許用戶自己註冊。 但是,根據應用程序的類型,您可能希望限制註冊並讓系統僅限邀請。 此外,您還必須配置這些邀請在不使用時的過期時間。

對於我們的演示應用程序,我選擇僅使用默認值,但我不希望用戶能夠自行註冊。 有了這些值,我們就可以進行驗證。

用戶池創建中的第 3 步
為用戶池配置策略(查看大圖)

4. 驗證

驗證步驟允許您設置多因素身份驗證以及電子郵件和電話驗證。 雖然在控制台中設置此功能相對容易,但請注意,如果您想驗證電話號碼或使用多重身份驗證,則需要請求增加 AWS SNS 的支出。

對於我們的演示應用程序,我選擇僅使用默認值。

用戶池創建中的第 4 步
為用戶池配置驗證(查看大版本)

5.消息自定義

此步驟允許您自定義用戶池將發送的電子郵件和 SMS 消息,以及“發件人”和“回复”電子郵件地址。 出於我們演示應用程序的目的,我將在此處保留默認值並繼續。

用戶池創建中的第 5 步
為用戶池配置生命週期消息(查看大圖)

6. 標籤

如果您是 AWS 新手,您可能不需要指定任何標籤。 但是,如果您的組織經常使用 AWS,標籤提供了一種使用 IAM 分析支出和分配權限的方法。 例如,一些組織按環境(開發、登台、生產)和項目指定標籤。

無論您在此步驟中輸入什麼內容,都不會影響我們的演示應用程序。

用戶池創建中的第 6 步
為用戶池添加標籤(查看大圖)

7. 設備

下一步允許您定義用戶池是否會記住您用戶的設備。 這是一個額外的安全步驟,可讓您查看特定帳戶已登錄的設備。 當您利用多重身份驗證 (MFA) 時,這具有額外的價值。 如果設備被記住,您可以選擇在每次登錄時不需要 MFA 令牌。

出於演示應用程序的目的,我選擇將值設置為“始終”。

用戶池創建步驟 7
為用戶池配置設備處理(查看大版本)

8. 應用客戶端

對於您要為其使用用戶池的每個應用程序(例如 iOS 應用程序、Web 應用程序、Android 應用程序等),您應該創建一個應用程序。 但是,您可以在創建用戶池後返回並創建這些,因此目前還沒有迫切需要添加所有這些。

每個應用程序都有幾個可以配置的值。 對於這個演示應用程序,我們將為應用程序命名,然後保留默認值。 接下來,您可以配置每個應用程序可以讀取和寫入的用戶屬性。

用戶池創建中的第 8 步
為用戶池配置客戶端應用程序(查看大圖)

您可以在此步驟中設置您喜歡的任何值,只要電子郵件地址、姓氏和名字都可以被應用程序讀取和寫入。 在繼續之前,請務必單擊“創建應用程序客戶端”選項。

9. 觸發器

借助觸發器,您可以使用 Lambda 函數來完全自定義用戶生命週期流程。 例如,如果您只希望具有來自您公司域的電子郵件地址的用戶能夠註冊,您可以為“預註冊”觸發器添加一個 Lambda 函數來執行此驗證並拒絕任何註冊請求不通過。

對於我們的演示應用程序,我不會添加任何觸發器。

用戶池創建中的第 9 步
為用戶池配置觸發器(查看大圖)

10. 回顧

我意識到這似乎是一個漫長而艱鉅的過程。 但請記住,創建用戶池的每個步驟都具有靈活性,可以讓解決方案適應更多用例。 現在,您一直在等待聽到的消息:這是最後一步。

只需檢查設置以確保您已為演示應用程序正確配置它們。 在此屏幕中,您可以返回並編輯之前的任何設置。 創建用戶池後,某些配置值(例如必需的屬性)將無法更改。

創建新用戶池後,您現在可以繼續使用適用於 iOS 的 AWS 開發工具包將它們集成到示例 iOS 應用程序中。

用戶池創建中的第 10 步
創建前對用戶池的最終審查(查看大圖)

為您的用戶池設置您的 iOS 應用程序

我創建了一個與 Cognito 集成的示例 iOS 應用程序,以允許用戶登錄、註銷、輸入他們的名字和姓氏以及設置密碼。 對於這個初始演示,不包括用戶註冊,所以我使用 Cognito 的控制台添加了一個新用戶進行測試。

此應用程序的代碼可以在我的 GitHub 存儲庫中找到。

配置依賴

此應用程序使用 CocoaPods 來管理依賴項。 此時,唯一的依賴項是與 Cognito 用戶池相關的 AWS iOS 開發工具包的特定部分。

(對 CocoaPods 的完整描述超出了本文的範圍,但是,如果您對這個概念不熟悉,CocoaPods 網站上的資源將幫助您啟動和運行。)

此應用程序的 Podfile 的內容如下所示:

 source 'https://github.com/CocoaPods/Specs.git' platform :ios, '10.0' use_frameworks! target 'CognitoApplication' do pod 'AWSCore', '~> 2.5.5' pod 'AWSCognitoIdentityProvider', '~> 2.5.5' end

假設您的機器上安裝了 CocoaPods,您只需運行pod install ,就會為您安裝必要的依賴項。

用戶池配置

下一步是包含用戶池和客戶端應用程序的值。 演示應用程序配置為使用文件CognitoApplication/CognitoConfig.plist ,從中提取此信息。 需要定義四個值:

  • region (字符串)
    這是您創建用戶池的區域。 這需要是標準區域標識符,例如us-east-1ap-southeast-1
  • poolId (字符串)
    這是您創建的用戶池的 ID。
  • clientId (字符串)
    這是作為附加到用戶池的應用程序的一部分配置的clientId
  • clientSecret (字符串)
    這是配置為附加到用戶池的應用程序的一部分的clientSecret

使用該文件和適當的值,可以啟動演示應用程序。 如果在啟動期間發生任何異常,請確保您已包含下面顯示的四個值中的每一個,並且該文件已放置在正確的目錄中。

Xcode 中的 plist 配置
使用plist文件在 Xcode 中配置用戶池(查看大圖)

應用代理集成

與 Amazon Cognito 集成的核心發生在應用程序的AppDelegate中。 我們的第一步是確保我們已設置日誌記錄並已連接到我們的用戶池。 作為該過程的一部分,我們將AppDelegate分配為用戶池的委託。 對於這個基本示例,我們可以將此邏輯保留在AppDelegate中。 對於較大的項目,在其他地方處理它可能是有意義的。

 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // set up logging for AWS and Cognito AWSDDLog.sharedInstance.logLevel = .verbose AWSDDLog.add(AWSDDTTYLogger.sharedInstance) // set up Cognito config self.cognitoConfig = CognitoConfig() // set up Cognito setupCognitoUserPool() return true } func setupCognitoUserPool() { // we pull the needed values from the CognitoConfig object // this just pulls the values in from the plist let clientId:String = self.cognitoConfig!.getClientId() let poolId:String = self.cognitoConfig!.getPoolId() let clientSecret:String = self.cognitoConfig!.getClientSecret() let region:AWSRegionType = self.cognitoConfig!.getRegion() // we need to let Cognito know which region we plan to connect to let serviceConfiguration:AWSServiceConfiguration = AWSServiceConfiguration(region: region, credentialsProvider: nil) // we need to pass it the clientId and clientSecret from the app and the poolId for the user pool let cognitoConfiguration:AWSCognitoIdentityUserPoolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: clientId, clientSecret: clientSecret, poolId: poolId) AWSCognitoIdentityUserPool.register(with: serviceConfiguration, userPoolConfiguration: cognitoConfiguration, forKey: userPoolID) let pool:AWSCognitoIdentityUserPool = AppDelegate.defaultUserPool() // we need to set the AppDelegate as the user pool's delegate, which will get called when events occur pool.delegate = self }

完成此配置後,我們需要為用戶池配置委託方法。 我們正在實施的協議是AWSCognitoIdentityInteractiveAuthenticationDelegate 。 每當用戶需要登錄、重置密碼或提供多因素身份驗證代碼,或者我們需要詢問用戶是否希望記住他們的設備時,都會調用此委託。 對於我們的示例,我們只需要實現startPasswordAuthenticationstartNewPasswordRequired方法:

 extension AppDelegate: AWSCognitoIdentityInteractiveAuthenticationDelegate { // This method is called when we need to log into the application. // It will grab the view controller from the storyboard and present it. func startPasswordAuthentication() -> AWSCognitoIdentityPasswordAuthentication { if(self.navigationController == nil) { self.navigationController = self.window?.rootViewController as? UINavigationController } if(self.loginViewController == nil) { self.loginViewController = self.storyboard?.instantiateViewController(withIdentifier: "LoginViewController") as? LoginViewController } DispatchQueue.main.async { if(self.loginViewController!.isViewLoaded || self.loginViewController!.view.window == nil) { self.navigationController?.present(self.loginViewController!, animated: true, completion: nil) } } return self.loginViewController! } // This method is called when we need to reset a password. // It will grab the view controller from the storyboard and present it. func startNewPasswordRequired() -> AWSCognitoIdentityNewPasswordRequired { if (self.resetPasswordViewController == nil) { self.resetPasswordViewController = self.storyboard?.instantiateViewController(withIdentifier: "ResetPasswordController") as? ResetPasswordViewController } DispatchQueue.main.async { if(self.resetPasswordViewController!.isViewLoaded || self.resetPasswordViewController!.view.window == nil) { self.navigationController?.present(self.resetPasswordViewController!, animated: true, completion: nil) } } return self.resetPasswordViewController! } }

需要注意的一個關鍵點是,這兩種方法都返回一個實現特定協議的視圖控制器。 例如, LoginViewController實現AWSCognitoIdentityPasswordAuthentication ,它有一個方法,該方法使用使用戶能夠完成登錄過程所需的參數被調用。

身份驗證流程

在演示應用程序中準備好所有這些部分後,您現在可以看到登錄過程從頭到尾工作。 應用程序的主視圖顯示用戶名以及用戶的名字和姓氏。 為此,需要執行以下步驟:

  1. AppViewController中,我們調用viewDidLoad方法中的fetchUserAttributes方法。 如果用戶未登錄,這將觸發登錄過程。
  2. AppDelegate中的startPasswordAuthentication方法將被觸發。 此方法加載LoginViewController並呈現它。
  3. LoginViewControllergetDetails方法由 AWS 開發工具包調用。 這包括一個作為AWSTaskCompletionSource實例的對象,我們可以使用它來允許用戶嘗試登錄。
  4. 當用戶按下“登錄”按鈕時,我們將登錄憑據傳遞給該對象。 然後這將調用didCompleteStepWithError方法,我們可以相應地處理結果。 如果沒有錯誤,我們可以關閉視圖控制器。
  5. 如果我們在控制台中創建了用戶,我們將在此處處理另一個步驟。 因為我們給了用戶一個臨時密碼,他們需要設置一個更永久的密碼。 此外,因為我們將名字和姓氏設置為必需參數,所以我們也需要允許用戶輸入這些參數。 AWS 開發工具包將檢測到這一點並調用AppDelegate中的startNewPasswordRequired方法。 這將顯示ResetPasswordViewController並設置其AWSTaskCompletionSource實例。
  6. ResetPasswordViewController的工作方式幾乎與LoginViewController相同。 我們只需要詢問用戶正確的值,然後提交這些值。 一旦這個過程成功完成,我們關閉視圖控制器。
  7. 整個登錄過程完成後,SDK 將安全地存儲 Cognito 返回的令牌。 然後,我們最終將檢索用戶詳細信息,我們可以使用這些信息將用戶的用戶名、名字和姓氏填充到AppViewController

具有身份驗證的工作應用程序
工作示例應用程序,顯示用戶名和元數據

結論

雖然用戶池設置過程可能有幾個步驟,但這些步驟很容易導航。 此外,可能的配置量應該讓您相信它可以支持大多數用例。 在 Universal Mind 的日常工作中,我曾與幾個客戶合作過,他們正在遷移他們現有的應用程序以利用 Cognito 為用戶管理提供的功能。

無論您是否需要定期實施用戶管理系統,這都是每個移動和 Web 開發人員都應該在他們的工具箱中擁有的工具。 在本系列的下一篇文章中,我們將通過實現一個功能更全面的演示應用程序來進一步探索 Cognito 的功能,該應用程序實現了更多常見的用戶管理用例。

通過一些練習,您可以在一天內設置一個滿足所有這些用戶管理用例的新應用程序,從而給所有朋友留下深刻印象。 這對於一天的工作來說已經很不錯了。

鏈接和資源

  • 亞馬遜認知
  • “開發者資源”,Amazon Cognito
  • AWS 移動開發工具包
  • “Swift 的 CocoaPods 教程:入門”,Joshua Greene,raywenderlich.com