17370845950

AWS Amplify React 路由权限控制:实现未登录用户自动重定向

本文介绍如何在 aws amplify + react router v6 项目中,为特定路由(如 `/dashboard`)添加认证保护,确保仅已登录用户可访问,未认证用户自动跳转至登录页或提示页。

在基于 AWS Amplify 构建的 React 应用中,实现页面级访问控制是常见需求。单纯使用 withAuthenticator 包裹组件会导致重复渲染登录框、路由循环等问题;而直接在函数组件内异步调用 Auth.currentAuthenticatedUser() 又会因 React 渲染机制(无法在渲染函数中 await)导致白屏或逻辑失效。正确方案是采用路由守卫(Route Guard)模式——通过自定义高阶组件拦截路由访问,在导航前完成身份校验。

以下是推荐的生产就绪实现方式:

✅ 正确做法:创建 RequireAuth 路由守卫组件

// src/components/RequireAuth.jsx
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { Auth } from 'aws-amplify';

export function RequireAuth({ children }) {
  const navigate = useNavigate();
  const [isAuth, setIsAuth] = useState(null); // null 表示校验中,避免闪屏

  useEffect(() => {
    const checkAuth = async () => {
      try {
        await Auth.currentAuthenticatedUser(); // 若已登录,返回 CognitoUser 对象
        setIsAuth(true);
      } catch (error) {
        console.warn('User not authenticated:', error);
        navigate('/login', { 
          replace: true,
          state: { from: window.location.pathname } // 记录来源页,登录后可跳回
        });
      }
    };

    checkAuth();
  }, [navigate]);

  // 渲染占位符(如加载 Spinner),提升用户体验
  if (isAuth === null) {
    return Checking authentication...;
  }

  return isAuth ? children : null;
}

✅ 在 App.js 中集成守卫路由

// src/App.js
import React from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { Amplify } from 'aws-amplify';
import awsExports from './aws-exports';
Amplify.configure(awsExports);

import Home from './pages/Home';
import Login from './pages/Login';
import Dashboard from './pages/Dashboard';
import ErrorPage from './pages/ErrorPage';

function App() {
  return (
    
      
        } />
        } />

        {/* 受保护路由:仅认证用户可进入 */}
        
              
            
          }
        />

        } />
      
    
  );
}

export default App;

⚠️ 关键注意事项

  • 不要在 element 中直接传入异步函数或条件渲染逻辑:React Router v6 的 element 属性期望一个同步 JSX 元素,而非返回 JSX 的函数。
  • 避免 useEffect 空依赖数组中的裸 Auth.currentAuthenticatedUser():应包裹在 async 函数中并显式 await,否则 .then().catch() 链易丢失错误上下文。
  • 添加加载态反馈:isAuth === null 时显示轻量加载提示,防止白屏,提升 UX。
  • 保留来源路径:跳转登录页时通过 state.from 记录原始目标路径,登录成功后可重定向回 /dashboard(需在 Login 组件中读取 useLocation().state?.from)。
  • 服务端校验不可替代:前端守卫仅用于体验优化,所有敏感 API 调用仍须在 Amplify 后端(如 GraphQL Resolver 或 REST Lambda)中进行严格权限验证。

✅ 进阶建议:统一认证状态管理

对于中大型应用,推荐结合 Context 或 Zustand 管理全局认证状态,避免每个受保护路由重复调用 Auth.currentAuthenticatedUser()。例如:

// src/contexts/AuthContext.jsx
import { createContext, useContext, useEffect, useState } from 'react';
import { Auth } from 'aws-amplify';

const AuthContext = createContext();

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const checkUser = async () => {
      try {
        const currentUser = await Auth.currentAuthenticatedUser();
        setUser(currentUser);
      } catch (e) {
        setUser(null);
      } finally {
        setLoading(false);
      }
    };
    checkUser();
  }, []);

  return (
    
      {!loading && children}
    
  );
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) throw new Error('useAuth must be used within AuthProvider');
  return context;
}

然后在 RequireAuth 中消费该 Context,实现状态复用与响应式更新。

通过以上方案,你将获得健壮、可维护且符合 React 最佳实践的认证路由控制能力,无缝集成 AWS Amplify 身份服务。