Flutter ー Authentication 認證

語言: CN / TW / HK

Flutter ー Authentication 認證

原文 https://medium.com/@simbu/flutter-authentication-adb8df7cf673

前言

如果我相信我知道你是誰那我就能讓你檢視你的個人 應用 application 資料。

身份驗證可能是應用程式必須處理的最大的交叉問題。

將它作為一個特性新增到 DigestablePrologue 允許我們多次抽象和重用它,並通過更新一組程式碼來減少維護。

正文

業務需求

讓我們從一個高層次的、故意模糊的業務需求開始:

“該應用程式將能夠使用現代身份驗證保護對螢幕和 API 的訪問”

經過最初的交談,我們決定接受這個要求:

  • 滑動登入螢幕。
  • 使用 Microsoft AD 或 Google Firebase 進行身份驗證。
  • 在設定中登出。

然後,我們舉辦了一個研討會,通過示例建立規範:

Login slide up specification

Login slide up specification

Auth service specification

認證服務規範

Logout setting specification

登出設定規範

並將它們轉換為可執行規範:

Login screen feature tests

登入螢幕功能測試

Authentication service feature tests

身份驗證服務特性測試

Logout setting feature tests

登出設定特性測試

為了縮短這篇文章的篇幅,我已經部分實現了登出設定,跳過了即將新增的 AD & Firebase 身份驗證服務。

因此,特性測試的結果不再完全反映最初的規範,但是一旦深入細節,範圍或方向的改變是正常的。

我發表的所有關於認證和資料訪問主題的文章將很快通過一篇摘要文章(一個迷你係列文章)結合在一起。

開始

從 app_config 讀取環境。Json 檔案,該檔案在應用程式啟動時載入,並由為 live 和 UAT 進行的 CodeMagic 整合構建注入:

根據環境選擇身份驗證服務:

```dart AuthenticationServiceStateNotifier selectAuthenticationServiceByEnvironment() {

var environment = GlobalEnvironmentValues.instance.environment;

AuthenticationService authenticationService = environment == Environments.live

  ? LiveAuthenticationService()

  : environment == Environments.uat

      ? UatAuthenticationService()

      : StubbedAuthenticationService();

return AuthenticationServiceStateNotifier(authenticationService);

}

final authenticationServiceProvider = StateNotifierProvider<

AuthenticationServiceStateNotifier, AuthenticationService>(

(ref) => selectAuthenticationServiceByEnvironment(),

); ```

當應用程式啟動時,它會檢測存根身份驗證服務並自動驗證使用者:

```dart if (ref.read(authenticationServiceProvider).typeName ==

    AuthenticationService.authenticationServiceTypeNameStubbed) {

    ref.read(authenticationProvider.notifier).setIsAuthenticated(true);

} ```

這個設定螢幕是使用一個很棒的包 sets_ui 新增的,它允許使用者登出,並呼叫身份驗證服務上的 signOut 方法:

Sign Out setting

登出設定

我們的自動化特性測試涵蓋了所有內容:

Login feature test report

登入功能測試報告

Authentication feature test report

認證特性測試報告

在未來的帖子中,當我們新增 UAT 和 Live 環境時,它會在未經身份驗證時路由到登入螢幕,並呼叫真正的身份驗證服務來獲取訪問令牌,這些令牌將被 Flutter Data 用來進行安全的 API 請求。

登入螢幕和身份驗證服務已經新增到 DigestablePrologue,設定螢幕新增到 DigestableMe。

引發導航事件 navigation events

為了響應導航,我在路由器上增加了一個觀察者

```dart MaterialApp.router(

...

navaigationObservers: [

   NavigationEventsObserver(ref.read(eventStoreProvider))

],

...

) ```

每次導航發生時,它都會在事件總線上引發 Navigated 事件:

```dart /// Raises Nativigated events when the GoRouter navigates.

class NavigationEventsObserver extends NavigatorObserver {

final EventStore eventStore;

NavigationEventsObserver(this.eventStore);

@override

void didPush(Route route, Route? previousRoute) {

raiseNavigatedEvent(route.settings.name ?? "");

}

@override

void didPop(Route route, Route? previousRoute) {

raiseNavigatedEvent(route.settings.name ?? "");

}

@override

void didReplace({ Route? newRoute, Route? oldRoute }) {

raiseNavigatedEvent(newRoute?.settings.name ?? "");

}

void raiseNavigatedEvent(String routeName) {

even(fn)tStore.bus.fire(Navigated(routeName));

}

} ```

