如何在Amplify中禁止自动刷新AccessToken并实现无活动超时登出
解决React + Amplify + Cognito无操作自动登出问题
核心问题分析
Amplify的Auth.currentSession方法会自动用RefreshToken刷新即将过期或已过期的AccessToken,导致你无法获取原始AccessToken的过期时间来判断用户是否无操作满1小时。要实现需求,需要绕过自动刷新的干扰,单独追踪用户活动状态和初始令牌过期时间。
解决方案步骤
1. 存储用户最后活动时间
监听用户的核心交互事件,每次触发时更新本地存储的最后活动时间,以此判断用户是否处于无操作状态。
// 在App根组件或全局布局组件中添加监听 useEffect(() => { const updateLastActivity = () => { localStorage.setItem('lastActivityTime', Date.now().toString()); }; // 监听常见用户交互事件 window.addEventListener('click', updateLastActivity); window.addEventListener('keydown', updateLastActivity); window.addEventListener('scroll', updateLastActivity); return () => { // 组件卸载时移除监听 window.removeEventListener('click', updateLastActivity); window.removeEventListener('keydown', updateLastActivity); window.removeEventListener('scroll', updateLastActivity); }; }, []);
2. 保存初始AccessToken过期时间
用户首次登录成功后,立即获取原始AccessToken的过期时间并存储到本地,避免后续自动刷新覆盖该值。
// 登录成功后执行(比如Auth.signIn的回调中) const handleLoginSuccess = async () => { const userSession = await Auth.currentSession(); // 存储初始AccessToken过期时间(转成毫秒) const initialAccessTokenExp = userSession.accessToken.payload.exp * 1000; localStorage.setItem('initialAccessTokenExp', initialAccessTokenExp); // 初始化最后活动时间 localStorage.setItem('lastActivityTime', Date.now().toString()); };
3. 定时检查无操作超时
设置定时任务,定期对比当前时间、最后活动时间、初始AccessToken过期时间,满足条件则强制登出。
useEffect(() => { const checkInactivity = () => { const now = Date.now(); const lastActivity = parseInt(localStorage.getItem('lastActivityTime') || '0', 10); const initialExp = parseInt(localStorage.getItem('initialAccessTokenExp') || '0', 10); const inactivityDuration = now - lastActivity; // 无操作满1小时,且当前时间已过初始AccessToken过期时间 if (inactivityDuration >= 3600000 && now > initialExp) { Auth.signOut() .then(() => { // 清理存储的状态 localStorage.removeItem('initialAccessTokenExp'); localStorage.removeItem('lastActivityTime'); setIsAuthenticated(false); // 可选:跳转到登录页 window.location.href = '/login'; }) .catch(err => console.error('登出错:', err)); } }; // 每分钟检查一次,平衡性能与实时性 const interval = setInterval(checkInactivity, 60000); return () => clearInterval(interval); }, [setIsAuthenticated]);
4. 调整页面访问时的验证逻辑
每次页面加载时,先检查无操作超时状态,再执行IdToken验证:
const verifyUser = async () => { try { // 先检查无操作超时 const now = Date.now(); const lastActivity = parseInt(localStorage.getItem('lastActivityTime') || '0', 10); const initialExp = parseInt(localStorage.getItem('initialAccessTokenExp') || '0', 10); if (now - lastActivity >= 3600000 && now > initialExp) { await Auth.signOut(); localStorage.clear(); setIsAuthenticated(false); return; } // 正常执行IdToken验证 const userSession = await Auth.currentSession(); setIsAuthenticated(true); setAuth(userSession); } catch (err) { setIsAuthenticated(false); } }; // 页面加载时触发验证 useEffect(() => { verifyUser(); }, []);
关键逻辑说明
- 初始AccessToken过期时间:确保我们判断的是用户最后一次操作后是否经过1小时,而非Amplify自动刷新后的令牌有效期。
- 用户活动监听:覆盖核心交互场景,保证只要用户有操作就重置无操作计时。
- 定时检查:避免实时监听带来的性能消耗,同时确保超时状态能被及时检测。
内容的提问来源于stack exchange,提问作者Ovber
相关产品推荐
相关产品推荐

