You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何持久化用户登录?Supabase认证状态保持问题求助

问题:跳过登录界面后Supabase无法识别用户身份

我设置了两个导航器(已登录/未登录),可以通过代码跳过登录界面,但此时Supabase无法识别用户,导致无法读写受保护的表。直接通过登录表单登录时一切正常,但通过本地存储的token跳过登录后就失效了。试过Auth Context Provider、getSession()/getUser()等方法都没用。

现有代码片段

导航器 index.js

const AppNavigator = () => {
  const dispatch = useDispatch();
  const loggedIn = useSelector((state) => state.auth.loggedIn);

  useEffect(() => {
    const fetchAuthInfo = async () => {
      try {
        const token = await getFromAsyncStorage("AUTH_TOKEN");
        if (!token) {
          return dispatch(authActions.updateLoggedInState(false));
        } else {
          dispatch(authActions.updateLoggedInState(true));
        }
      } catch (error) {
        Toast.show("Oops, something went wrong!");
      }
    };
    fetchAuthInfo();
  }, []);

  return (
    <NavigationContainer>
      <Root.Navigator>
        {loggedIn ? (
          <Root.Screen
            name="MainNavigator"
            component={MainNavigator}
            options={{ headerShown: false }}
          />
        ) : (
          <Root.Screen
            name="AuthNavigator"
            component={AuthNavigator}
            options={{ headerShown: false }}
          />
        )}
        <Root.Screen
          name="ForgotPassword"
          component={ForgotPassword}
          options={{ headerShown: false }}
        />
      </Root.Navigator>
    </NavigationContainer>
  );
};

登录页面核心逻辑

const [email, setEmail] = useState("");
const [password, setPassword] = useState("");

const loggedIn = useSelector((state) => state.auth.loggedIn);
const dispatch = useDispatch();

const handleLogin = async () => {
  let { data, error } = await supabase.auth.signInWithPassword({
    email,
    password,
  });

  if (error) {
    Toast.show(error.message);
    console.log(error);
  }

  if (data.session) {
    await saveToAsyncStorage("AUTH_TOKEN", data.session.access_token);
    dispatch(authActions.updateLoggedInState(true));
    console.log("logged in", data.session.user.id);
  }
};

问题根源

你现在的逻辑只是自己在Redux里标记了用户已登录,但Supabase客户端完全不知道这个本地存储的token——所有Supabase请求都需要携带有效的会话信息才能通过RLS策略,而你没有把存储的token同步到Supabase客户端中。

另外,只存储access_token是不够的:Supabase会话需要access_token和refresh_token(用来自动刷新过期的访问令牌),单独的access_token无法维持完整会话。


解决方案

1. 修改登录逻辑:存储完整会话对象

登录成功后,不要只存access_token,把整个session对象序列化后存入本地存储:

if (data.session) {
  // 存储完整会话,而非单独的access_token
  await saveToAsyncStorage("AUTH_SESSION", JSON.stringify(data.session));
  dispatch(authActions.updateLoggedInState(true));
  console.log("logged in", data.session.user.id);
}

2. 修改导航器逻辑:恢复Supabase会话

在启动时取出存储的会话,调用Supabase的setSession方法恢复认证状态,再更新Redux:

useEffect(() => {
  const fetchAuthInfo = async () => {
    try {
      const sessionString = await getFromAsyncStorage("AUTH_SESSION");
      if (!sessionString) {
        return dispatch(authActions.updateLoggedInState(false));
      }
      
      const session = JSON.parse(sessionString);
      // 通知Supabase恢复会话
      const { error } = await supabase.auth.setSession({
        access_token: session.access_token,
        refresh_token: session.refresh_token
      });
      
      if (error) {
        // 会话无效(比如令牌过期),清除存储并强制登录
        await removeFromAsyncStorage("AUTH_SESSION");
        dispatch(authActions.updateLoggedInState(false));
        Toast.show("会话已过期,请重新登录");
        return;
      }
      
      // 会话有效,更新登录状态
      dispatch(authActions.updateLoggedInState(true));
    } catch (error) {
      Toast.show("Oops, something went wrong!");
      dispatch(authActions.updateLoggedInState(false));
    }
  };
  fetchAuthInfo();
}, []);

3. 可选:添加状态监听增强可靠性

在App根组件中添加Supabase auth状态监听,自动同步Redux和本地存储,避免状态不一致:

useEffect(() => {
  // 监听Supabase认证状态变化
  const subscription = supabase.auth.onAuthStateChange((event, session) => {
    if (session) {
      // 登录/会话刷新时,保存会话到本地并更新Redux
      saveToAsyncStorage("AUTH_SESSION", JSON.stringify(session));
      dispatch(authActions.updateLoggedInState(true));
    } else {
      // 登出/会话过期时,清除存储并更新Redux
      removeFromAsyncStorage("AUTH_SESSION");
      dispatch(authActions.updateLoggedInState(false));
    }
  });
  
  // 组件卸载时取消监听
  return () => subscription.unsubscribe();
}, [dispatch]);

验证逻辑

完成修改后:

  • 重启App,会自动从本地存储恢复Supabase会话,此时调用supabase.auth.getUser()应该能返回当前用户
  • 所有访问受保护表的请求会自动带上认证信息,RLS策略可以正常识别用户ID

内容的提问来源于stack exchange,提问作者Josie Jones

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 03:23:20