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

如何用Context API和React Query在React中实现角色授权

React 基于角色的授权实现方案

关于JWT客户端解码的安全性

首先明确:客户端用JWT-decoder解码token是安全的。因为JWT的payload部分是base64编码(不是加密),本身就是公开可读的,解码只是把编码转成明文,不存在泄露敏感信息的问题。但必须注意:客户端的角色检查仅用于前端UI控制和路由跳转,真正的权限校验必须在后端接口层完成——不能依赖前端检查保护数据,恶意用户可轻易绕过前端逻辑。

现有方案优化与落地步骤

你已经能从API获取角色并存入Context,接下来可按以下步骤完成权限控制:

1. 完善AuthContext设计

确保Context包含支撑授权逻辑的核心状态与方法:

import { createContext, useContext, useState } from 'react';
import { useQuery } from '@tanstack/react-query';

const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
  const [token, setToken] = useState(localStorage.getItem('authToken'));
  const [userRoles, setUserRoles] = useState([]);

  // 依赖token拉取用户角色
  const { data: fetchedRoles } = useQuery({
    queryKey: ['userRoles'],
    queryFn: () => fetch('/api/user/roles', {
      headers: { Authorization: `Bearer ${token}` }
    }).then(res => res.json()),
    enabled: !!token,
    onSuccess: (roles) => setUserRoles(roles)
  });

  // 登录逻辑:保存token与初始角色
  const login = (newToken, initialRoles) => {
    localStorage.setItem('authToken', newToken);
    setToken(newToken);
    setUserRoles(initialRoles);
  };

  // 登出逻辑:清空状态
  const logout = () => {
    localStorage.removeItem('authToken');
    setToken(null);
    setUserRoles([]);
  };

  return (
    <AuthContext.Provider value={{ token, userRoles, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
};

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

登录时可选择让后端直接返回token和角色(减少一次请求),或登录后用React Query单独拉取角色,两种方式均可行,依后端接口设计而定。

2. 实现角色校验的受保护路由

基于React Router封装权限检查组件,比如RoleProtectedRoute:

import { Navigate, Outlet } from 'react-router-dom';
import { useAuth } from './AuthContext';

export const RoleProtectedRoute = ({ requiredRoles }) => {
  const { userRoles, token } = useAuth();

  // 未登录跳转登录页
  if (!token) {
    return <Navigate to="/login" replace />;
  }

  // 检查是否拥有所需角色
  const hasPermission = requiredRoles.some(role => userRoles.includes(role));
  
  if (!hasPermission) {
    return <Navigate to="/unauthorized" replace />;
  }

  return <Outlet />;
};

路由配置中使用示例:

import { Routes, Route } from 'react-router-dom';
import { RoleProtectedRoute } from './RoleProtectedRoute';
import Dashboard from './Dashboard';
import Unauthorized from './Unauthorized';
import Login from './Login';

function App() {
  return (
    <Routes>
      <Route path="/login" element={<Login />} />
      <Route path="/unauthorized" element={<Unauthorized />} />
      
      // 需要especialidades角色的路由组
      <Route element={<RoleProtectedRoute requiredRoles={['especialidades']} />}>
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/special-page" element={<SpecialPage />} />
      </Route>
    </Routes>
  );
}

3. 组件内的权限控制

在组件中通过useAuth钩子获取角色,控制UI显示:

import { useAuth } from './AuthContext';

function Dashboard() {
  const { userRoles } = useAuth();

  return (
    <div>
      <h1>Dashboard</h1>
      {userRoles.includes('especialidades') && (
        <button>仅专科用户可见的操作按钮</button>
      )}
    </div>
  );
}

4. 后端兜底校验(核心!)

前端控制仅提升用户体验,所有敏感接口必须在后端做权限校验:

  • 先验证JWT的签名与有效期
  • 从token payload或数据库中获取用户角色
  • 检查用户是否拥有接口访问权限,无权限则返回403状态码

Node.js/Express中间件示例:

const verifyRole = (requiredRole) => {
  return (req, res, next) => {
    // 假设已通过JWT中间件解析出用户信息到req.user
    if (!req.user.roles.includes(requiredRole)) {
      return res.status(403).json({ message: '无权限访问' });
    }
    next();
  };
};

// 接口使用权限校验中间件
app.get('/api/special-data', verifyToken, verifyRole('especialidades'), (req, res) => {
  // 返回敏感数据
});

你的疑问解答

  • 是否需要修改Context?如果当前Context仅存储角色,建议补充token状态、登录/登出方法,方便全局状态管理。
  • 登录时同时返回token和角色可行吗?完全可以,能减少一次API请求,提升用户体验。
  • 存入Context后下一步?按上述方案实现路由保护与组件内权限控制,同时确保后端完成兜底校验。
  • 这是不是最佳方案?对于中小项目,Context + React Query的组合简洁高效;大型项目可考虑Redux Toolkit等状态管理库,但核心逻辑一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 03:26:02