如何在React+Redux应用中实现AWS Amplify的currentSession维持登录状态
如何在结合Redux的React应用中维持AWS Amplify登录状态(页面刷新后)
我之前也遇到过完全一样的问题——页面刷新后Redux store清空,用户登录状态丢失,UI直接回到未登录状态。结合AWS Amplify和Redux的最佳实践其实分两步:恢复Amplify的会话状态并同步到Redux,同时持久化Redux中需要保留的非敏感数据。下面是具体的实现方案:
核心思路
- 页面刷新后,Redux store会被重置,所以我们需要在应用启动时主动检查Amplify的当前会话有效性。
- 使用Redux持久化工具(比如
redux-persist)保存store中需要保留的非敏感数据(比如用户基本信息、应用偏好),避免这些数据随页面刷新丢失。 - 监听Amplify的会话变化事件,确保Redux状态始终和Amplify的认证状态保持同步。
具体实现步骤
1. 配置Redux持久化
首先安装redux-persist(和Redux Toolkit适配性很好):
npm install redux-persist
然后修改你的Redux store配置,指定需要持久化的slice,同时处理Amplify对象的序列化问题:
// store.js import { configureStore, combineReducers } from '@reduxjs/toolkit'; import { persistStore, persistReducer } from 'redux-persist'; import storage from 'redux-persist/lib/storage'; // 默认使用localStorage import authReducer from './slices/authSlice'; import userPreferencesReducer from './slices/userPreferencesSlice'; // 持久化配置:只保留需要的slice,避免存储敏感数据 const persistConfig = { key: 'root', storage, whitelist: ['auth', 'userPreferences'], // 这里列你想保留的slice }; // 包装reducer const rootReducer = combineReducers({ auth: authReducer, userPreferences: userPreferencesReducer, // 其他不需要持久化的reducer放在这里 }); const persistedReducer = persistReducer(persistConfig, rootReducer); export const store = configureStore({ reducer: persistedReducer, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: false, // 关闭序列化检查,因为Amplify的会话对象可能包含不可序列化的属性 }), }); export const persistor = persistStore(store);
2. 在应用启动时检查并同步会话
在你的根组件(比如App.jsx)中,使用useEffect在组件挂载时检查Amplify的当前会话,并将状态同步到Redux:
// App.jsx import { useEffect } from 'react'; import { useDispatch } from 'react-redux'; import { Auth } from 'aws-amplify'; import { setUser, setSession } from './store/slices/authSlice'; import { PersistGate } from 'redux-persist/integration/react'; import { store, persistor } from './store'; import { BrowserRouter as Router } from 'react-router-dom'; // 如果用路由的话 function App() { const dispatch = useDispatch(); useEffect(() => { // 检查当前会话的异步函数 const checkAuthStatus = async () => { try { // 获取Amplify的当前会话 const session = await Auth.currentSession(); // 获取当前认证用户信息 const user = await Auth.currentAuthenticatedUser(); // 将会话和用户信息同步到Redux dispatch(setSession(session)); dispatch(setUser(user)); } catch (error) { // 会话无效(比如用户未登录、令牌过期),重置Redux中的认证状态 dispatch(setSession(null)); dispatch(setUser(null)); // 可以在这里跳转到登录页 // navigate('/login'); } }; checkAuthStatus(); // 监听Amplify的会话变化事件,确保状态同步 const authListener = Auth.listen((data) => { switch (data.payload.event) { case 'signOut': // 用户登出时重置Redux状态 dispatch(setSession(null)); dispatch(setUser(null)); break; case 'tokenRefresh': // 令牌自动刷新后,更新Redux中的会话 Auth.currentSession().then(session => { dispatch(setSession(session)); }); break; // 其他事件可以根据需要处理,比如signIn } }); // 组件卸载时清理监听 return () => authListener(); }, [dispatch]); return ( <PersistGate loading={<div>加载中...</div>} persistor={persistor}> <Router> {/* 你的应用路由和页面组件 */} </Router> </PersistGate> ); } export default App;
3. 编写Auth Slice
创建一个Redux slice来管理认证状态:
// slices/authSlice.js import { createSlice } from '@reduxjs/toolkit'; const initialState = { user: null, // 存储用户基本信息 session: null, // 存储会话对象(注意:不要持久化敏感令牌,Amplify会自己管理) }; const authSlice = createSlice({ name: 'auth', initialState, reducers: { setUser: (state, action) => { state.user = action.payload; }, setSession: (state, action) => { state.session = action.payload; }, }, }); export const { setUser, setSession } = authSlice.actions; export default authSlice.reducer;
关键注意事项
- 不要持久化敏感令牌:Amplify会自动将
idToken、accessToken等存储在安全的位置(比如浏览器的localStorage,可通过Amplify配置修改),我们只需要在Redux中存储会话状态和用户基本信息即可,避免手动存储敏感数据带来的安全风险。 - 处理序列化问题:因为Amplify的
CognitoUserSession对象包含一些不可序列化的方法,所以我们关闭了Redux Toolkit的serializableCheck,如果担心这个,可以只存储会话中的必要字段(比如令牌字符串),而不是整个对象。 - 加载状态处理:
PersistGate的loading属性可以设置一个加载组件,避免在持久化数据恢复前显示未授权的UI。
内容的提问来源于stack exchange,提问作者Skate to Eat
相关产品推荐
相关产品推荐

