反应导航:与身份验证的深层链接

问题描述 投票:5回答:2

我正在使用react-native和react-navigation库构建一个移动应用程序,用于管理我的应用程序中的导航。现在,我的应用程序看起来像这样:

App [SwitchNavigator]
    Splash [Screen]
    Auth [Screen]
    MainApp [StackNavigator]
        Home [Screen]            (/home)
        Profile [Screen]         (/profile)
        Notifications [Screen]   (/notifications)

我已经将深度链接与上面的模式集成在一起,用于屏幕HomeProfileNotifications,它可以按预期工作。我面临的问题是如何在使用深层链接时管理用户的身份验证。现在每当我打开一个深层链接(例如myapp://profile)时,无论我是否经过身份验证,该应用程序都会将我带到屏幕上。我想要它做的是在AsyncStorage之前检查是否有userToken,如果没有或它无效,那么只需重新定向Auth屏幕。

我以与here描述的方式几乎完全相同的方式设置了身份验证流程。因此,当我的应用程序启动时,Splash屏幕检查用户的手机是否有有效令牌并将其发送到Auth屏幕或Home屏幕。

我现在提出的唯一解决方案是将每个深层链接指向Splash,对我的用户进行身份验证,然后解析链接以导航到良好的屏幕。因此,例如当用户打开myapp://profile时,我在Splash上打开应用程序,验证令牌,然后解析URL(/profile),最后重定向到AuthProfile

这是这样做的好方法,还是反应导航提供了更好的方法来做到这一点?他们网站上的Deep linking页面有点亮。

谢谢您的帮助 !

react-native react-navigation deep-linking
2个回答
4
投票

我的设置与您的设置类似。我跟着Authentication flows · React NavigationSplashScreen - Expo Documentation设置了我的Auth流程,所以我有点失望的是,通过它获得深层链接是一个挑战。我能够通过自定义我的主开关导航器来实现这一点,该方法类似于您所说的解决方案。我只是想分享我的解决方案,所以有一个具体的例子说明如何才能开始工作。我的主切换导航器设置如下(我也使用TypeScript,因此如果不熟悉则忽略类型定义):

const MainNavigation = createSwitchNavigator(
  {
    SplashLoading,
    Onboarding: OnboardingStackNavigator,
    App: AppNavigator,
  },
  {
    initialRouteName: 'SplashLoading',
  }
);

const previousGetActionForPathAndParams =
  MainNavigation.router.getActionForPathAndParams;

Object.assign(MainNavigation.router, {
  getActionForPathAndParams(path: string, params: any) {
    const isAuthLink = path.startsWith('auth-link');

    if (isAuthLink) {
      return NavigationActions.navigate({
        routeName: 'SplashLoading',
        params: { ...params, path },
      });
    }

    return previousGetActionForPathAndParams(path, params);
  },
});

export const AppNavigation = createAppContainer(MainNavigation);

您希望通过身份验证流程路由的任何深层链接都需要从auth-link开始,或者您选择在其前面添加的任何内容。这是SplashLoading的样子:

export const SplashLoading = (props: NavigationScreenProps) => {
  const [isSplashReady, setIsSplashReady] = useState(false);

  const _cacheFonts: CacheFontsFn = fonts =>
    fonts.map(font => Font.loadAsync(font as any));

  const _cacheSplashAssets = () => {
    const splashIcon = require(splashIconPath);
    return Asset.fromModule(splashIcon).downloadAsync();
  };

  const _cacheAppAssets = async () => {
    SplashScreen.hide();
    const fontAssetPromises = _cacheFonts(fontMap);
    return Promise.all([...fontAssetPromises]);
  };

  const _initializeApp = async () => {
    // Cache assets
    await _cacheAppAssets();

    // Check if user is logged in
    const sessionId = await SecureStore.getItemAsync(CCSID_KEY);

      // Get deep linking params
    const params = props.navigation.state.params;
    let action: any;

    if (params && params.routeName) {
      const { routeName, ...routeParams } = params;
      action = NavigationActions.navigate({ routeName, params: routeParams });
    }

    // If not logged in, navigate to Auth flow
    if (!sessionId) {
      return props.navigation.dispatch(
        NavigationActions.navigate({
          routeName: 'Onboarding',
          action,
        })
      );
    }

    // Otherwise, navigate to App flow
    return props.navigation.navigate(
      NavigationActions.navigate({
        routeName: 'App',
        action,
      })
    );
  };

  if (!isSplashReady) {
    return (
      <AppLoading
        startAsync={_cacheSplashAssets}
        onFinish={() => setIsSplashReady(true)}
        onError={console.warn}
        autoHideSplash={false}
      />
    );
  }

  return (
    <View style={{ flex: 1 }}>
      <Image source={require(splashIconPath)} onLoad={_initializeApp} />
    </View>
  );
};

我使用routeName查询参数创建深层链接,这是执行身份验证后导航到的屏幕的名称(显然可以添加您需要的任何其他查询参数)。由于我的SplashLoading屏幕处理加载所有字体/资产以及auth检查,我需要每个深层链接来路由它。我遇到的问题是我会从多任务手动退出应用程序,点击深层链接网址,并让应用程序崩溃,因为深层链接绕过SplashLoading所以没有加载字体。

上面的方法声明了一个action变量,如果没有设置则不会做任何事情。如果routeName查询参数不是undefined,我设置action变量。这使得一旦Switch路由器决定基于auth(OnboardingApp)采用哪条路径,该路由将获得子动作并在退出auth / splash加载流后导航到routeName

这是我创建的一个示例链接,可以正常使用此系统:exp://192.168.1.7:19000/--/auth-link?routeName=ForgotPasswordChange&cacheKey=a9b3ra50-5fc2-4er7-b4e7-0d6c0925c536

希望图书馆作者将来能够将其作为本机支持的功能,因此不需要黑客攻击。我也很想看看你提出了什么!


1
投票

我最终使用自定义URI拦截深层链接启动,然后将这些参数传递到预期路由。我的加载屏幕处理验证检查。

const previousGetActionForPathAndParams = AppContainer.router.getActionForPathAndParams

Object.assign(AppContainer.router, {
  getActionForPathAndParams (path, params) {
    if (path === 'auth' && params.routeName && params.userId ) {
      // returns a profile navigate action for myApp://auth?routeName=chat&userId=1234
      return NavigationActions.navigate({
        routeName: 'Loading',
          params: { ...params, path },
      })
    }
    return previousGetActionForPathAndParams(path, params)
  },
})

https://reactnavigation.org/docs/en/routers.html#handling-custom-uris

然后,在我的装载路线中,我像正常一样解析参数,然后路由到预定位置,再次传递它们。

const userId = this.props.navigation.getParam('userId')

https://reactnavigation.org/docs/en/params.html

© www.soinside.com 2019 - 2024. All rights reserved.