React中实现完整的登录鉴权与权限控制系统

发布于:2025-08-11 ⋅ 阅读:(23) ⋅ 点赞:(0)

系统架构设计

1. 全局前置守卫 - 路由拦截器

// src/components/AuthGuard.jsx
import { useEffect } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import { useAuth } from '../hooks/useAuth';

const AuthGuard = ({ children }) => {
  const navigate = useNavigate();
  const location = useLocation();
  const { token, userInfo, checkPermission } = useAuth();

  useEffect(() => {
    // Token校验
    if (!token) {
      navigate('/login', { state: { from: location } });
      return;
    }

    // 权限校验
    const currentPath = location.pathname;
    if (!checkPermission(currentPath)) {
      navigate('/403'); // 无权限页面
      return;
    }
  }, [token, location.pathname]);

  return token ? children : null;
};

export default AuthGuard;

2. Token管理与RBAC权限控制

// src/hooks/useAuth.js
import { createContext, useContext, useState, useEffect } from 'react';
import { jwtDecode } from 'jwt-decode';

const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
  const [token, setToken] = useState(localStorage.getItem('token'));
  const [userInfo, setUserInfo] = useState(null);
  const [permissions, setPermissions] = useState([]);
  const [roles, setRoles] = useState([]);

  // Token校验
  const validateToken = (token) => {
    try {
      const decoded = jwtDecode(token);
      return decoded.exp > Date.now() / 1000;
    } catch {
      return false;
    }
  };

  // 登录
  const login = async (credentials) => {
    try {
      const response = await fetch('/api/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(credentials)
      });
      
      const data = await response.json();
      if (data.success) {
        setToken(data.token);
        setUserInfo(data.userInfo);
        setPermissions(data.permissions);
        setRoles(data.roles);
        localStorage.setItem('token', data.token);
        return { success: true };
      }
    } catch (error) {
      return { success: false, error: error.message };
    }
  };

  // 权限检查
  const checkPermission = (permission) => {
    return permissions.includes(permission) || 
           roles.some(role => role.permissions?.includes(permission));
  };

  // 角色检查
  const hasRole = (role) => {
    return roles.some(r => r.name === role);
  };

  useEffect(() => {
    if (token && !validateToken(token)) {
      logout();
    }
  }, [token]);

  const logout = () => {
    setToken(null);
    setUserInfo(null);
    setPermissions([]);
    setRoles([]);
    localStorage.removeItem('token');
  };

  return (
    <AuthContext.Provider value={{
      token, userInfo, permissions, roles,
      login, logout, checkPermission, hasRole
    }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => useContext(AuthContext);

3. 动态路由与菜单权限

// src/router/DynamicRouter.jsx
import { useMemo } from 'react';
import { Routes, Route } from 'react-router-dom';
import { useAuth } from '../hooks/useAuth';
import AuthGuard from '../components/AuthGuard';

// 路由配置
const routeConfig = [
  {
    path: '/dashboard',
    component: () => import('../pages/Dashboard'),
    permission: 'dashboard:view',
    meta: { title: '仪表盘', icon: 'dashboard' }
  },
  {
    path: '/users',
    component: () => import('../pages/Users'),
    permission: 'user:view',
    meta: { title: '用户管理', icon: 'users' },
    children: [
      {
        path: '/users/list',
        component: () => import('../pages/Users/List'),
        permission: 'user:list'
      }
    ]
  }
];

const DynamicRouter = () => {
  const { checkPermission } = useAuth();

  // 根据权限过滤路由
  const filteredRoutes = useMemo(() => {
    const filterRoutes = (routes) => {
      return routes.filter(route => {
        if (route.permission && !checkPermission(route.permission)) {
          return false;
        }
        if (route.children) {
          route.children = filterRoutes(route.children);
        }
        return true;
      });
    };
    return filterRoutes(routeConfig);
  }, [checkPermission]);

  return (
    <Routes>
      {filteredRoutes.map(route => (
        <Route
          key={route.path}
          path={route.path}
          element={
            <AuthGuard>
              <route.component />
            </AuthGuard>
          }
        />
      ))}
    </Routes>
  );
};

export default DynamicRouter;

4. 动态菜单生成

// src/components/DynamicMenu.jsx
import { useMemo } from 'react';
import { Menu } from 'antd';
import { useAuth } from '../hooks/useAuth';
import { useNavigate } from 'react-router-dom';

const DynamicMenu = () => {
  const { checkPermission } = useAuth();
  const navigate = useNavigate();

  const menuItems = useMemo(() => {
    const buildMenuItems = (routes) => {
      return routes
        .filter(route => route.permission ? checkPermission(route.permission) : true)
        .map(route => ({
          key: route.path,
          icon: route.meta?.icon,
          label: route.meta?.title,
          children: route.children ? buildMenuItems(route.children) : undefined
        }));
    };
    return buildMenuItems(routeConfig);
  }, [checkPermission]);

  const handleMenuClick = ({ key }) => {
    navigate(key);
  };

  return (
    <Menu
      mode="inline"
      items={menuItems}
      onClick={handleMenuClick}
    />
  );
};

export default DynamicMenu;

5. 按钮级别权限控制 - 自定义指令

// src/components/PermissionButton.jsx
import { useAuth } from '../hooks/useAuth';

const PermissionButton = ({ 
  permission, 
  role, 
  children, 
  fallback = null,
  ...props 
}) => {
  const { checkPermission, hasRole } = useAuth();

  // 权限检查
  const hasPermission = permission ? checkPermission(permission) : true;
  const hasRequiredRole = role ? hasRole(role) : true;

  if (!hasPermission || !hasRequiredRole) {
    return fallback;
  }

  return children;
};

// 使用示例
const UserManagement = () => {
  return (
    <div>
      <PermissionButton permission="user:create">
        <button>新增用户</button>
      </PermissionButton>
      
      <PermissionButton permission="user:edit">
        <button>编辑用户</button>
      </PermissionButton>
      
      <PermissionButton 
        permission="user:delete" 
        role="admin"
        fallback={<span>无权限</span>}
      >
        <button>删除用户</button>
      </PermissionButton>
    </div>
  );
};

export default PermissionButton;

6. 高阶组件权限控制

// src/hoc/withPermission.jsx
import { useAuth } from '../hooks/useAuth';

const withPermission = (permission, fallback = null) => {
  return (WrappedComponent) => {
    return (props) => {
      const { checkPermission } = useAuth();
      
      if (!checkPermission(permission)) {
        return fallback;
      }
      
      return <WrappedComponent {...props} />;
    };
  };
};

// 使用示例
const AdminPanel = withPermission('admin:access')(() => {
  return <div>管理员面板</div>;
});

7. 权限指令Hook

// src/hooks/usePermission.js
import { useAuth } from './useAuth';

export const usePermission = () => {
  const { checkPermission, hasRole } = useAuth();

  const can = (permission) => checkPermission(permission);
  const cannot = (permission) => !checkPermission(permission);
  const is = (role) => hasRole(role);
  const isNot = (role) => !hasRole(role);

  return { can, cannot, is, isNot };
};

// 使用示例
const MyComponent = () => {
  const { can, is } = usePermission();

  return (
    <div>
      {can('user:view') && <UserList />}
      {is('admin') && <AdminTools />}
    </div>
  );
};

8. 应用入口配置

// src/App.jsx
import { BrowserRouter } from 'react-router-dom';
import { AuthProvider } from './hooks/useAuth';
import DynamicRouter from './router/DynamicRouter';
import DynamicMenu from './components/DynamicMenu';

function App() {
  return (
    <BrowserRouter>
      <AuthProvider>
        <div className="app">
          <aside className="sidebar">
            <DynamicMenu />
          </aside>
          <main className="content">
            <DynamicRouter />
          </main>
        </div>
      </AuthProvider>
    </BrowserRouter>
  );
}

export default App;

核心特性

  1. Token自动校验:全局拦截器自动检查Token有效性
  2. RBAC权限模型:支持角色和权限的灵活组合
  3. 动态路由:根据用户权限动态生成可访问路由
  4. 动态菜单:菜单项根据权限实时显示/隐藏
  5. 按钮级控制:细粒度的操作权限控制
  6. 多种使用方式:组件、HOC、Hook等多种权限控制方式
  7. 类型安全:完整的TypeScript支持(可选)

这套方案提供了完整的前端权限控制体系,可以根据实际需求进行调整和扩展。


网站公告

今日签到

点亮在社区的每一天
去签到