然後測試可以使用它來證明導航操作已經發生,當前事件儲存僅保留最後一個 Navigated 事件。

通過偵聽事件並記錄它們,我很可能會 extension 這個功能來新增應用程式監視。

Flutter App 生命週期

為了在喚醒時強制執行所需的身份驗證,我需要使用重寫來捕獲應用程式狀態(AppLificycleState)。

```dart @override

void didChangeAppLifecycleState(AppLifecycleState state) {

ref.read(appLifecycleStateProvider.notifier)

    .setLifecycleState(state);

ref.read(authenticationServiceProvider.notifier)

    .checkAuthenticated();

} ```

可觀察的生命週期事件(AppLificycleState) :

  • Inactive ー應用程式處於非活動狀態,不接收使用者輸入。這個事件只能在 iOS 上執行,因為在 Android 上沒有等效的事件可以對映到
  • 暫停ー應用程式當前對使用者不可見,不響應使用者輸入,並在後臺執行。這相當於 Android 中的 onPace()
  • 應用程式可見並響應使用者輸入,這相當於 Android 中的 onPostResume()
  • 暫停ー 應用 application 暫停。這相當於 Android 中的 onStop; 它不會在 iOS 上觸發,因為在 iOS 上沒有可對映到的等價事件

功能測試支援你

Feature tests letting us know we have broken other features

特性測試讓我們知道我們已經破壞了其他特性

這是偉大的,我已經做了一些相當大的變化,但我知道我需要修復,以避免任何迴歸錯誤。

測試的契約確保應用程式仍然能夠完成早期特性所要求的工作。

特性測試認證

事實證明,要使特性測試通過非常困難,但是為了確保身份驗證按計劃工作,將它們放在適當的位置是值得的。

主要問題是在測試步驟執行之前從檔案載入環境,這意味著我們已經運行了檢查身份驗證和重定向到登入螢幕的邏輯。

在幾次嘗試通過程式碼改變環境失敗後,我選擇了一套單獨的特性測試:

Gherkin feature test config that loads the live config value file.

Gherkin 特性測試配置,載入實時配置值檔案。

載入不同的環境配置檔案:

Live config value file

實時配置值檔案

它需要更多的維護,但是很有必要,因為我們將在整合和部署構建中注入配置檔案以保護祕密值。

使用事件來解決難以實現的、特徵化的測試步驟

登入特性的一些步驟很難實現,因為功能將在使用微應用程式 DigestablePrologue 的父應用程式 DigestableMe 中實現:

dart When: Making an API request

這意味著我們無法在 DigestablePrologue 中導航到螢幕或發出 API 請求。

這是一個耦合問題,我們可以用事件匯流排來解決。

應用程式現在只是偵聽事件並採取適當的操作,允許我們在步驟中引發事件,而不是實際的導航或 API 呼叫。

何時: 發出 API 請求事件: API 請求

使用 EventBus 還有其他優點,我們可以在以後新增這些優點,例如記錄引發的事件。

規程 ーー 執行特性步驟

使用 Flutter Gherkin 最乏味的部分是建立所有的步驟方法,我將嘗試使用構建器來自動建立包含要填寫的框架步驟的檔案。

與此同時,我使用視覺化程式碼中的高亮來規範所需的步驟,例如:

這樣說:

```dart Given: Always on authentication

And: Not authenticated

And: authenticated

When: The application is started

When: The application awakes

When: Making an API request

When: Displaying a restricted screen

When: Displaying an unrestricted screen

Then: The application routes to the 'Login Screen'

And: Records the current screen for redirection ```

然後將骨架方法新增到步驟檔案中

路線授權

具有自定義屬性的路由角色,則可以實現基於非角色的路由,大家暫時可以,但可以 extension 。

沒有實現登入/id/auth 的受限螢幕,因為它的一些不同之處,它的授權和路由保護,在需要時通過 GoRouter 重定向作為保護。

Packages

結束語

如果本文對你有幫助,請轉發讓更多的朋友閱讀。

也許這個操作只要你 3 秒鐘,對我來說是一個激勵,感謝。

祝你有一個美好的一天~


© 貓哥

  • 微信 ducafecat

  • https://wiki.ducafecat.tech

  • https://video.ducafecat.tech